Closed8

Web NN の動向を追う (2021年5月時点)

nissy-devnissy-dev

WebNN support async APIs?
https://github.com/webmachinelearning/webnn/issues/230

グラフ構築(MLGraphBuilder.build)と実行 (MLGraph.compute)はどちらも sync APIとしてデザインされている。 sync APIだとメインスレッドをブロッキングするので、async API もデザインする必要がある?という議論。

MLGraphBuilder.build

ビルドに含まれる操作
→ 計算グラフのコンパイルと重みのアップロードとGPUでの初期化

グラフ構築は実行と比較して計算コストがかからないので同期APIでも良さそうだが、同期 → 非同期のインターフェイスに変更のは大変 & グラフ構築にコストがかかる場合もある (特に GPU での初期化) ので、非同期APIだけに変更する提案がされていた。しかし、WASM バックエンドの都合によって同期APIも必要になりそうとの議論がされていた。

WASM バックエンドの都合

WASM バックエンドでは、C++ で実装されたMLフレームワークをWASMにコンパイルして実行する。既存の多くの C++ のメソッドは同期APIで実装されているため、compute 自体も同期APIになっていることが要求されることが多い。

C++ の同期API を JS の非同期APIに変換ツールとしてAysncify があるが、これを使うとパフォーマンスが悪化するらしい。

https://qiita.com/chikoski/items/ed78aace5452a94a49d5

MLGraph.compute

以下のコメントの表がまとまっている
https://github.com/webmachinelearning/webnn/pull/257#issuecomment-1080120454

どこで計算が行われるかで、同期・非同期どちらのAPIを利用すべきかが変わってくる

通常のケース

CPU/GPU デバイスは自動で決定される。CPUで計算が実行される可能性があるので、同期APIは worker スレッドのみに制限すべきとの議論がされている。もちろん非同期APIは、メイン・Worker どちらのスレッドでも呼び出せるようにしている。

WebGPU interop

メインスレッドはGPU スレッドに計算を実行させるようにキューを積むだけなので、同期 API にしても問題ないはず?という議論が起きていた (自分も同じ認識) 。MLGraph.compute を実行した後の計算結果は、GPUからCPUへのデータの取り出しになり GPUBuffer.mapAsync で非同期でできる。

nissy-devnissy-dev

同期・非同期 API の議論についてはほとんど落ち着いたみたい (参考) なので、W3C のドキュメントを読むのが良さそう

https://www.w3.org/TR/webnn/#programming-model

まずは各インターフェイス / API の紹介

  • MLOperand
    • 計算グラフ内のデータ表現 (数値演算を表現したデータ)
  • MLGraph
    • コンパイルされた計算グラフ
  • MLGraphBuilder
    • build, buildAsync: MLOperandを受け取って MLGraph を作成するための builder 関数
      • build: Worker スレッド上で直ちにグラフをコンパイルして,MLGraphを返す
        • メインスレッド上では呼べないことに注意
      • buildAsync: MLOperandを受け取って MLGraph を作成するための builder (factory) 関数
        • 呼び出したスレッドをブロックせずに非同期でグラフをコンパイル
    • softmax, batchNormalization, ...: それぞれの演算に対応したMLOperandを作成する関数
      • input として MLOperand を受け取り、APIの演算を適応した新しい MLOperand を返す
  • MLContext
    • compute, computeAync: MLGraphと入力・出力値を受け取って計算を行う
      • compute: Worker スレッド上でグラフの実行を直ちに行う
        • メインスレッドでは呼べない
      • computeAync: 非同期でグラフの実行を行う
        • CPU: worker スレッドを利用
        • GPU: GPUタイムラインを利用
  • MLCommandEncoder
    • WebGPUを利用するユーザに対して、柔軟にグラフ実行をできるようにしたインターフェイス
      • MLContext.computeAsyncより細かい単位でグラフの実行に関連するAPIが利用できる
      • グラフ初期化、GPUタイムラインへの演算実行の dispatch、など
nissy-devnissy-dev

Device Selection
https://www.w3.org/TR/webnn/#programming-model-device-selection

MLContext は、グラフ実行に必要なさまざまな状態を保持している。

  • context type
    • "default": User Agent (ブラウザ)が自動的に最適な MLContext を作成する
      • MLPowerPreference と MLDeviceType から 各ハードフェアAPI で実行デバイスを最適化する
    • "webgl": WebGLRenderingContext から MLContext を作成する
    • "webgpu": GPUDevice から MLContext を作成する
  • device type
    • 使用するデバイス ("cpu" or "gpu") を指定する
  • power preference
    • 消費電力に応じた最適化方針を指定する。
    • "default": User Agent (ブラウザ)が自動的に最適化方針を決定する
    • "high-performance": 消費電力より計算速度を優先する
    • "low-power": 計算速度より消費電力を優先する

メモ (自信がない部分)

WebGPU/WebGL は、ブラウザで標準化されたAPIなので個別で context type を指定する。恐らく、WebGPU 内で適切な GPU デバイスが選択される。context type = "default" の場合は、各OSでのNN APIを利用した最適なデバイス選択がされる。WASMはCPUのみで実行される (恐らく) ので、デバイスは常に CPU で選択される。

WebGPU 自体は、DirectX (Win) / Metal (MacOS) / Vulkan (Linux) といった Low Level Graphics API を利用している。一方で、Windows の Direct MLや iOS の ML Compute API のCore NN も、それぞれ内部ではDirectX や Metal を利用する。なので context type の "default" と "webgpu" の違いは、WebNN API → OS specific ML API か WebNN API → WebGPU → OS specific ML API の違いだと理解している。 要は、WebGPU を経由するかどうかが違う。WebGPUを学んでから戻ってくる

このスクラップは2022/08/01にクローズされました