可转移的对象 - 闪电般的速度

Chrome 13 引入了使用结构化克隆算法向/从 Web Worker 发送 ArrayBuffer 的方法。这样一来,postMessage() API 便可接受不仅仅是字符串,还包括 FileBlobArrayBuffer 和 JSON 对象等复杂类型的消息。较高版本的 Firefox 也支持结构化克隆。

越快越好

结构化克隆非常棒,但它仍然是一种复制操作。将 32MB 的 ArrayBuffer 传递给 Worker 的开销可能高达数百毫秒。新版浏览器在消息传递方面取得了巨大的性能提升,这种传递方式称为可传输对象

借助可传输对象,数据可从一个上下文传输到另一个上下文。它是零拷贝的,这极大地提高了向 Worker 发送数据的性能。如果您使用的是 C/C++,可以将其视为引用传递。不过,与引用传递不同,调用上下文中的“版本”在转移到新上下文后将无法再使用。例如,从主应用将 ArrayBuffer 转移到 Worker 时,系统会清除原始 ArrayBuffer,使其无法再使用。其内容会(确切地说)转移到 Worker 上下文。

如需使用可传输对象,请使用支持可传输对象的新版 postMessage()

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

对于 worker 情形,第一个参数是 ArrayBuffer 消息。第二个参数是应转移的内容列表。在此示例中,您需要在可转移列表中指定 arrayBuffer

基准演示

为了让您了解可转移项带来的性能提升,我制作了一个演示

该演示会使用 postMessage() 将 32MB 的 ArrayBuffer 发送到工作器,然后再发回。如果您的浏览器不支持可传输项,则该示例会回退到结构化克隆。在不同的浏览器中平均运行 5 次后,我得到了以下结果:

结构化克隆与可转移对象对比图表

在 MacBook Pro/10.6.8/2.53 GHz/Intel Core 2 Duo 上,使用结构化克隆时,FF 的速度最快。平均而言,将 32MB 的 ArrayBuffer 发送到工作器并将其发回主线程需要 302 毫秒(往返时间 [RTT])。与可传输项相比,相同的测试耗时 6.6 毫秒。性能提升非常明显!

有了这种速度,就可以在 Worker 和主应用之间无缝传递大量 WebGL 纹理/网格。

特征检测

这项功能的功能检测有点棘手。我建议您向 worker 发送一个小型 ArrayBuffer。如果缓冲区被传输而不是复制,则其 .byteLength 将变为 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.
}

支持:目前支持 Chrome 17 及更高版本、Firefox、Opera、Safari 和 IE10 及更高版本

更新时间:2011 年 12 月 13 日:更新了代码段,以显示 webkitPostMessage() 签名因窗口和 worker 而异。更新时间:2016 年 11 月 3 日:移除了供应商前缀并更新了代码段