Oggetti trasferibili - Velocissime

Chrome 13 ha introdotto l'invio di ArrayBuffer a/da un web worker utilizzando un algoritmo chiamato clonazione strutturata. In questo modo, l'API postMessage() ha potuto accettare messaggi non solo costituiti da stringhe, ma anche da 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 si tratta comunque di un'operazione di copia. L'overhead di passaggio di un ArrayBuffer di 32 MB a un worker può essere di centinaia di millisecondi. Le nuove versioni dei browser contengono un enorme miglioramento delle prestazioni per la trasmissione dei messaggi, chiamato oggetti trasferibili.

Con gli oggetti trasferibili, i dati vengono trasferiti da un contesto all'altro. È una copia zero, il che migliora notevolmente le prestazioni dell'invio di dati a un worker. Pensalo come a un passaggio per riferimento se ti occupi di C/C++. Tuttavia, a differenza del passaggio per riferimento, la "versione" del contesto di chiamata non è più disponibile una volta trasferita al nuovo contesto. Ad esempio, quando trasferisci un ArrayBuffer dall'app principale a Worker, il ArrayBuffer originale viene cancellato e non è più utilizzabile. I relativi contenuti vengono (letteralmente) trasferiti al contesto del worker.

Per giocare con gli oggetti trasferibili, è disponibile una nuova versione di postMessage() che supporta gli oggetti trasferibili:

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

Per la richiesta dell'operatore, il primo argomento è il messaggio ArrayBuffer. Il secondo argomento è un elenco di elementi da trasferire. In questo esempio, devi specificare arrayBuffer nell'elenco trasferibile.

Demo di benchmark

Per vedere i miglioramenti del rendimento dei trasferibili, ho creato una demo.

La demo invia un ArrayBuffer di 32 MB a un worker e viceversa utilizzando postMessage(). Se il browser non supporta gli elementi trasferibili, il sample torna alla clonazione strutturata. Ecco cosa ho ottenuto con una media di 5 esecuzioni in browser diversi:

Grafico di confronto tra clonazione strutturata e oggetti trasferibili

Su un MacBook Pro/10.6.8/2,53 GHz/Intel Core 2 Duo, FF è stato il più veloce con la clonazione strutturata. In media, sono stati necessari 302 ms per inviare ArrayBuffer da 32 MB a un worker e per postarlo di nuovo nel thread principale (RRT, tempo di round trip). Se confrontato con i trasferibili, lo stesso test ha richiesto 6,6 ms. Si tratta di un enorme miglioramento delle prestazioni.

Queste velocità consentono di trasferire senza problemi texture/mesh WebGL di grandi dimensioni tra un worker e l'app principale.

Rilevamento di funzionalità

Il rilevamento delle funzionalità è un po' complicato in questo caso. Ti consiglio di inviare un piccolo ArrayBuffer al tuo worker. Se il buffer viene trasferito e non copiato, il relativo .byteLength diventa 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 e versioni successive, Firefox, Opera, Safari e IE 10 e versioni successive

Aggiornato (13/12/2011): lo snippet di codice per mostrare la firma webkitPostMessage() è diversa per la finestra e il worker. Aggiornamento (03/11/2016): sono stati rimossi i prefissi dei fornitori e aggiornati gli snippet di codice