Objets transférables (rapide)

Chrome 13 a introduit l'envoi de ArrayBuffer vers/depuis un Web worker à l'aide d'un algorithme appelé clonage structuré. Cela a permis à l'API postMessage() d'accepter des messages qui n'étaient pas que des chaînes, mais aussi 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 idéal, mais il s'agit tout de même d'une opération de copie. La transmission d'un ArrayBuffer de 32 Mo à un worker peut prendre plusieurs centaines de millisecondes. Les nouvelles versions des navigateurs offrent une amélioration considérable des performances de transmission de messages, appelées 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 nœud de calcul. Considérez cela comme une référence de passement si vous êtes du monde C/C++. Cependant, contrairement à la méthode pass 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, lors du transfert d'un ArrayBuffer de votre application principale vers un worker, le ArrayBuffer d'origine est effacé et n'est plus utilisable. Son contenu est transféré (littéralement, silencieuse) vers le contexte du nœud de calcul.

Pour jouer avec les objets transférables, une nouvelle version de postMessage() est compatible avec les objets transférables:

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

Dans le cas du nœud de calcul, le premier argument est le message ArrayBuffer. Le deuxième argument est une liste d'articles à transférer. Dans cet exemple, vous devez spécifier le arrayBuffer dans la liste transférable.

Démonstration d'analyse comparative

Pour voir les gains de performances des clients transférables, j'ai réalisé une démonstration.

La démonstration envoie un fichier ArrayBuffer de 32 Mo à un nœud de calcul, puis le renvoie à l'aide de postMessage(). Si votre navigateur n'est pas compatible avec les éléments transférables, l'exemple utilise le clonage structuré. En moyenne, 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 utilisant le clonage structuré. En moyenne, il a fallu 302 ms pour envoyer le ArrayBuffer de 32 Mo à un nœud de calcul et le publier dans le thread principal (RRT : Round Trip Time). En comparaison avec les éléments transférables, le même test a pris 6,6 ms. Ça, c'est une grosse victoire !

Ces types de vitesses permettent de transmettre facilement des textures/grilles WebGL massives entre un worker et l'application principale.

Détection de caractéristiques

Avec cette méthode, la détection des caractéristiques est un peu délicate. Je vous recommande d'envoyer un petit ArrayBuffer à votre nœud de calcul. Si le tampon est transféré, mais pas 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é:Chrome 17 ou version ultérieure, Firefox, Opera, Safari et Internet Explorer 10 ou version ultérieure

Mise à jour (13/12/2011) : l'extrait de code permettant d'afficher la signature webkitPostMessage() est différent pour la fenêtre et le worker. Mise à jour (03/11/2016) : suppression des préfixes de fournisseurs et des extraits de code mis à jour