web-dev-qa-db-ja.com

ビッグデータをWebワーカーに渡す方法

私はWebワーカーに取り組んでおり、大量のデータをWebワーカーに渡しているため、時間がかかります。データを効率的に送信する方法を知りたいです。

私は次のコードを試しました:

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

[〜#〜]更新[〜#〜]

Mozillaによると SharedArrayBufferはすべての主要なブラウザで無効になっているため、次のEDITで説明するオプションは適用されなくなりました。

SharedArrayBufferは、Spectreに応じて、2018年1月5日にすべての主要なブラウザーでデフォルトで無効になっていることに注意してください。

EDIT:別のオプションがあり、sharedArrayバッファーを送信しています。これは共有メモリとアトミックの下でES2017の一部であり、FireFox 54Nightlyでサポートされるようになりました。あなたがそれについて読みたいならば、あなたは見ることができます ここ 。私はおそらくいつか何かを書き、それを私の答えに追加するでしょう。パフォーマンスベンチマークにも追加してみます。

元の質問に答えるには:

私はWebワーカーに取り組んでおり、大量のデータをWebワーカーに渡しているため、時間がかかります。データを効率的に送信する方法を知りたいです。

@ MichaelDibbets answer の代わりに、彼はオブジェクトのコピーをWebworkerに送信し、ゼロコピーである 転送可能なオブジェクト を使用します。

データを転送可能にするつもりだったことがわかりますが、うまくいかなかったと思います。それで、私はあなたと将来の読者のためにいくつかのデータが転送可能であることが何を意味するかを説明します。

「参照による」オブジェクトの転送(次の引用で説明するように、これは完全な用語ではありませんが)は、JavaScriptオブジェクトで機能するだけではありません。転送可能なデータ型である必要があります。

[Webワーカーの場合]ほとんどのブラウザーは構造化クローンアルゴリズムを実装しており、File、Blob、ArrayBuffer、JSONオブジェクトなどのより複雑なタイプをワーカーに渡したり渡したりすることができます。ただし、postMessage()を使用してこれらのタイプのデータを渡す場合でも、コピーは作成されます。したがって、たとえば50MBの大きなファイルを渡す場合、ワーカーとメインスレッドの間でそのファイルを取得する際に顕著なオーバーヘッドが発生します。

構造化されたクローン作成は素晴らしいですが、コピーには数百ミリ秒かかる場合があります。パフォーマンスヒットと戦うために、転送可能なオブジェクトを使用できます。

Transferable Objectsを使用すると、データは1つのコンテキストから別のコンテキストに転送されます。これはゼロコピーであり、ワーカーへのデータ送信のパフォーマンスを大幅に向上させます。 C/C++の世界から来た場合は、参照渡しと考えてください。ただし、参照渡しとは異なり、呼び出し元のコンテキストの「バージョン」は、新しいコンテキストに転送されると使用できなくなります。たとえば、ArrayBufferをメインアプリからWorkerに転送すると、元のArrayBufferがクリアされ、使用できなくなります。その内容は(文字通り静かに)ワーカーコンテキストに転送されます。

-Eric BidelmanGoogleの開発者、出典: html5rocks

唯一の問題は、現時点では 譲渡可能なものが2つ しかないことです。 ArrayBuffer 、および MessagePort 。 ( Canvas Proxies うまくいけば後で来る)。 ArrayBuffersはAPIを介して直接操作することはできず、 型付き配列オブジェクト または DataView を作成して、バッファーに特定のビューを提供し、読み取りと書き込みができるようにするために使用する必要がありますそれに。

Html5rocksリンクから

転送可能なオブジェクトを使用するには、postMessage()のわずかに異なる署名を使用します。

worker.postMessage(arrayBuffer, [arrayBuffer]);

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

ワーカーの場合、最初の引数はデータで、2番目の引数は転送する必要のあるアイテムのリストです。ちなみに、最初の引数は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]);

高速で実行され、ゼロコピーで転送される必要があります。唯一の問題は、bufferまたはobj.mat2ArrayBuffer または転送可能でない場合です。 ArrayBuffersを、そのバッファーを使用する必要があるものではなく、 型付き配列 のビューと混同している可能性があります。

したがって、このArrayBufferがあり、それがInt32表現である場合。 (変数のタイトルはviewですが、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]);

これらは、実行後の結果です plnkrでのこのテスト

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

編集: @ Bergi in コメント で述べられているように、ビューがある場合はview.bufferを送信できるため、バッファ変数はまったく必要ありません。そのようです

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

将来の読者への補足として、ArrayBufferが何であるかを指定する最後の引数なしでArrayBufferを送信するだけでは、ArrayBufferを転送可能に送信することはありません。

言い換えれば、譲渡可能物を送信するときは、これが必要です。

worker.postMessage(buffer, [buffer]);

これではない:

worker.postMessage(buffer);

編集:そして、バッファを送信しているので最後の注意点は、Webworkerがバッファを受信したら、バッファをビューに戻すことを忘れないでください。ビューになったら、もう一度操作(読み取りと書き込み)できます。

そしてその恩恵のために:

Firefox/Chromeの公式サイズ制限(時間制限だけでなく)にも興味があります。ただし、元の質問に答えると、報奨金の対象になります(;

特定のサイズのものを送信するためのウェブブラウザの制限については、完全にはわかりませんが、その引用から、Eric Bidelmanがワーカーについて話しているときにhtml5rocksにエントリがあったため、転送可能なデータ型を使用せずに転送されている50MBのファイルが表示されました数百ミリ秒で、転送可能なデータ型を使用してわずか約1ミリ秒で私のテストで示されているように。どの50mbが正直かなり大きいか。

純粋に私自身の意見ですが、データ型自体の制限以外に、転送可能または転送不可能なデータ型で送信するファイルのサイズに制限があるとは思いません。もちろん、最大の心配は、ブラウザがすべてをコピーする必要があり、ゼロコピーや転送ができない場合に、長時間実行されているスクリプトを停止することです。

この投稿がお役に立てば幸いです。正直なところ、これまで譲渡可能物については何も知りませんでしたが、いくつかのテストとEricBidelmanによるブログ投稿を通じてそれらを理解するのは楽しかったです。

29
John

Webworkerに1つの引数を渡すまで、WebWorkerにも問題がありました。

だから代わりに

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

試してみてください

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

このようにして、参照によって渡され、めちゃくちゃ速いことがわかりました。データ転送に気付かずに、5秒ごとに1回のプッシュで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ミリ秒
9
Tschallacka