Objetos transferíveis: ultrarrápida

O Chrome 13 introduziu o envio de ArrayBuffers para/de um Web Worker usando um algoritmo chamado clonagem estruturada. Isso permitiu que a API postMessage() aceitasse mensagens que não eram apenas strings, mas tipos complexos como File, Blob, ArrayBuffer e objetos JSON. O clonagem estruturada também é compatível com versões mais recentes do Firefox.

Quanto mais rápido, melhor

A clonagem estruturada é ótima, mas ainda é uma operação de cópia. O overhead de transmitir uma ArrayBuffer de 32 MB para um worker pode ser de centenas de milissegundos. As novas versões dos navegadores contêm uma grande melhoria de desempenho para a transmissão de mensagens, chamada de objetos transferíveis.

Com objetos transferíveis, os dados são transferidos de um contexto para outro. Ele não precisa de cópia, o que melhora muito o desempenho do envio de dados para um worker. Se você vem do mundo C/C++, pense nisso como transmissão por referência. No entanto, ao contrário da passagem por referência, a "versão" do contexto de chamada não estará mais disponível depois de transferida para o novo contexto. Por exemplo, ao transferir um ArrayBuffer do app principal para o worker, o ArrayBuffer original é limpo e não pode mais ser usado. O conteúdo é transferido (literalmente) para o contexto do worker.

Para brincar com os itens transferíveis, há uma nova versão de postMessage() que oferece suporte a objetos transferíveis:

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

No caso do trabalhador, o primeiro argumento é a mensagem ArrayBuffer. O segundo argumento é uma lista de itens que precisam ser transferidos. Neste exemplo, você especifica o arrayBuffer na lista transferível.

Demonstração de comparativo de mercado

Para conferir os ganhos de performance dos itens transferíveis, criei uma demonstração.

A demonstração envia um ArrayBuffer de 32 MB para um worker e vice-versa usando postMessage(). Se o navegador não oferecer suporte a transferências, a amostra vai voltar para a clonagem estruturada. A média de 5 execuções em diferentes navegadores é a seguinte:

Gráfico de comparação entre clonagem estruturada e objetos transferíveis

Em um MacBook Pro/10.6.8/2.53 GHz/Intel Core 2 Duo, o FF foi o mais rápido usando a clonagem estruturada. Em média, levou 302 ms para enviar a ArrayBuffer de 32 MB a um worker e enviá-la de volta à linha de execução principal (RRT - Round Trip Time). Comparando isso com os transferíveis, o mesmo teste levou 6,6 ms. Isso é um grande aumento de desempenho.

Ter esse tipo de velocidade permite que texturas/malhas WebGL enormes sejam transmitidas sem problemas entre um worker e o app principal.

Detecção de recursos

A detecção de recursos é um pouco complicada nesse caso. Minha recomendação é enviar uma pequena ArrayBuffer para o trabalhador. Se o buffer for transferido e não copiado, o .byteLength vai ser zerado:

var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);
if (ab.byteLength) {
    alert('Transferables are not supported in your browser!');
} else {
    // Transferables are supported.
}

Suporte:atualmente, Chrome 17 e versões mais recentes, Firefox, Opera, Safari e IE 10 e versões mais recentes

Atualização (13-12-2011): o snippet de código para mostrar a assinatura webkitPostMessage() é diferente para janela e worker. Atualização (3 de novembro de 2016): foram removidos os prefixos do fornecedor e atualizados os snippets de código.