Objetos transferibles: muy rápido

Chrome 13 introdujo el envío de objetos ArrayBuffer desde y hacia un trabajador web con un algoritmo llamado clonación estructurada. Esto permitió que la API de postMessage() aceptara mensajes que no eran solo cadenas, sino tipos complejos, como File, Blob, ArrayBuffer y objetos JSON. La clonación estructurada también es compatible con las versiones posteriores de Firefox.

Más rápido, mejor

La clonación estructurada es excelente, pero sigue siendo una operación de copia. La sobrecarga de pasar un ArrayBuffer de 32 MB a un trabajador puede ser de cientos de milisegundos. Las nuevas versiones de los navegadores contienen una gran mejora en el rendimiento del envío de mensajes, lo que se denomina objetos transferibles.

Con los objetos transferibles, los datos se transfieren de un contexto a otro. Es una copia cero, lo que mejora enormemente el rendimiento de enviar datos a un trabajador. Considéralo como una referencia de transferencia si perteneces al mundo de C/C++. Sin embargo, a diferencia del paso por referencia, la “versión” del contexto de llamada ya no está disponible una vez que se transfiere al contexto nuevo. Por ejemplo, cuando se transfiere una ArrayBuffer de tu app principal a Worker, se borra el ArrayBuffer original y ya no se puede usar. Su contenido se transfiere (literalmente) al contexto de los trabajadores.

Para jugar con los elementos transferibles, hay una nueva versión de postMessage() que admite objetos transferibles:

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

Para el caso del trabajador, el primer argumento es el mensaje ArrayBuffer. El segundo argumento es una lista de elementos que se deben transferir. En este ejemplo, se especifica arrayBuffer en la lista transferible.

Demostración de comparativas

Para ver el aumento del rendimiento de los elementos transferibles, armé una demostración.

La demostración envía un archivo ArrayBuffer de 32 MB a un trabajador y lo devuelve mediante postMessage(). Si tu navegador no admite transferibles, la muestra recurre a la clonación estructurada. En promedio, 5 se ejecuta en distintos navegadores, y esto es lo que obtuve:

Gráfico de comparación entre la clonación estructurada y los objetos transferibles

En una MacBook Pro/10.6.8/2.53 GHz/Intel Core 2 Duo, FF fue la más rápida con la clonación estructurada. En promedio, se necesitaron 302 ms para enviar el ArrayBuffer de 32 MB a un trabajador y publicarlo en el subproceso principal (RRT - tiempo de ida y vuelta). En comparación con los transferibles, la misma prueba tardó 6.6 ms. Ese es un gran aumento del rendimiento.

Tener este tipo de velocidades permite que las texturas/mallas masivas de WebGL se pasen sin problemas entre un trabajador y una app principal.

Detección de funciones

En este caso, la detección de características es un poco complicada. Te recomendamos que envíes un ArrayBuffer pequeño a tu trabajador. Si el búfer se transfiere y no se copia, su .byteLength irá 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.
}

Compatibilidad: Actualmente, Chrome 17 o versiones posteriores, Firefox, Opera, Safari e IE10 y versiones posteriores

Actualización (13/12/2011): El fragmento de código para mostrar la firma webkitPostMessage() es diferente para la ventana y el trabajador. Actualización (3/11/2016): Se quitaron los prefijos de proveedores y se actualizaron los fragmentos de código