モバイル&ワイヤレスブロードバンドでインターネットへ

gwaw.jp
 
GWAW.JP / REAL DEVICE / GPU PATH
実機検証・経路解析編

なぜ WebGL が速いのか
— chrome://gpu で経路を読む

razr fold に乗り換えてから、WebGL の速さが目につくようになりました。GPU の性能差だけでは説明しきれません。chrome://gpu の出力を読むと、WebGL と WebGPU がこの端末でまったく別の道を通っていることが分かりました。実測とあわせて整理します。

razr foldAdreno 829ANGLEOpenGL ESDawn / VulkanTensorFlow.js

[ § 0 ] 導入

WebGL だけが、妙に速い

前回の 2台を1台に — razr fold でベンチマークを測り直す では、4本のベンチマークを測り直し、「GPU の連続処理は強い、GPU との往復は遅い」とまとめました。

ただ、数字を並べ直すと、もうひとつ気になる傾向があります。WebGL が、以前の2台より目立って速いのです。

WebGL が目立った場面
ベンチマークrazr foldiPad miniPixel 10 Pro
TF.js LSTM Cell(WebGL)21.1ms98ms—
ティック描画 100万点(WebGL)90fps49fps121fps
モンテカルロ 5万回(WebGL2)17.6ms25ms—

一方で、同じ GPU を使うはずの WebGPU は、計算の往復が絡むと iPad mini に届きません。同じ Adreno の上で、WebGL と WebGPU の評価がここまで分かれる。その理由を、ブラウザ自身の申告から探ります。

[ § 1 ] chrome://gpu

ブラウザに、経路を申告させる

Chrome のアドレスバーに chrome://gpu と入れると、GPU の種類、ドライバ、各 API がどの経路で動いているかが一覧で出てきます。razr fold(Chrome 155 / Android 16)の要点はこうでした。

chrome://gpu の要点(razr fold)
項目値
GPUAdreno (TM) 829
ドライバOpenGL ES 3.2 V@0842.43.1(2026年5月27日付)
WebGL の経路gl=egl-angle, angle=opengles
ANGLE → OpenGL ES
WebGPU の経路Dawn → Vulkan(既定のアダプタ)
別に OpenGL ES の互換モードアダプタもある
Chrome の画面合成Skia Backend: GaneshGL(GL)
Vulkan: Disabled

ここから、この端末の Chrome の構造が読み取れます。WebGL と Chrome 自身の画面描画は、どちらも OpenGL ES の上で動いています。WebGPU だけが、Vulkan という別の道を通っています。

[ § 2 ] 経路の違い

WebGL は本線、WebGPU は脇道

WEBGL / WEBGPU の実装経路 — 3台
API 変換層 ネイティブ API GPU razr fold WebGL ANGLE OpenGL ES(本線) WebGPU Dawn Vulkan(脇道) Adreno 829 Qualcomm iPad mini(Safari) WebGL ANGLE(変換) WebGPU ほぼ直結 Metal Apple A17 Pro Apple GPU Pixel 10 Pro(Imagination 系 GPU)は chrome://gpu の記録が残っておらず、経路は未確認。
スマホでは図を横にスクロールできます。razr fold は chrome://gpu の実測値。iPad mini は Safari の一般的な構成(WebGL は ANGLE 経由で Metal、WebGPU は Metal 直結)に基づく図です。

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)のは、この構成と整合します。

「WebGL が速い」の正体

razr fold の WebGL が速いのは、GPU の性能だけでなく、成熟したドライバの本線を通っていることが大きいと考えられます。裏返せば、「WebGL が速い」というより「WebGPU が相対的にまだ追いついていない」面もあります。どちらの API が有利かは、端末ごとの経路で決まります。

[ § 3 ] 回避策と実測

ドライバの回避策が、実測の癖と一致した

chrome://gpu には、Chrome がこの GPU のために入れている「Driver Bug Workarounds(ドライバ不具合の回避策)」も並んでいます。そのうち2つが、前回の計測で見えていた小さな異常と一致しました。

