Chrome 13 ha introdotto l'invio di ArrayBuffer
da/verso un web worker utilizzando un algoritmo chiamato clonazione strutturata. In questo modo l'API postMessage()
ha potuto accettare messaggi che non erano solo stringhe, ma di tipi complessi come File
, Blob
, ArrayBuffer
e oggetti JSON. La clonazione strutturata è supportata anche nelle versioni successive di Firefox.
Più veloce è meglio
La clonazione strutturata è ottima, ma è comunque un'operazione di copia. L'overhead del passaggio di un ArrayBuffer
da 32 MB a un worker può essere di centinaia di millisecondi.
Le nuove versioni dei browser presentano un enorme miglioramento delle prestazioni per la trasmissione dei messaggi, denominate oggetti trasferibili.
Nel caso di oggetti trasferibili, i dati vengono trasferiti da un contesto all'altro. Si tratta di zero-copy, che migliora notevolmente le prestazioni di invio dei dati a un worker. È una sorta di "pass-by-reference" se appartieni al mondo C/C++. Tuttavia, a differenza del passaggio per riferimento, la "versione" del contesto di chiamata non è più disponibile una volta trasferita nel nuovo contesto. Ad esempio, quando trasferisci un ArrayBuffer
dalla tua app principale a Worker, l'ArrayBuffer
originale viene cancellato e non può più essere utilizzato. I suoi contenuti vengono (letteralmente) trasferiti al contesto del worker.
Per poter usare i contenuti trasferibili, esiste una nuova versione di postMessage()
che supporta gli oggetti trasferibili:
worker.postMessage(arrayBuffer, [transferableList]);
window.postMessage(arrayBuffer, targetOrigin, [transferableList]);
Per il caso dei worker, il primo argomento è il messaggio ArrayBuffer
. Il secondo argomento è un elenco di elementi che devono essere trasferiti. In questo esempio, devi specificare arrayBuffer
nell'elenco dei contenuti trasferibili.
Demo benchmark
Per verificare l'incremento del rendimento dei prodotti trasferibili, ho preparato una demo.
La demo invia un file ArrayBuffer
di 32 MB a un worker e torna indietro utilizzando postMessage()
. Se il tuo browser non supporta i contenuti trasferibili, l'esempio esegue un ricorso alla clonazione strutturata. In media 5 viene eseguito in browser diversi, ecco cosa ho ottenuto:
Su un MacBook Pro/10.6.8/2.53 GHz/Intel Core 2 Duo, FF è stato il più veloce nell'utilizzo della clonazione strutturata. In media, sono stati necessari 302 ms per inviare il ArrayBuffer
di 32 MB a un worker e pubblicarlo di nuovo nel thread principale (RRT - Round Trip Time). Confrontando i dati trasferibili, lo stesso test ha richiesto 6,6 ms. Una grande spinta in termini di prestazioni!
La presenza di questo tipo di velocità consente di passare senza soluzione di continuità grandi texture/macchine WebGL tra un'app worker e l'app principale.
Rilevamento delle caratteristiche
Il rilevamento delle caratteristiche è un po' complicato in questa. Ti consiglio di inviare un ArrayBuffer
di piccole dimensioni al tuo lavoratore. Se il buffer viene trasferito e non copiato, il valore di .byteLength
passerà a 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.
}
Supporto: attualmente Chrome 17 o versioni successive, Firefox, Opera, Safari e IE10 o versioni successive
Aggiornato (13/12/2011): lo snippet di codice per mostrare la firma webkitPostMessage()
è diverso per finestra e worker.
Aggiornamento (3/11/2016): rimozione dei prefissi dei fornitori e degli snippet di codice aggiornati