3
0

More than 5 years have passed since last update.

flutter_web から JS の WebWorkers を呼んでみた

Posted at

Flutter web でアプリを試作してみた の処理部分を 生 JS の Web worker にくくりだしました。

からです。この文章は、主に後者のメモです。

まとめ

  • Web worker へは、JSON で渡した(思考停止)
  • Web worker から Uint8Array を返すと、ByteBuffer にキャストできる形で返ってくる。
  • ByteBufferasInt8ListInt8List にし、 必要に応じて sublistList<int> を切り出して使用した。

今回の Web worker の入出力

  • 入力: 試行回数と、フィルタの構成(JSON)
  • 出力: フィルタをパスした回数と、そのサンプル群

Web worker 部(処理側)

web/filter.js を置いておくと、webdev serve で参照でき、webdev build 時に build/filter.js にコピーしてくれます。

入力は、Enumindex への変換などが必要で、JSONで渡すことにしました。
処理は、汎用的な話ではないのでざっくり載せておきます。

web/filter.js
/* 前略 */
self.addEventListener('message', function (e) {
  // 指定された試行回数と、フィルタを元に、ランダムな局面を生成しパスする回数を数える
  const param = JSON.parse(e.data);
  const trials = param.trials;
  const filters = param.filters;
  const testFunc = makeTestFunc("yama", filters);
  var samples = [], passedCount = 0;
  const yama = Array.from(initYama);
  for (var i = 0; i < trials; ++i) {
    yama.shuffle();
    if (testFunc(yama)) {
      ++passedCount;
      if (samples.length < 100) {
        samples.push(Array.from(yama));
      }
    }
  }

それで、flutter_web に結果を返すのがこちらの部分。
配列をそのまま返すと、プロセス間のコピーが生じて遅くなるので、Transferable な形で返す。
所有権の移動だけになるらしい。shared なのは未だ無さそう。

最初、パスした回数などもバイナリに埋め込まないと駄目かと思ったのですが、
普通にオブジェクトを渡せば、flutter 側で Map で受け取れるので、
重たいデータだけ変換してやると良いと思います。

web/filter.js
  // Transferable に変換してコピーのロスを減らす
  const result = new Uint8Array(samples.length * 32);
  var offset = 0;
  for (var i = 0; i < samples.length; ++i) {
    const yama = samples[i];
    for (var j = 0; j < 32; ++j) {
      result[offset + j] = yama[j];
    }
    offset += 32;
  }
  self.postMessage({ passedCount: passedCount, samples: result.buffer }, [result.buffer]);
}, false);

この文章を書いていて、最終的に Uint8Array が固定長で良くなったので、Arraypush してるのを直接、
Uint8Array に保存すれば無駄が無くなることに気がついた。

Flutter 部(呼び出し側)

呼び出しは postMessage、結果の受け取りは onMessage.listen で行う。

配列の結果は ByteBuffer にキャストすれば良かった。print(msg.data["samples"]) すると NativeByteBuffer だと言われましたが、問題なくキャストできているようなのでこれ以上深堀りしていない。

ByteBufferasInt8ListInt8List にしておき、
必要に応じて sublistList<int> を切り出して使用した。

lib/main.dart
final w = Worker("filter.js");
w.onMessage.listen((msg) {
  final passedCount = msg.data["passedCount"] as int;
  final samples = msg.data["samples"] as ByteBuffer; /* NativeByteBuffer */
  setState(() {
    _passedCount = passedCount;
    _samples = samples.asInt8List();
    /* (中略) */
w.postMessage(jsonEncode({"trials": _trials, "filters": _filters}));
3
0
0

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
3
0