- カテゴリー
- ブラウザAI・性能改善
- 公開日
- 2026.09.09
Contents
はじめに
ブラウザでAIモデルを動かしたとき、「WebGPUを使えば速いはず」と設定だけを変えても原因は分かりません。遅い場所は、モデルのダウンロード、セッション作成、最初のシェーダーコンパイル、各演算、CPUとGPUのデータ転送のどこかにあります。
この記事では、ONNX Runtime Webの診断機能を有効にし、どの段階が遅いかを切り分ける最小ページを作ります。対象はJavaScriptの基本が分かり、ONNXモデルを一度動かしたことがある人です。
完成物と測る区間
次の4区間を別々に表示します。
- モデル取得
- 推論セッション作成
- 初回推論
- 2回目以降10回の平均
プロファイルは計測の詳細を得るための機能であり、実際の利用者向け画面では通常無効にします。
必要環境
mkdir ort-profile-demo
cd ort-profile-demo
npm init -y
npm install onnxruntime-web@1.27.0
npm install --save-dev vite
package.jsonのscriptsへ"dev": "vite"を追加します。今回は公式リポジトリの小さなmul_1.onnxを使います。これは入力値を2乗するテストモデルで、大規模AIの速度を代表するものではありません。
計測コード
main.jsを作ります。
import * as ort from "onnxruntime-web/all";
ort.env.wasm.wasmPaths =
"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.27.0/dist/";
const MODEL_URL =
"https://raw.githubusercontent.com/microsoft/onnxruntime/main/onnxruntime/test/testdata/mul_1.onnx";
function input() {
return new ort.Tensor(
"float32",
Float32Array.from([1, 2, 3, 4, 5, 6]),
[3, 2],
);
}
async function measure(provider) {
const fetchStart = performance.now();
const response = await fetch(MODEL_URL);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const model = new Uint8Array(await response.arrayBuffer());
const fetchMs = performance.now() - fetchStart;
const createStart = performance.now();
const session = await ort.InferenceSession.create(model, {
executionProviders: [provider],
enableProfiling: true,
});
const createMs = performance.now() - createStart;
const firstStart = performance.now();
const first = await session.run({ X: input() });
const firstMs = performance.now() - firstStart;
const steadyStart = performance.now();
for (let i = 0; i < 10; i += 1) await session.run({ X: input() });
const steadyMs = (performance.now() - steadyStart) / 10;
session.endProfiling();
await session.release();
return {
provider,
output: Array.from(first.Y.data),
fetchMs,
createMs,
firstMs,
steadyMs,
};
}
measure("wasm").then(console.table).catch(console.error);
index.htmlからmain.jsをtype="module"で読み、npm run devで開きます。コンソールには区間別の時間とCPUプロファイルが出ます。
WebGPUの演算時間を受け取る
WebGPUではセッション作成前にコールバックを設定します。
ort.env.webgpu.profiling = {
mode: "default",
ondata(data) {
console.table(data);
},
};
measure("webgpu").then(console.table).catch(console.error);
performance.now()はJavaScript側から見た待ち時間です。WebGPUプロファイルはGPU側の演算を調べる手掛かりになります。同じ種類の数値として単純に足し合わせないでください。
Performanceパネルと結び付ける
ort.env.trace = true;
Traceを有効にするとconsole.timeStamp()を使った印が記録されます。Chrome DevToolsのPerformanceパネルで記録し、UIの長い停止と推論処理が重なるかを確認します。診断時だけort.env.logLevel = "verbose"やort.env.debug = trueを使い、本番では戻します。
結果の読み方
- 取得だけが遅い:モデル圧縮、CDN、キャッシュ、分割配信を検討
- セッション作成が遅い:モデル形式、演算子、初期化タイミングを確認
- 初回だけ遅い:ウォームアップやシェーダーコンパイルの影響を確認
- 毎回遅い:演算、テンソル形状、EP、CPU・GPU転送を確認
- UIが固まる:WASMのProxy WorkerやWorker分離を検討
小さなモデルではGPUへ渡す準備の方が重く、WASMが速い場合もあります。EP名だけで性能順位を決めません。
動作確認とエラー確認
正常時は出力が[1,4,9,16,25,36]になり、4区間の有限な時間が表示されます。次も確認します。
- オフラインではHTTPエラーを表示する
- WebGPU非対応端末では失敗を握りつぶさない
endProfiling()を推論終了後に呼ぶ- 診断後に
release()する - 同じモデル、入力、回数、端末で比較する
プロファイリング自体にも負荷があります。記事へ性能値を載せるなら、診断機能を外した測定も別に行い、OS、ブラウザ、CPU、GPU、電源設定を記録します。
トラブル対処
- 「Got invalid dimensions」:このテストモデルは入力Xへ形状[3, 2]の6値を渡します。
- WASM取得失敗:wasmPathsとnpmパッケージの版、CSP、配信URLを確認します。
- WebGPUだけ失敗:WASMで正常系を確かめ、対応演算子とアダプター取得を確認します。
まとめ
性能改善は「WebGPUに変える」ことからではなく、時間を区間に分けることから始めます。ONNX Runtime WebのCPU/WebGPUプロファイルとTraceを組み合わせれば、ネットワーク、初期化、初回、定常処理を混同せずに調査できます。

