Objets transférables (rapide)

Chrome 13 a introduit l'envoi de ArrayBuffer à/depuis un Web Worker à l'aide d'un algorithme appelé clonage structuré. Cela a permis à l'API postMessage() d'accepter des messages qui ne sont pas seulement des chaînes, mais des types complexes tels que File, Blob, ArrayBuffer et des objets JSON. Le clonage structuré est également compatible avec les versions ultérieures de Firefox.

Plus de rapidité, c'est encore mieux

Le clonage structuré est excellent, mais il reste une opération de copie. Le coût de transmission d'un ArrayBuffer de 32 Mo à un nœud de calcul peut atteindre des centaines de millisecondes. Les nouvelles versions des navigateurs contiennent une énorme amélioration des performances pour la transmission de messages, appelée objets transférables.

Avec les objets transférables, les données sont transférées d'un contexte à un autre. Il s'agit d'une copie zéro, ce qui améliore considérablement les performances d'envoi de données à un worker. Pensez-y comme à une transmission par référence si vous venez de l'univers C/C++. Toutefois, contrairement au transfert par référence, la "version" du contexte d'appel n'est plus disponible une fois transférée vers le nouveau contexte. Par exemple, lorsque vous transférez un ArrayBuffer de votre application principale vers le nœud de calcul, le ArrayBuffer d'origine est effacé et n'est plus utilisable. Son contenu est (littéralement) transféré vers le contexte du nœud de calcul.

Pour utiliser des éléments transférables, une nouvelle version de postMessage() est disponible, qui prend en charge les objets transférables:

worker.postMessage(arrayBuffer, [transferableList]);
window.postMessage(arrayBuffer, targetOrigin, [transferableList]);

Dans le cas du worker, le premier argument est le message ArrayBuffer. Le deuxième argument est une liste d'éléments à transférer. Dans cet exemple, vous devez spécifier arrayBuffer dans la liste transférable.

Démo de benchmark

Pour voir les gains de performances des éléments transférables, j'ai créé une démonstration.

La démonstration envoie un ArrayBuffer de 32 Mo à un nœud de travail et le renvoie à l'aide de postMessage(). Si votre navigateur n'est pas compatible avec les éléments transférables, l'échantillon revient au clonage structuré. Après cinq exécutions dans différents navigateurs, voici ce que j'ai obtenu:

Tableau comparatif du clonage structuré et des objets transférables

Sur un MacBook Pro/10.6.8/2,53 GHz/Intel Core 2 Duo, FF était le plus rapide avec le clonage structuré. En moyenne, il a fallu 302 ms pour envoyer le ArrayBuffer de 32 Mo à un nœud de calcul et le renvoyer au thread principal (délai aller-retour). En comparaison, le même test a pris 6,6 ms avec des éléments transférables. Cela représente une amélioration considérable des performances !

Ces vitesses permettent de transmettre facilement des textures/maillages WebGL massifs entre un Worker et l'application principale.

Détection de fonctionnalités

La détection des éléments est un peu délicate dans ce cas. Je vous recommande d'envoyer un petit ArrayBuffer à votre nœud de calcul. Si le tampon est transféré et non copié, son .byteLength passe à 0:

var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);
if (ab.byteLength) {
    alert('Transferables are not supported in your browser!');
} else {
    // Transferables are supported.
}

Compatibilité:actuellement Chrome 17 et versions ultérieures, Firefox, Opera, Safari et IE 10 et versions ultérieures

Mise à jour (13/12/2011) : extrait de code montrant que la signature webkitPostMessage() est différente pour la fenêtre et le worker. Mise à jour (3 novembre 2016) : suppression des préfixes du fournisseur et mise à jour des extraits de code