回避策と、ベンチマークで見えた現象
回避策意味一致した現象
disable_program_caching_for_transform_feedbackQualcomm 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回は捨てる(ウォームアップする)という定石の根拠のひとつとして読んでください。

[ § 4 ] sin 波の LSTM

7年前のコードで、3バックエンドを走らせる

経路の違いが、実際の機械学習でどう出るか。2019年の記事 tfjs で sin 波の機械学習。スマホのテキストエディタで。 のソースコードをもとに、CPU・WebGL・WebGPU の各バックエンドで同じ LSTM を学習させました。

sin 波 LSTM — 10エポック目の Fit / Predict 時間(秒)
各バックエンド1回ずつの実行。Fit は10エポック目の学習時間、Predict は予測時間。
razr fold での結果
バックエンドFitPredict最終 loss(train / val)
CPU0.101s0.062s約 0.043 / 0.037
WebGL0.170s0.234s約 0.021 / 0.019
WebGPU0.173s0.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 の差は、精度差ではない

最終 loss は CPU が 0.04 前後、GPU 2方式が 0.02 前後でした。ただ、重みの初期値が実行ごとにランダムなうえ、各1回の実行です。バックエンドによる精度の差とは言えません。比べるなら、初期化に seed を指定するか、複数回の平均を取る必要があります。

[ § 5 ] WebLLM 3B

Vulkan の限界は、デバイスロストで現れた

WebGPU が Vulkan の上で動いていることは、WebLLM の上限を探ったときにもはっきり表に出ました。前回の記事では 2B まで生成できたので、Qwen2.5-3B(q4f32_1、約1.5GB)に挑戦しました。

ロードは約2時間(7423.7秒)かかって成功。しかし生成を始めた瞬間、PC のリモートデバッグに次のログが出ました。

vkQueueSubmit failed with VK_ERROR_DEVICE_LOST Device was lost. This can happen due to insufficient memory or other GPU constraints.

Dawn が Vulkan に処理を投げた時点で、GPU デバイスが失われています。画面に出ていた Failed to execute 'mapAsync' on 'GPUBuffer' は、その巻き添えで出る二次的なエラーでした。端末を再起動し、ほかのモデルを一切ロードせず、コンテキスト長も 1024 に絞って試しましたが、結果は同じでした。

WebLLM の生成上限(q4f32_1)
モデル重み結果
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本のバッファの上限で、合計の上限ではありません。

1.5B の「再現しない失敗」とのつながり

前回の記事で、Qwen2.5-1.5B が前日だけ生成できず、再起動後に動いた件を書きました。あのときのエラーも同じ mapAsync でした。今回のログから、あれもデバイスロストだった可能性が高くなりました。複数のモデルを続けてロードした後の GPU メモリの圧迫か、GPU の状態異常が原因だったと見るのが自然です。ただし、1.5B 失敗時のログは取れていないので、確定ではありません。

[ § 6 ] 実装への反映

経路を記録し、エラーを分類する

今回の調査から、ベンチマークページに入れておきたい仕組みが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取得済みの分はキャッシュに残る。再試行で続きから再開
GPUmapAsync ... Buffer was unmapped / Device was lostメモリ不足か GPU の一時的な不調。再起動して、このモデルを最初にロード
容量QuotaExceededErrorブラウザの保存容量不足。小さいモデルを試す

通信エラーなら自動で数回再試行し、GPU エラーなら再起動を案内する。3B のような大きなモデルを扱うなら、この切り分けは欠かせません。

[ § 7 ] まとめ

速さは、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 のデバイスロストで停止。

関連記事

gwaw.jp — WebGPU / WGSL / TensorFlow.js experiments
// razr fold(Adreno 829 / Chrome 155 / Android 16)は2026年10月計測。iPad mini A17 Pro / Pixel 10 Pro の値は各記事公開時点のもの。経路の解釈は chrome://gpu の出力と実測からの推定を含みます。

『なぜ WebGL が速いのか — chrome://gpu で razr fold の実装経路を読む』を公開しました。