WebAssemblyでは、コンパイル済のwasmモジュールをindexDBでローカルにキャッシュすることができます(Chrome 61+, Firefox 55+ で確認)。とは言っても生のwasmをいじることはあんまりなくて、大抵はEmscriptenで生成したwasmを使いますよね? というわけで、この記事ではEmscriptenで生成したwasmをキャッシュする方法を説明します。
wasmファイルをコンパイルしてみる
Emscriptenでキャッシュしたい場合は、自分でwasmモジュールのインスタンス化を行うようにする必要があります。具体的にはemccの--preオプションで指定するファイルでModule.instantiateWasmを実装すればokです。とりあえず普通にフェッチしてコンパイルしてインスタンス化するようなコードを書いてみましょう。
var Module = {};
var wasmCacheVersion = 1;
Module["instantiateWasm"] = function (info, callback) {
fetch("foo.wasm")
.then(function(res) { return res.arrayBuffer() })
.then(function(buff) { return WebAssembly.instantiate(buff, info) })
.then(function(result) { callback(result.instance) });
return {};
};
infoでWebAssembly.instantiateで指定するインポートオブジェクトを受け取ります。callback関数では、インスタンス化されたWebAssembly.Instanceオブジェクトを引数に指定して実行する必要があります。{}を返しているのは元々インスタンス化を行っている関数でそれを返していたので同じように書いています(ちなみに何も返さないとエラーになる)。
キャッシュしたバージョンを使う
次にキャッシュするように書き換えてみましょう。
var Module = {};
var wasmCacheVersion = 1;
Module["instantiateWasm"] = function (info, callback) {
instantiateCachedURL(wasmCacheVersion, "foo.wasm", info).then(function (instance) {
callback(instance);
});
return {};
};
簡単にするために、上のmdnのページで紹介されているinstantiateCachedURLを使います。こいつはキャッシュされていたらそれを使い、なければフェッチしてコンパイルしたものをローカルにキャッシュしてくれます(詳しくはwasm-utils.jsを参照)。実行したら、resolve時にWebAssembly.Instanceオブジェクトを渡してくれるのでそれをcallback関数の引数に指定すればokです。
実際にキャッシュしたバージョンについてはデモを確認してください。初回と2回目以降で結構スピードが変わっているはずです。
まとめ
Emscriptenで生成されたwasmモジュールをローカルにキャッシュする方法を紹介しました。この機能はモバイルだと特に嬉しいですねー。