Chrome 13 引入了使用结构化克隆算法向/从 Web Worker 发送 ArrayBuffer
的方法。这样一来,postMessage()
API 便可接受不仅仅是字符串,还包括 File
、Blob
、ArrayBuffer
和 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 日:移除了供应商前缀并更新了代码段