WebAssembly を使用する場合、モジュールをダウンロードしてコンパイルし、インスタンス化し、JavaScript でエクスポートされたものを使用することがよくあります。この投稿では、効率を最適化するために推奨されるアプローチについて説明します。
WebAssembly を使用する場合、モジュールをダウンロードしてコンパイルし、インスタンス化し、 エクスポートしたテンプレートを使ってこの投稿は、一般的ではあるものの最適ではないコードから始まっています 可能な最適化について説明し、最後に JavaScript から WebAssembly を実行する最も簡単で効率的な方法です。
次のコード スニペットは、最適ではないものの、ダウンロード、コンパイル、インスタンス化のダンス全体を実行します。
これは使用しないでください。
(async () => {
const response = await fetch('fibonacci.wasm');
const buffer = await response.arrayBuffer();
const module = new WebAssembly.Module(buffer);
const instance = new WebAssembly.Instance(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
new WebAssembly.Module(buffer)
を使用してレスポンス バッファをモジュールに変換する方法に注目してください。これは
つまり、完了するまでメインスレッドをブロックします。使用しないようにする目的で、Chrome では
4 KB を超えるバッファの WebAssembly.Module
を無効にします。サイズ制限を回避するには、
代わりに await WebAssembly.compile(buffer)
を使用してください。
(async () => {
const response = await fetch('fibonacci.wasm');
const buffer = await response.arrayBuffer();
const module = await WebAssembly.compile(buffer);
const instance = new WebAssembly.Instance(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
await WebAssembly.compile(buffer)
はまだ最適なアプローチではありませんが、これについては次のスライドで説明します。
なります。
await
を使用すると、変更したスニペット内のオペレーションのほとんどが非同期になりました。
定義します。唯一の例外は new WebAssembly.Instance(module)
で、同じ 4 KB のバッファを持ちます。
Chrome でのサイズ制限。一貫性を保ち、メインスレッドを維持するため
Free の場合、非同期関数を使用できます。
WebAssembly.instantiate(module)
。
(async () => {
const response = await fetch('fibonacci.wasm');
const buffer = await response.arrayBuffer();
const module = await WebAssembly.compile(buffer);
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
先ほど示唆した compile
の最適化に戻りましょう。ストリーミング
コンパイルする場合、ブラウザは
モジュール バイトのダウンロード中に、WebAssembly モジュールのコンパイルを開始します。ダウンロード以降
コンパイルが並列で行われるため、特に大きなペイロードでは高速になります。
この最適化を有効にするには、WebAssembly.compile
ではなく WebAssembly.compileStreaming
を使用します。
この変更により、中間配列バッファを取り除くこともできます。これは、
await fetch(url)
から直接返される Response
インスタンス。
(async () => {
const response = await fetch('fibonacci.wasm');
const module = await WebAssembly.compileStreaming(response);
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
WebAssembly.compileStreaming
API は、Response
に解決される Promise も受け入れます。
作成します。コード内の他の場所に response
の必要がない場合は、Promise を渡すことができます。
結果を明示的に await
化することなく、fetch
から直接返されるようにします。
(async () => {
const fetchPromise = fetch('fibonacci.wasm');
const module = await WebAssembly.compileStreaming(fetchPromise);
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
fetch
の結果を他の場所でも必要としない場合は、直接渡すこともできます。
(async () => {
const module = await WebAssembly.compileStreaming(
fetch('fibonacci.wasm'));
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
ただ、個人的には別の行に記述した方が読みやすいと思います。
レスポンスをモジュールにコンパイルし、すぐにインスタンス化する方法をご覧ください。結局のところ、
WebAssembly.instantiate
は一度にコンパイルとインスタンス化を実行できます。「
WebAssembly.instantiateStreaming
API はこの処理をストリーミング方式で行います。
(async () => {
const fetchPromise = fetch('fibonacci.wasm');
const { module, instance } = await WebAssembly.instantiateStreaming(fetchPromise);
// To create a new instance later:
const otherInstance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
インスタンスが 1 つしかない場合は、module
オブジェクトを保持する意味がありません。
コードをさらに簡素化します。
// This is our recommended way of loading WebAssembly.
(async () => {
const fetchPromise = fetch('fibonacci.wasm');
const { instance } = await WebAssembly.instantiateStreaming(fetchPromise);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
適用した最適化の概要は次のとおりです。
- 非同期 API を使用してメインスレッドのブロックを回避する
- ストリーミング API を使用して WebAssembly モジュールをより迅速にコンパイルおよびインスタンス化する
- 必要のないコードを記述しない
WebAssembly をぜひご利用ください。