O Chrome 13 introduziu o envio de ArrayBuffer
s 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:
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.