O Chrome 13 introduziu o envio de ArrayBuffer
s de/para 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. A clonagem estruturada também é compatível em versões mais recentes do Firefox.
Quanto mais rápido, melhor
A clonagem estruturada é ótima, mas ainda é uma operação de cópia. A sobrecarga da transmissão de um ArrayBuffer
de 32 MB para um worker pode ser de centenas de milissegundos.
As novas versões dos navegadores apresentam uma grande melhoria de desempenho na transmissão de mensagens, chamada de Objetos transferíveis.
Com objetos transferíveis, os dados são transferidos de um contexto para outro. É zero cópia, o que melhora muito o desempenho do envio de dados para um worker. Pense nisso como uma passagem de referência se você estiver no mundo C/C++. No entanto, ao contrário da passagem por referência, a "versão" do contexto de chamada não fica mais disponível após a transferência para o novo contexto. Por exemplo, ao transferir um ArrayBuffer
do app principal para o worker, o ArrayBuffer
original é apagado e não pode mais ser usado. O conteúdo é transferido (literalmente) para o contexto do worker.
Para jogar com transferíveis, há uma nova versão do postMessage()
que oferece suporte a objetos transferíveis:
worker.postMessage(arrayBuffer, [transferableList]);
window.postMessage(arrayBuffer, targetOrigin, [transferableList]);
No caso do worker, o primeiro argumento é a mensagem ArrayBuffer
. O segundo argumento é uma lista de itens que precisam ser transferidos. Neste exemplo, você especificaria arrayBuffer
na lista transferível.
Demonstração do comparativo de mercado
Para conferir os ganhos de desempenho dos itens transferíveis, criei uma demonstração (em inglês).
A demonstração envia um ArrayBuffer
de 32 MB para um worker e retorna usando postMessage()
. Se o navegador não for compatível com transferência, o exemplo recorrerá à clonagem estruturada. Em média, cinco são executados em navegadores diferentes. Isto é o que eu consegui:
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, levava 302 ms para enviar o ArrayBuffer
de 32 MB a um worker e postá-lo de volta na linha de execução principal (RRT, na sigla em inglês). Comparando com os transferíveis, o mesmo teste levou 6,6 ms. Isso é um grande aumento no desempenho!
Com esses tipos de velocidade, é possível transmitir texturas/malhas enormes de WebGL entre um worker e o app principal.
Detecção de recursos
A detecção de atributos é um pouco complicada. Minha recomendação é enviar um pequeno ArrayBuffer
para o worker. Se o buffer for transferido e não copiado, o .byteLength
dele vai para 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.
}
Suporte: no momento, Google Chrome 17 ou superior, Firefox, Opera, Safari e IE10 ou superior
Atualizado em 13/12/2011: o snippet de código para mostrar a assinatura webkitPostMessage()
é diferente para janela e worker.
Atualizado em 03/11/2016: prefixos de fornecedores e snippets de código atualizados