LoginSignup
46
34

More than 5 years have passed since last update.

WebWorkersで巨大データ転送の不思議

Last updated at Posted at 2014-09-03

Web Workersで、worker間でデータをやりとりする際、普通はpostMessageにオブジェクトを渡すだけでことが足りる.

Simple.js
array = new Uint8Array(1000 * 1000 * 256);
// ★
worker.postMessage({
  array: array
});

しかし、WebWorkersのpostMessageは参照ではなく、データの実体をコピーして飛ばすため、arrayのサイズがデカくなってくると、workers間のデータ転送のオーバヘッドが馬鹿にならない.
重たい処理をUIから分離するためにWebWorkersを活躍させたい時に困ってしまう.

そこで、登場するのがpostMessageの第2引数.
ここにTransferableなオブジェクト(Uint8ArrayFloat32ArrayがもつArrayBufferはTransferable実装)を喰わせると、データ実体ではなく、データの参照が送信されるため、転送オーバヘッドは限りなく小さくなる.

Tranferable_1.js
array = new Uint8Array(1000 * 1000 * 256);
// ★
worker.postMessage({
  array: array
}, [array.buffer]);

ただ、この方法で問題なのは、一旦送信してしまうと、送信元のworkerではarrayを扱えなくなる、という点1.
送信元workerで、送信後はarrayに対して、値の参照も更新も許されなくなり、delete arrayされているのと大差ない状態になってしまう.

そこで、送信前にUint8Arrayのコピー、array2を作成し、こいつのTranferableデータを送信してみる.

Transferable_2.js
array = new Uint8Array(1000 * 1000 * 256);
// ★
array2 = new Uint8Array(array);
worker.postMessage({
  array: array2
}, [array2.buffer]);

確かに元々のarrayを送信している訳ではないので、postMessage後もarrayを送信元workerで触ることは出来る.
もちろん僕も最初は「それって第2引数無で送信していたパターン(冒頭のSimple.js)と大差ないんじゃね?」と考えていた.

そこで、上記3パターンで、//★の箇所から、送信先のworker.onmessageのリスナに到達するまでの時間を計測してみた(jsfiddleに動作コードをおいておく).

256MBのデータをChromeで試した結果、下記となった.

  • Simple:500msec程度
  • Transferable_1: 70msec程度
  • Transferable_2: 180msec程度

Transferable_1が最も速いのは当然なのだが、SimpleとTransferable_2に明らかに差がでている.
Firefoxでも試したが、数値は異なるものの傾向は傾向はChromeの場合と変わらなかった.

正直、何故なのかはサッパリ判らないのだが、どっかで使えるかもしれないので残しておく.


  1. 2017.09.27追記: Shared Array Buffer が利用可能になれば解決する問題ではある 

46
34
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46
34