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

Chrome 13 引入了使用名为结构化克隆的算法向 Web Worker 发送ArrayBuffer数据的功能。这使得 postMessage() API 能够接受不仅是字符串,而且是复杂类型(如 FileBlobArrayBuffer 和 JSON 对象)的消息。较高版本的 Firefox 也支持结构化克隆。

越快越好

结构化克隆固然不错,但它仍然是一种复制操作。将 32MB 的 ArrayBuffer 传递给 worker 的开销可能为数百毫秒。 新版浏览器在消息传递方面进行了重大改进,称为“可传输对象”。

使用可转移对象时,数据可以从一个上下文转移到另一个上下文。它属于零副本,极大地提高了向 worker 发送数据的性能。如果您来自 C/C++,可将其视为传递引用。不过,与传递引用不同的是,“version”从调用上下文转移到新上下文后将不再可用。例如,将 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 并将其发布回主线程(RRT - 往返时间)平均需要 302 毫秒。与转移作业相比,同一测试需要 6.6 毫秒。这大幅提高了性能!

具有这样的速度可以在 worker 和主应用之间无缝传递大规模的 WebGL 纹理/网格。

特征检测

使用此类型进行特征检测有点棘手。建议您向您的工作器发送一个小 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):移除了供应商前缀并更新了代码段