【问题标题】:how to pass large data to web workers如何将大数据传递给网络工作者
【发布时间】:2013-10-03 07:00:12
【问题描述】:

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

我已经尝试了以下代码:

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!');
}

【问题讨论】:

  • 虽然它适用于 Web Worker => 浏览器,但 in this RedHat article 有一个很好的解释。基本上,您需要将缓冲区分成几个块并传递每个块。其他选项(也在该链接中)是使用 FileReader。不过,由于我也很感兴趣,因此我对您的问题提出了悬赏。
  • 也许你应该只传输那个缓冲区而不是序列化它?
  • Transferables 将作为第三个参数传递,而不是第二个...
  • @dandavis 不是两个 worker.postMessage(arrayBuffer, [arrayBuffer]); window.postMessage(arrayBuffer, targetOrigin, [arrayBuffer]); 完美有效(来源:html5rocks.com/en/tutorials/workers/basics/#toc-transferrables
  • 这是一个诚实的简短回答:1)您还不能真正做到这一点,您不能在工作人员之间轻松共享​​>数据(但如果足够了,您可以传输它) 和 b) groups.google.com/a/chromium.org/forum/#!topic/blink-dev/…

标签: javascript web-worker


【解决方案1】:

更新

Chrome、Edge 和 Firefox 的现代版本现在支持 SharedArrayBuffers(尽管在撰写本文时还没有 safari see SharedArrayBuffers on MDN),因此这将是另一种快速传输数据的可能性,与一组不同的权衡相比到可转让的 (you can see MDN for all the trade offs and requirements of SharedArrayBuffers)。

更新

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

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

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

回答原问题:

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

@MichaelDibbets answer 的替代方案是将对象的副本发送给 webworker,使用的是零拷贝的transferrable object

这表明您打算让您的数据可传输,但我猜它没有成功。因此,我将解释一些数据对于您和未来的读者可转移意味着什么。

“通过引用”传输对象(尽管这不是它的完美术语,正如在下一个引用中解释的那样)不仅仅适用于任何 JavaScript 对象。它必须是可传输的数据类型。

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

结构化克隆很棒,但一个副本可能需要数百个 毫秒。为了对抗性能命中,您可以使用 Transferable 对象。

使用 Transferable Objects,数据从一个上下文传输到 其他。它是零拷贝,极大地提高了 向 Worker 发送数据。如果你是 来自 C/C++ 世界。但是,与传递引用不同,“版本” 一旦转移到调用上下文中就不再可用 新的语境。例如,当从 你的主应用程序到 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 表示。 (虽然变量的标题是视图,但它不是 DataView,但 DataView 确实有一个属性缓冲区,就像类型化数组一样。此外,在编写此内容时,MDN 使用名称“view”作为调用类型化数组构造函数的结果所以我认为这是定义它的好方法。)

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

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

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

作为对未来读者的附注,只是发送一个 ArrayBuffer 而没有最后一个参数指定 ArrayBuffer 是什么,你不会发送 ArrayBuffer 可转移

换句话说,在发送可转让物品时,您需要这样:

worker.postMessage(buffer, [buffer]);

不是这个:

worker.postMessage(buffer);

编辑:最后一点,因为您正在发送缓冲区,请不要忘记在 webworker 收到缓冲区后将其转回视图。一旦它是一个视图,您就可以再次对其进行操作(读取和写入)。

为了赏金:

我也对 firefox/chrome 的官方尺寸限制感兴趣(不是 只有时间限制)。但是回答原始问题有资格 赏金 (;

至于网络浏览器限制发送一定大小的东西,我不完全确定,但从 Eric Bidelman 的 html5rocks 条目中谈到工人时,他确实提出了一个 50 mb 的文件,该文件在不使用可传输设备的情况下被传输数据类型在数百毫秒内,如我的测试所示,使用可转移数据类型只需大约一毫秒。哪一个 50 mb 真的很大。

纯粹是我个人的观点,但我认为除了数据类型本身的限制之外,对于可传输或不可传输数据类型发送的文件大小没有限制。当然,您最担心的可能是浏览器停止长时间运行的脚本,如果它必须复制整个内容并且不是零复制和可转移的。

希望这篇文章有所帮助。老实说,在此之前我对可转移对象一无所知,但通过一些测试和 Eric Bidelman 的博文了解它们很有趣。

【讨论】:

  • 或者发送view.buffer,您不需要为此提供额外的变量:-)
  • 谢谢@Bergi 我已经更新了我的答案,以表明这是一个更好的选择。
  • 缓冲区现在也可以共享(请参阅我关于问题的 cmets),但只能在 Chrome 中。
  • 感谢您的明确解释。您为我清除了所有在线手册中从未弄清楚的部分,即缓冲区发挥作用以及它扮演什么角色。
  • 不幸的是,SharedArrayBuffer 已于 2018 年 1 月 5 日在所有主要浏览器中默认禁用,以响应 Spectre。您可能需要修改该编辑。
【解决方案2】:

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

所以不是

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 毫秒


  • 发送至 212387.9840001464

  • 收到于 212504.72499988973
  • 117 毫秒


  • 发送至 247635.6210000813

  • 收到于 247760.1259998046
  • 125 毫秒


  • 发送至 288194.15999995545

  • 收到于 288304.4079998508
  • 110 毫秒

【讨论】:

  • 我会说 86 毫秒的数据可以通过引用传递慢。
  • @arthur 负责调度、线程同步、解耦我认为它非常快。此外,我正在传输 90000000 * 32 位 = 2880000000 位 = 360000000 字节 = 351562.5kb = 343mb 的数据。我在上面做这个的电脑有 ddr2 和 5.3gb 的数据传输率。在完全不间断的访问中,它可以重新分配的最快速度是 64 毫秒。但由于还有其他进程需要访问,您需要等待。因此延迟和这种速度。在具有更快 RAM、CPU 和总线速度的电脑上会更快
  • 从合理的假设出发,所有 javascript 变量都有自己的包装器,将其分配给需要更新的线程、范围等。来自 JavaScript 观点的引用,但来自运行时端点的大量数据需要重新分配
  • 是的,我的意思是,如果它实际上是通过引用传递的,则会有 0 毫秒的延迟,因为您不需要复制任何内容。将变量传递给函数时,通常不会等待 86 毫秒,对吧?数组缓冲区的Transferable 似乎是解决 OP 问题的最有效方法。抱歉,你的“我不知道这是否属于你的慢速”只是让我表达了我的意见;-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多