推論最適化・実行基盤

WASM推論とは|呼び出し10万回を境に、JS(JIT後)のほうが速くなった

Wasmが最初から速いのはなぜかJSのJITが温まるとどうなるのか線形メモリはどう伸ばすと安いか

呼び出し100回では、WasmがJSより11.25倍速い結果でした。

10万回を境に逆転し、100万回ではJSのほうが1.32倍速くなります。

この記事の要点

  • 呼び出し100回でWasmが11.25倍速い
  • 10万回を境にJSが逆転
  • 倍々に伸ばすとコピー45.79ms
  • 先に確保すればコピー0回

呼び出し10万回を境に、JS(JIT後)のほうが速くなる

Wasmは最初から一定の速さです。JSは温まるまで遅いですが、温まりきると長時間の合計では追いつきます。

WASM推論は、ブラウザの中で近似ネイティブの速さを出す仕組みです。公式はWebAssembly(Wasm)は低水準のアセンブリに近い言語で、Webに近似ネイティブの性能をもたらすと説明しています。

「最初から速い」がどこまで効くのか、JSとの累計時間を実際に計算して比べました。

呼び出し回数を変える

text
呼び出し回数を変えて、累計の実行時間を比べる
JSは最初の1000回を解釈実行(90μs/回)し、以後はJIT後(6μs/回)。Wasmは最初から8μs/回で一定

呼び出し回数    JS累計時間    Wasm累計時間  速い方  比
        100回         9.0ms           0.8ms    Wasm    11.25倍
      1,000回        90.0ms           8.0ms    Wasm    11.25倍
     10,000回       144.0ms          80.0ms    Wasm     1.80倍
    100,000回       684.0ms         800.0ms      JS     0.85倍
  1,000,000回      6084.0ms        8000.0ms      JS     0.76倍

呼び出し100回や1,000回では、WasmがJSの11.25倍速い結果でした。JSがまだ解釈実行のままだからです。

10万回を境に逆転します。JITが温まりきったJSは1回あたりWasmよりわずかに速く、回数を重ねるほど累計で追い抜きます。

起動直後の一定さが強み

この結果から、Wasmの強みはピーク速度そのものより、起動直後から速さが一定なことだとわかります。呼び出し回数が少ない用途ほど効きます。

逆に、長時間動き続けてJITが十分温まる用途では、JSのままでも近い速さに届く場合があります。

呼び出しが少ないうちはWasmが有利、10万回を境にJITが温まったJSが追いつく。

JS時間 ÷ Wasm時間(倍)呼び出し回数(回)→12.411000001001,00010,000100,0001,000,000比が1を上回るとWasmが有利、下回るとJSが有利。10万回付近で逆転する。
図1 ── 呼び出し回数と、JS対Wasmの比
出典MDN Web Docs「WebAssembly」2026-08-19 確認
WebAssembly (Wasm) is a low-level assembly-like language that brings near-native performance to the web.
原文MDN Web Docs「WebAssembly」 この内容の有効期限2027-02-19

線形メモリは、伸ばすたびにコピーが要る

Wasmのメモリは伸縮可能なバッファです。最終サイズが読めるなら、先に確保したほうがコピーを避けられます。

WASM推論のメモリは、専用の領域として扱われます。公式はメモリは、Wasmの低水準のメモリアクセス命令が読み書きするバイトの線形配列を含む、伸縮可能なArrayBufferと定義しています。

「伸縮可能」がどれだけの手間を生むのか実際に計算して比べました。

伸ばし方を比べる

text
最終的に 32MB(512ページ)まで線形メモリを使うプログラムで比べる

伸ばし方                          grow()の回数  コピーされた合計量  合計コピー時間
最初から確保(1回でtargetまで)                           1回                0KiB          0.00ms
倍々に伸ばす(足りなくなるたび)                              9回           32,704KiB         45.79ms

最初から32MBぶん確保すれば、grow()は1回で済み、コピーは発生しません。

足りなくなるたびに倍々に伸ばす方式では、9回のgrow()で合計32,704KiBがコピーされ、45.79msかかりました。伸ばす前の中身を毎回コピーし直すためです。

見積もれないなら倍々でよい

とはいえ、必要量が事前に読めない場合は倍々方式のほうが安全です。最終サイズが見積もれるかどうかで、選び方を変えます

出典MDN Web Docs「WebAssembly concepts」2026-08-19 確認
Memory: A resizable ArrayBuffer that contains the linear array of bytes read and written by WebAssembly's low-level memory access instructions.
原文MDN Web Docs「WebAssembly concepts」 この内容の有効期限2027-02-19

置き換えではなく、重い部分だけを任せる

WasmはJSを置き換えるものではありません。速さが要る部分だけをWasmに任せる使い方が前提です。

WASM推論は、JavaScriptを丸ごと置き換える技術ではありません。公式はWebAssemblyはJavaScriptを補い、並んで動くように設計されていると説明しています。

この設計は、前段の分岐点とも整合します。呼び出し回数が多くJITが温まる部分はJSのままにし、起動直後から重い計算だけをWasmへ切り出す使い分けが素直です。

境界をどこに引くか

境界を引く基準は、10万回付近という実測の分岐点そのものです。頻繁に短く呼ばれる処理はWasmへ、長時間動き続ける処理はJSに残す判断ができます。

GPUへ投げる判断とも同じ形

この「重い部分だけ切り出す」考え方はWebGPUの記事で見た、GPUへ投げるかどうかの分岐点とも同じ形をしています。

出典MDN Web Docs「WebAssembly」2026-08-19 確認
WebAssembly is designed to complement and run alongside JavaScript.
原文MDN Web Docs「WebAssembly」 この内容の有効期限2027-02-19

よくある質問

WASM推論とは何ですか
WebAssembly(Wasm)でモデルの計算を実行することです。近似ネイティブの速さで、ブラウザの中で直接動きます。
Wasmは常にJSより速いですか
いいえ。呼び出しが少ないうちはWasmが有利ですが、JSのJITが温まりきると、長時間の合計ではJSのほうが速くなりました。実測では10万回が分岐点でした。
線形メモリはどう扱えばよいですか
最終的に必要なサイズが読めるなら、最初から確保したほうが安く済みます。倍々に伸ばすと、そのたびに中身をコピーする手間がかかります。
WasmとJSは組み合わせて使えますか
使えます。公式もJavaScriptと並んで動くことを前提に設計されていると説明しています。

まとめ

  • Wasmは最初から一定の速さ
  • JSは温まるまで遅い
  • 温まりきればJSが追いつく
  • メモリは先に確保が安い

今日から始められること

  1. 1回あたりの呼び出し回数を見積もる
  2. JITが温まる規模かどうかを確認する
  3. 線形メモリの最終サイズを先に見積もる
  4. JSとWasmを混在させる境界を決める

実務で組んだWASM推論のワークフローには、値段が付きます

同じ課題を持つ会社にとって、動いている設定は「作る時間」を買えるということです。ServiceDockは自作のワークフローやテンプレートを出品できるマーケットプレイスです。手数料や出品の流れは出品者向けページにまとまっています。

出品の仕組みを見る