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