Flutter web でアプリを試作してみた の処理部分を 生 JS の Web worker
にくくりだしました。
-
JavsScript
のFunction
コンストラクタ を使いたかった - 生だとどういう型が動くのか知っておきたかった
からです。この文章は、主に後者のメモです。
まとめ
-
Web worker
へは、JSON
で渡した(思考停止) -
Web worker
からUint8Array
を返すと、ByteBuffer
にキャストできる形で返ってくる。 -
ByteBuffer
をasInt8List
でInt8List
にし、
必要に応じてsublist
でList<int>
を切り出して使用した。
今回の Web worker
の入出力
- 入力: 試行回数と、フィルタの構成(JSON)
- 出力: フィルタをパスした回数と、そのサンプル群
Web worker
部(処理側)
web/filter.js
を置いておくと、webdev serve
で参照でき、webdev build
時に build/filter.js
にコピーしてくれます。
入力は、Enum
の index
への変換などが必要で、JSONで渡すことにしました。
処理は、汎用的な話ではないのでざっくり載せておきます。
/* 前略 */
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
で受け取れるので、
重たいデータだけ変換してやると良いと思います。
// 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
が固定長で良くなったので、Array
に push
してるのを直接、
Uint8Array
に保存すれば無駄が無くなることに気がついた。
Flutter
部(呼び出し側)
呼び出しは postMessage
、結果の受け取りは onMessage.listen
で行う。
配列の結果は ByteBuffer
にキャストすれば良かった。print(msg.data["samples"])
すると NativeByteBuffer
だと言われましたが、問題なくキャストできているようなのでこれ以上深堀りしていない。
ByteBuffer
を asInt8List
で Int8List
にしておき、
必要に応じて sublist
で List<int>
を切り出して使用した。
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}));