Chrome 13 introduced sending ArrayBuffers to/from a Web Worker using an algorithm called structured cloning. This allowed the postMessage() API to accept messages that were not just strings, but complex types like File, Blob, ArrayBuffer, and JSON objects. Structured cloning is also supported in later versions of Firefox.

Faster is better

Structured cloning is great, but it’s still a copy operation. The overhead of passing a 32MB ArrayBuffer to a Worker can be hundreds of milliseconds. New versions of Chrome contain a huge performance improvement for message passing, called Transferable Objects.

With transferable objects, data is transferred from one context to another. It is zero-copy, which vastly improves the performance of sending data to a Worker. Think of it as pass-by-reference if you’re from the C/C++ world. However, unlike pass-by-reference, the ‘version’ from the calling context is no longer available once transferred to the new context. For example, when transferring an ArrayBuffer from your main app to Worker, the original ArrayBuffer is cleared and no longer usable. Its contents are (quiet literally) transferred to the Worker context.

To play with transferables, there’s a new version of postMessage() in Chrome/V8 that supports transferable objects:

worker.webkitPostMessage(arrayBuffer, [arrayBuffer]);
window.webkitPostMessage(arrayBuffer, targetOrigin, [arrayBuffer]);

For the worker case, the first argument is the ArrayBuffer message. The second argument is a list of items that should be transferred.

Benchmark demo

To see the performance gains of transferrables, I’ve put together a demo.

The demo sends a 32MB ArrayBuffer to a worker and back using webkitPostMessage(). If your browser doesn’t support transferables, the sample falls back to structured cloning. Averaging 5 runs in different browsers, here’s what I got:

On a MacBook Pro/10.6.8/2.53 GHz/Intel Core 2 Duo, FF was the fastest using structured cloning. On average, it took 302ms to send the 32MB ArrayBuffer to a worker and post it back to the main thread (RRT - Round Trip Time). Comparing that with transferables, the same test took 6.6ms. That is a huge perf boost!

Having these kinds of speeds allows massive WebGL textures/meshes to be seamlessly passed between a Worker and main app.

Feature detecting

Feature detecting is a bit tricky with this one. My recommendation is to send a small ArrayBuffer to your worker. If the buffer is transferred and not copied, its .byteLength will go to 0:

worker.postMessage = worker.webkitPostMessage || worker.postMessage;

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

Support: Currently Chrome 17+

Updated (2011-12-13): Code snippet to show webkitPostMessage() signature is different for window and worker.

binary performance workers code announcement

Comments

0