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

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

基准演示

如需了解可转移数据在性能方面的提升,我整理了一份演示

演示版向 worker 发送 32MB ArrayBuffer,然后使用 postMessage() 返回。如果您的浏览器不支持可转移数据,则示例会回退到结构化克隆。在不同浏览器中平均运行 5 次,以下是我得到的结果:

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

在 MacBook Pro/10.6.8/2.53 GHz/Intel Core 2 Duo 上,FF 是使用结构化克隆的最快速度。平均而言,将 32MB 的 ArrayBuffer 发送到 worker 并将其发布回主线程需要 302 毫秒(RRT - 往返时间)。与可传输资源相比,同一测试耗时 6.6 毫秒。性能可大大提升!

这样的速度可以让大量 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() 签名的代码段对于 window 和 worker 不同。 更新日期 (2016-11-03):移除了供应商前缀并更新了代码段