如何将大数据传递给Web worker

时间:2013-10-03 07:00:13

标签: javascript web-worker

我正在研究网络工作者,我正在向网络工作者传递大量数据,这需要花费大量时间。我想知道发送数据的有效方法。

我尝试过以下代码:

var worker = new Worker('js2.js');
worker.postMessage( buffer,[ buffer]);
worker.postMessage(obj,[obj.mat2]);
if (buffer.byteLength) {
  alert('Transferables are not supported in your browser!');
}

2 个答案:

答案 0 :(得分:26)

<强>更新

According to Mozilla所有主流浏览器都禁用了SharedArrayBuffer,因此以下编辑中描述的选项不再适用。

  

请注意,默认情况下,所有主要版本都禁用SharedArrayBuffer   浏览器于2018年1月5日响应Spectre。

编辑:现在有另一个选项,它正在发送一个sharedArray缓冲区。这是ES2017在共享内存和原子下的一部分,现在在FireFox 54 Nightly中得到支持。如果您想阅读它,可以查看here。我可能会写一些时间并将其添加到我的答案中。我也会尝试添加性能基准测试。

回答原来的问题:

  

我正在研究网络工作者,我正在传递大量数据   网络工作者,这需要很多时间。我想知道效率   发送数据的方式。

@MichaelDibbets answer的替代方案,他将对象的副本发送给网络工作者,使用的是transferrable object,这是零拷贝。

它表明您打算让您的数据可以转移,但我猜它没有成功。因此,我将解释一些数据可以为您和未来的读者传输的意义。

转移对象&#34;参考&#34; (虽然这不是它的完美术语,如下一篇文章所述)并不适用于任何JavaScript对象。它必须是可传输的数据类型。

  

[使用Web Workers]大多数浏览器都实现了结构化克隆   算法,允许您传入/传出更复杂的类型   诸如File,Blob,ArrayBuffer和JSON对象之类的工作者。然而,   使用postMessage()传递这些类型的数据时,副本仍然是   制作。因此,如果你传递一个大的50MB文件(例如),   在工人之间获取该文件有明显的开销   和主线。

     

结构化克隆很棒,但副本可能需要数百个   毫秒。要打击性能,您可以使用Transferable   对象。

     

使用可转移对象,数据从一个上下文传输到   另一个。它是零拷贝,大大提高了性能   将数据发送给工人。如果你是的话,可以把它想象为传递参考   来自C / C ++世界。但是,与传递引用不同,&#39;版本&#39;   一旦转移到调用上下文不再可用   新的背景。例如,从中传输ArrayBuffer时   你的主应用程序是Worker,原来的ArrayBuffer被清除,没有   更长的可用。它的内容(字面上很安静)转移到了   工人背景。

     

- Eric Bidelman Google开发人员,来源:html5rocks

唯一的问题是到目前为止只有two things that are transferrableArrayBufferMessagePort。 (Canvas Proxies希望稍后出现)。 ArrayBuffers无法通过其API直接操作,应该用于创建typed array objectDataView,以便为缓冲区提供特定视图,并能够对其进行读写。

来自html5rocks链接

  

要使用可传输对象,请使用稍微不同的签名   的postMessage():

     

worker.postMessage(arrayBuffer, [arrayBuffer]);

     

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

     

工人案例,第一个参数是数据,第二个参数是   应转移的项目清单。第一个论点不是   顺便说一句,必须是一个ArrayBuffer。例如,它可以是JSON   对象:

     

worker.postMessage({data: int8View, moreData: anotherBuffer}, [int8View.buffer, anotherBuffer]);

所以根据你的

var worker = new Worker('js2.js');
worker.postMessage(buffer, [ buffer]);
worker.postMessage(obj, [obj.mat2]);

应该以极快的速度运行,并且应该被转移为零拷贝。唯一的问题是,如果您的bufferobj.mat2不是ArrayBuffer或可转让的。您可能会将ArrayBuffers与typed array视图混淆,而不是使用其缓冲区。

