WebAssembly モジュールを効率的に読み込む

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 モジュールのコンパイル時間より長い場合、WebAssembly.compileStreaming()
最後のバイトがダウンロードされると、ほぼ直ちにコンパイルが終了します。

この最適化を有効にするには、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 をぜひご利用ください。