WebGL だけが、妙に速い
前回の 2台を1台に — razr fold でベンチマークを測り直す では、4本のベンチマークを測り直し、「GPU の連続処理は強い、GPU との往復は遅い」とまとめました。
ただ、数字を並べ直すと、もうひとつ気になる傾向があります。WebGL が、以前の2台より目立って速いのです。
| ベンチマーク | razr fold | iPad mini | Pixel 10 Pro |
|---|---|---|---|
| TF.js LSTM Cell(WebGL) | 21.1ms | 98ms | — |
| ティック描画 100万点(WebGL) | 90fps | 49fps | 121fps |
| モンテカルロ 5万回(WebGL2) | 17.6ms | 25ms | — |
一方で、同じ GPU を使うはずの WebGPU は、計算の往復が絡むと iPad mini に届きません。同じ Adreno の上で、WebGL と WebGPU の評価がここまで分かれる。その理由を、ブラウザ自身の申告から探ります。
ブラウザに、経路を申告させる
Chrome のアドレスバーに chrome://gpu と入れると、GPU の種類、ドライバ、各 API がどの経路で動いているかが一覧で出てきます。razr fold(Chrome 155 / Android 16)の要点はこうでした。
| 項目 | 値 |
|---|---|
| GPU | Adreno (TM) 829 |
| ドライバ | OpenGL ES 3.2 V@0842.43.1(2026年5月27日付) |
| WebGL の経路 | gl=egl-angle, angle=openglesANGLE → OpenGL ES |
| WebGPU の経路 | Dawn → Vulkan(既定のアダプタ) 別に OpenGL ES の互換モードアダプタもある |
| Chrome の画面合成 | Skia Backend: GaneshGL(GL)Vulkan: Disabled |
ここから、この端末の Chrome の構造が読み取れます。WebGL と Chrome 自身の画面描画は、どちらも OpenGL ES の上で動いています。WebGPU だけが、Vulkan という別の道を通っています。
WebGL は本線、WebGPU は脇道
razr fold:WebGL は「いつもの道」を通る
WebGL の命令は、ANGLE が薄く仲介して、Qualcomm のネイティブな OpenGL ES ドライバに渡ります。Adreno の OpenGL ES ドライバは、Android のゲームやアプリで長年使い込まれてきた部分です。
しかも、この端末では Chrome 自身の画面描画も GL で動いています。GL は、Chrome がこの端末で毎フレーム使っている本線です。WebGL はその本線に相乗りしている形になります。
razr fold:WebGPU は別の道を通る
WebGPU は、Chrome 内部の Dawn が Vulkan に変換して動かします。Chrome の中で Vulkan を使っているのは、ほぼ WebGPU だけです。使い込まれ方では、GL に及びません。
chrome://gpu の「Problems Detected」には、Disable webgpu on vk via gl interop という項目もありました。Vulkan で動く WebGPU と、GL で動く画面合成をつなぐ相互運用の一部に既知の問題があり、無効化されているという意味です。計算処理の速度にどこまで効いているかは、この情報だけでは分かりません。ただ、WebGPU がこの端末で「本線から外れた場所」にいることは読み取れます。
iPad mini:関係が逆になる
Safari では、WebGL は ANGLE が Metal に変換して動かします。一方、WebGPU は設計が Metal に近く、ほぼ直結です。iPad mini で WebGPU が強く、WebGL の読み戻しが重かった(TF.js の LSTM で 98ms)のは、この構成と整合します。
razr fold の WebGL が速いのは、GPU の性能だけでなく、成熟したドライバの本線を通っていることが大きいと考えられます。裏返せば、「WebGL が速い」というより「WebGPU が相対的にまだ追いついていない」面もあります。どちらの API が有利かは、端末ごとの経路で決まります。
ドライバの回避策が、実測の癖と一致した
chrome://gpu には、Chrome がこの GPU のために入れている「Driver Bug Workarounds(ドライバ不具合の回避策)」も並んでいます。そのうち2つが、前回の計測で見えていた小さな異常と一致しました。
| 回避策 | 意味 | 一致した現象 |
|---|---|---|
disable_program_caching_for_transform_feedback | Qualcomm GPU では、Transform Feedback を使うシェーダーのバイナリをキャッシュしない | モンテカルロの WebGL2(Transform Feedback で計算)は、1,000回だけ 49.8ms。5,000回以降は 4ms 台 |
wake_up_gpu_before_drawing | アイドル状態からの最初の描画が遅いため、描画前に GPU を起こす | ティック描画の WebGL は、計測開始直後の 1,000点だけ 79fps。以降は上限の 91fps |
モンテカルロの WebGL2 は、シェーダーの出力を頂点バッファに書き戻す Transform Feedback で計算しています。Qualcomm ではこの種のシェーダーがキャッシュされないため、最初の1回はコンパイルからやり直しになります。最初の計測だけが突出していたのは、このコストと考えるのが自然です。
どちらも「回避策の内容と、測定値の癖が一致する」という状況証拠です。回避策がその遅さの原因だと、計測で切り分けたわけではありません。ベンチマークの最初の1回は捨てる(ウォームアップする)という定石の根拠のひとつとして読んでください。
7年前のコードで、3バックエンドを走らせる
経路の違いが、実際の機械学習でどう出るか。2019年の記事 tfjs で sin 波の機械学習。スマホのテキストエディタで。 のソースコードをもとに、CPU・WebGL・WebGPU の各バックエンドで同じ LSTM を学習させました。
| バックエンド | Fit | Predict | 最終 loss(train / val) |
|---|---|---|---|
| CPU | 0.101s | 0.062s | 約 0.043 / 0.037 |
| WebGL | 0.170s | 0.234s | 約 0.021 / 0.019 |
| WebGPU | 0.173s | 0.252s | 約 0.018 / 0.015 |
LSTM は、やはり CPU
CPU は Fit で約1.7倍、Predict で約4倍速い結果です。ハイブリッド計算のバックエンド選択 — LSTMはCPU、モンテカルロはGPU の結論は、端末が変わってもそのまま成り立ちました。小さな演算を逐次つなぐ LSTM では、GPU に渡して結果を受け取る往復のコストが、並列化の利益を上回ります。
WebGL が、WebGPU と並んだ
注目したいのは WebGL です。Fit 0.170秒、Predict 0.234秒と、WebGPU とほぼ同じ水準まで来ています。以前の iPad mini と Pixel 10 Pro では、WebGL は目に見えて遅い印象でした。比較用のスクリーンショットが残っていないので数字では示せませんが、TF.js ベンチの LSTM Cell(razr fold 21ms、iPad mini 98ms)と同じ傾向です。
TF.js の WebGL バックエンドは、演算の種類と形ごとに小さなシェーダーを大量に作ります。成熟した GL ドライバの上でそれを回せることが、この差に効いていると考えられます。chrome://gpu では ANGLE のシェーダーキャッシュ(cacheCompiledShader)が有効になっていたので、2回目以降の実行でコンパイルが省かれている可能性もあります。こちらは推測です。
WebGPU で、学習が正しく進んだ
もうひとつの収穫は、WebGPU で loss が 0.40 から 0.018 まで下がり、予測が sin 波に沿ったことです。Pixel 10 Pro の GPU では、WebGPU バックエンドで勾配がゼロになり学習が進まない不具合を報告していました(tfjs issue #8590)。Adreno 829 では、その問題は起きていません。不具合が GPU 依存だったことを示す実機データになりました。
最終 loss は CPU が 0.04 前後、GPU 2方式が 0.02 前後でした。ただ、重みの初期値が実行ごとにランダムなうえ、各1回の実行です。バックエンドによる精度の差とは言えません。比べるなら、初期化に seed を指定するか、複数回の平均を取る必要があります。
Vulkan の限界は、デバイスロストで現れた
WebGPU が Vulkan の上で動いていることは、WebLLM の上限を探ったときにもはっきり表に出ました。前回の記事では 2B まで生成できたので、Qwen2.5-3B(q4f32_1、約1.5GB)に挑戦しました。
ロードは約2時間(7423.7秒)かかって成功。しかし生成を始めた瞬間、PC のリモートデバッグに次のログが出ました。
Dawn が Vulkan に処理を投げた時点で、GPU デバイスが失われています。画面に出ていた Failed to execute 'mapAsync' on 'GPUBuffer' は、その巻き添えで出る二次的なエラーでした。端末を再起動し、ほかのモデルを一切ロードせず、コンテキスト長も 1024 に絞って試しましたが、結果は同じでした。
| モデル | 重み | 結果 |
|---|---|---|
| Qwen3.5-2B | 約 1.0GB | 生成 OK(12.8〜14.7 tok/s) |
| Qwen2.5-3B | 約 1.5GB | ロード OK、生成でデバイスロスト |
q4f32_1 は計算を f32 で行うので、生成中は重みに加えて中間データと KV キャッシュが乗ります。それが GPU プロセスに許されたメモリを超えたと考えられます。chrome://gpu の MAX BUFFER 2048MB は1本のバッファの上限で、合計の上限ではありません。
前回の記事で、Qwen2.5-1.5B が前日だけ生成できず、再起動後に動いた件を書きました。あのときのエラーも同じ mapAsync でした。今回のログから、あれもデバイスロストだった可能性が高くなりました。複数のモデルを続けてロードした後の GPU メモリの圧迫か、GPU の状態異常が原因だったと見るのが自然です。ただし、1.5B 失敗時のログは取れていないので、確定ではありません。
経路を記録し、エラーを分類する
今回の調査から、ベンチマークページに入れておきたい仕組みが2つ見えてきました。
GPU 名と経路を、画面に出す
旧端末の比較用スクリーンショットが残っていなかった反省からです。GPU 名と経路を計測結果と同じ画面に出しておけば、スクリーンショット1枚が「どの GPU の、どの経路での数字か」の記録になります。
const gl = document.createElement('canvas').getContext('webgl2');
const ext = gl.getExtension('WEBGL_debug_renderer_info');
const renderer = ext ? gl.getParameter(ext.UNMASKED_RENDERER_WEBGL) : '(非公開)';
// razr fold の例:
// "ANGLE (Qualcomm, Adreno (TM) 829, OpenGL ES 3.2 V@0842.43.1 ...)"
// → "OpenGL ES" が見えれば GLES 経由、"Vulkan" なら Vulkan 経由
Safari など、この拡張でGPU名を返さないブラウザもあるので、取れない場合の表示も用意しておきます。
「失敗」を、原因別に言い分ける
WebLLM のデモは、どんな失敗でも「この端末には大きすぎる可能性があります」と表示していました。今回見てきたように、失敗には少なくとも3種類あります。
| 種類 | 代表的なエラー文 | 利用者への案内 |
|---|---|---|
| 通信 | Cache.add() encountered a network error | 取得済みの分はキャッシュに残る。再試行で続きから再開 |
| GPU | mapAsync ... Buffer was unmapped / Device was lost | メモリ不足か GPU の一時的な不調。再起動して、このモデルを最初にロード |
| 容量 | QuotaExceededError | ブラウザの保存容量不足。小さいモデルを試す |
通信エラーなら自動で数回再試行し、GPU エラーなら再起動を案内する。3B のような大きなモデルを扱うなら、この切り分けは欠かせません。
速さは、GPU ではなく道で決まる
同じ Adreno 829 の上でも、WebGL は成熟した OpenGL ES の本線を通り、WebGPU は Vulkan の脇道を通っていました。razr fold で WebGL が目立って速く、WebGPU の往復が重かったのは、この経路の違いでかなり説明がつきます。iPad mini ではこの関係が逆になり、WebGPU が Metal 直結で強くなります。
「WebGPU は新しいから速い」「GPU が強いから速い」とは、単純には言えません。どの API が、その端末のどの道を通るのか。chrome://gpu は、それを読むための地図でした。
- razr fold の WebGL は ANGLE → OpenGL ES。Chrome の画面合成と同じ、成熟した本線。
- WebGPU は Dawn → Vulkan。Chrome の中では脇道で、往復の重さと整合する。
- iPad mini は逆で、WebGPU が Metal 直結、WebGL は ANGLE で Metal に変換。
- Qualcomm 向けの回避策2つが、ベンチの「最初の1回だけ遅い」と一致。
- sin 波 LSTM は CPU が最速のまま。WebGL は WebGPU と並び、WebGPU の学習も正常。
- WebLLM は 2B が生成の上限。3B は Vulkan のデバイスロストで停止。