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 di stringhe, ma anche 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 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, l'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 tuo 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