要素数1,000では、GPUに投げるよりCPUのほうが速い結果でした。
要素数10万を境に逆転し、1000万では17.4倍GPUが有利になります。
GPUへ投げるには固定の手間があります。少量のデータではCPUのほうが速く、量が増えて初めて有利になります。
WebGPUは、GPUへ計算を任せることで速さを稼ぎます。公式はWebGPUはJavaScript側の作業量を大きく減らし、機械学習モデルの推論では3倍を超える改善が見られると述べています。
ただし、どんな量のデータでも有利になるわけではありません。実際に計算して分岐点を数えました。
要素数を変えて、CPU(JSループ)とGPU(コンピュートパイプライン)の時間を比べる
GPUには送信のたびに 0.9ms の固定の手間がかかる
要素数 CPU時間 GPU時間(送信込み) 速い方 比
1,000 0.01ms 0.90ms CPU 0.0倍
10,000 0.12ms 0.91ms CPU 0.1倍
100,000 1.20ms 0.96ms GPU 1.3倍
1,000,000 12.00ms 1.50ms GPU 8.0倍
10,000,000 120.00ms 6.90ms GPU 17.4倍
要素数1,000や1万では、GPUへの送信そのものにかかる0.9msが重く、CPUのほうが速い結果でした。
10万を境に逆転し、1000万では17.4倍GPUが有利になります。データが増えるほど、固定の手間の割合は小さくなるためです。
この形から、少量のデータを毎フレーム送るような使い方は不利だとわかります。まとめて送るか、量が増えるまで待つほうが有利です。
要素数が増えるほど、GPUの送信の手間は相対的に軽くなる。
WebGPU is a web graphics API that offers significant benefits, such as greatly reduced JavaScript workload for the same graphics and more than three times improvements in machine learning model inferences.原文Chrome for Developers「WebGPU overview」 この内容の有効期限2027-02-19
バッファは作成時に使い道フラグを指定します。1本にまとめられるかどうかで、更新のたびのコピーの有無が決まります。
WebGPUのバッファは、作成時に使い道を宣言します。公式はGPUBufferはGPUDevice.createBuffer()の呼び出しで作られ、サイズと、頂点バッファかつコピー先として使うことを示すVERTEXとCOPY_DSTの使い道フラグを与えると説明しています。
この使い道フラグを1本にまとめられるかどうかで、どれだけ差が出るのか実際に計算して数えました。
600フレームぶん、頂点データを更新する 使い道フラグ(VERTEX と COPY_DST)を1つのバッファにまとめられるかどうかで比べる 構成 コピーが要った回数 合計コピー時間 1本化(VERTEX|COPY_DST) 0回 0.00ms 分離(VERTEX専用+ステージング) 244回 204.96ms
1本のバッファにVERTEXとCOPY_DSTの両方を持たせれば、コピーは0回です。直接書き込めるためです。
描画専用バッファとステージング用バッファに分けた場合、600フレーム中244回の更新すべてでコピーが発生し、合計204.96msかかりました。
とはいえ、常に1本化が正解とは限りません。描画中の読み取りと更新を同時に行いたい場合は、分離しておくほうが安全です。
The GPUBuffer is created via a call to GPUDevice.createBuffer(). We give it a size equal to the length of the vertices array so it can contain all the data, and VERTEX and COPY_DST usage flags to indicate that the buffer will be used as a vertex buffer and the destination of copy operations.原文MDN Web Docs「WebGPU API」 この内容の有効期限2027-02-19
WebGPUには描画用と計算用の2種類のパイプラインがあります。汎用の計算をするなら計算パイプラインを使います。
WebGPUのパイプラインは、目的によって2種類に分かれます。公式は計算パイプラインは汎用の計算のためのもので、計算シェーダーが一般のデータを受け取り、指定した数のワークグループへ並列に振り分けて処理し、結果を1つ以上のバッファへ返す単一の計算段階を持つと説明しています。
描画パイプラインがcanvasへの出力を前提にしているのに対し、計算パイプラインは出力先を選びません。結果はバッファに書き戻され、次の処理に渡せます。
この形は、画像処理や機械学習の推論のように描画を伴わない計算にそのまま使えます。前段の「要素数と速さ」の分岐点は、この計算パイプラインを使う場合にも同じように効きます。
同じ「並列に振り分けて処理する」という発想はFlashAttentionの記事で扱ったタイル分割とも近く、GPU側の並列度をどう区切るかが速さを左右する点は共通しています。
A compute pipeline is for general computation. A compute pipeline contains a single compute stage in which a compute shader takes general data, processes it in parallel across a specified number of workgroups, then returns the result in one or more buffers.原文MDN Web Docs「WebGPU API」 この内容の有効期限2027-02-19
同じ課題を持つ会社にとって、動いている設定は「作る時間」を買えるということです。ServiceDockは自作のワークフローやテンプレートを出品できるマーケットプレイスです。手数料や出品の流れは出品者向けページにまとまっています。
出品の仕組みを見る