所以如果你有这个ArrayBuffer及它的Int32表示。 (虽然该变量标题为view,但它不是DataView,但DataView有一个属性缓冲区,就像类型化数组一样。在编写本文时,MDN使用名称&#39; view&#39;对于调用类型化数组构造函数的结果,所以我认为这是一种定义它的好方法。)

var buffer = new ArrayBuffer(90000000);
var view = new Int32Array(buffer);
for(var c=0;c<view.length;c++) {
    view[c]=42;
}

这是做的(发送视图)

worker.postMessage(view);

这是你应该做的(发送ArrayBuffer)

worker.postMessage(buffer, [buffer]);

这些是运行this test on plnkr后的结果。

Average for sending views is 144.12690000608563
Average for sending ArrayBuffers is 0.3522000042721629

编辑:正如@Bergi the comments所述,如果您有视图,则根本不需要缓冲变量,因为您可以像这样发送view.buffer < / p>

worker.postMessage(view.buffer, [view.buffer]);

正如对未来读者的旁注,只是发送一个没有最后一个参数的ArrayBuffer来指定ArrayBuffers是什么,你将无法转发ArrayBuffer

换句话说,当发送可转让物品时,你想要这个:

worker.postMessage(buffer, [buffer]);

不是这个:

worker.postMessage(buffer);

编辑:最后一个注意事项,因为您发送缓冲区并不会忘记在网络工作者收到缓冲区后将其恢复为视图。一旦它成为一个视图,你就可以再次操作它(从中读取和写入)。

为赏金:

  

我也对firefox / chrome的官方大小限制感兴趣(不是   唯一的时间限制)。但是回答原始问题符合条件   赏金(;

至于webbrowsers限制发送一定大小的东西,我并不完全确定,但是从引用html5rocks上的条目来看,Eric Bidelman在谈论工作人员的时候他确实提出了一个50 mb的文件而没有使用可转移文件数据类型在几百毫秒内,并且通过我的测试显示,使用可传输数据类型仅在大约一毫秒内。其中50 MB是非常大的。

纯粹是我自己的看法,但我不相信您在可传输或不可传输的数据类型上发送的文件大小有限,而不是数据类型本身的限制。当然,您最担心的可能是浏览器停止长时间运行的脚本,如果它必须复制整个脚本并且不是零拷贝和可转移的。

希望这篇文章有所帮助。老实说,在此之前我对可转移物一无所知,但通过一些测试以及Eric Bidelman撰写的博客文章很有趣。

答案 1 :(得分:8)

我也遇到了网络工作者的问题,直到我向网络工作者传递了一个论点。

所以而不是

worker.postMessage( buffer,[ buffer]);
worker.postMessage(obj,[obj.mat2]);

尝试

var myobj = {buffer:buffer,obj:obj};
worker.postMessage(myobj);

通过这种方式,我发现它通过引用传递,而且速度非常快。我每隔5秒就在一次推送中来回发送超过20,000个数据元素,而我没有注意到数据传输。 我一直专注于使用chrome,所以我不知道它会如何在其他浏览器中使用。

<强>更新

我已经对一些统计数据进行了一些测试。

tmp = new ArrayBuffer(90000000);
test = new Int32Array(tmp);
for(c=0;c<test.length;c++) {
    test[c]=42;
}
for(c=0;c<4;c++) {
    window.setTimeout(function(){
        // Cloning the Array. "We" will have lost the array once its sent to the webworker. 
        // This is to make sure we dont have to repopulate it.
        testsend = new Int32Array(test);
        // marking time. sister mark is in webworker
        console.log("sending at at  "+window.performance.now());
        // post the clone to the thread.
        FieldValueCommunicator.worker.postMessage(testsend);
    },1000*c);
}

测试结果。我不知道这是否属于你的慢类别,因为你没有定义“慢”

  • 发送电子邮件:28837.418999988586
  • 收到28923.06199995801
  • 86 ms

  • 发送电子邮件地址为212387.9840001464

  • 收到212504.72499988973
  • 117 ms

  • 发送电子邮件地址:247635.6210000813

  • 收到247760.1259998046
  • 125 ms

  • 发送电子邮件:288194.15999995545

  • 收到288304.4079998508
  • 110 ms