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モジュールをローカルにキャッシュする方法を紹介しました。この機能はモバイルだと特に嬉しいですねー。