AI PCのAI性能不足:CPUがNPUを上回る
(github.com/usefulsensors)- Microsoft Surface Pro 11th EditionのQualcomm ArmベースSoCでNPUベンチマークを実行した結果、Windows AI PCが掲げるアクセラレーション性能を大きく下回るスループットが測定された
- テストは、Onnx RuntimeとQualcomm QNN実行プロバイダーを使い、transformer系モデルのコストの大きいレイヤーに似た大規模MatMul演算を実行する形で構成された
- 例の結果では、CPUは821 Gigaops/s、NPUはfloat入出力の量子化構成で225 Gigaops/s、8-bit入出力まで適用した構成で573 Gigaops/sを記録した
- NPUの573 Gigaops/sは、Microsoft Surface Pro 11th Editionのマーケティング資料にある45 Teraops/sの1.3%程度であり、同じモデルをNvidia Geforce RTX 4080 Laptop GPUで実行すると3.2ms、2,160 Gigaops/sとなる
- 現在の結果は2024年10月2日時点のもので、ソフトウェア・フレームワーク・ドライバーの改善によりレイテンシが下がる可能性は期待されるが、現行ベンチマークではCPUがNPUより高速
SurfaceのQualcomm NPUベンチマークの目的
- MicrosoftはQualcomm ArmベースSoCを搭載したSurfaceタブレットをWindows AI PCとして販売し、機械学習モデルをより高速かつ効率的に実行できると宣伝している
- Useful SensorsはQualcommのハードウェアとNPUを前向きに評価し、自社のサードパーティーアプリをこのプラットフォームへ移植するために時間とリソースを投入した
- 外部開発者が素早く結果を得る方法を示すコード例やベンチマークが多くないため、実際に観測した性能を小さな独立プロジェクトとして公開した
- 測定性能は期待を大きく下回ったが、Androidのような他プラットフォームでは同じハードウェアが有効に動作した経験があるため、今後のアプリケーション・フレームワーク・ドライバー変更による改善可能性を残している
実行環境とインストール上の制約
- テストにはWindows上でPythonスクリプトを使用した
- 2024年10月2日時点でMicrosoft StoreのPythonはArmアーキテクチャをサポートしておらず、Qualcomm NPUへのアクセスに必要なパッケージの実行には適していない
- 結果にはPython 3.11.9 Arm64インストーラーが使用された
- Windows on Arm向けの事前ビルド済みOnnxパッケージがまだないため、CMakeとVisual Studioコンパイラーが必要
- CMakeは
winget install cmakeでインストールする - Visual StudioはVisual Studio Community Editionを使用し、インストール時に
Desktop C++ Developmentワークロードを選択する
- CMakeは
- Pythonパッケージはリポジトリフォルダーで
py -m pip install -r requirements.txtによりインストールする- Onnxブランチは、公式
pyランチャーのコンパイル修正がOnnx 1.16にバックポートされたバージョン - Qualcomm Onnx Runtimeは最新Onnxで
Unsupported model IR versionエラーが出るため、この組み合わせを使用する - Qualcomm Onnx Runtimeパッケージはnightlyビルドが使用された
- Onnxブランチは、公式
ベンチマーク実行と出力の読み方
- ベンチマークは
py benchmark_matmul.pyで実行する - Onnx Runtimeは初回実行中に多くのログを出力する
- 例として、
Snapdragon(R) X 12-core X1E80100 @ 3.40 GHzチップモデルを認識できないというcpuinfoメッセージが出る - グラフの最終処理段階と完了段階のログも出力される
- 例として、
- 実際のベンチマーク結果は最後に表示される
NPU quantized compute, float I/O accuracy difference is 0.0100NPU quantized compute and I/O accuracy difference is 0.0060CPU took 8.42ms, 821,141,860,688 ops per secondNPU (quantized compute, float I/O) took 30.63ms, 225,667,671,183 ops per secondNPU (quantized compute and I/O) took 12.05ms, 573,475,650,364 ops per second
- 最初の2行は、CPUとNPUの数値結果が互いに一致しているかを確認する精度差を示す
- 最後の3行は、モデルを最初から最後まで実行する実時間と、そのレイテンシから計算した秒間演算数を示す
測定対象モデルと量子化方式
- ベンチマークは、OpenAI Whisperのようなtransformerモデルで時間がかかるレイヤーに似た6つの大きな行列乗算を再現するよう設計されている
- 入力形状は
(6, 1500, 256) X (6, 256, 1500) - 結果形状は
(6, 1500, 1500) - モデルは2つの入力と1つの出力を持つ単一の
MatMulノードで構成される
- 入力形状は
- モデルはOnnxモデルフレームワークでその場で生成した後、Onnx Runtimeに渡す
- 基準モデルは純粋なfloat版で、CPUでのみ実行される
- NPUで効率的に実行するには、主に量子化モデルが必要で、float16のサポートは限定的
- 1つ目のNPUアプローチでは、公式ORTの
quantize_static()メソッドを使用する- 利便性のため、入力テンソルと出力テンソルは32-bit floatのままにする
- グラフの開始部と終了部でランタイム変換を行い、残りの計算は8-bitで実行する
- この構成ではNPUの変換演算が非常に遅く、
npu_quant_profile.csvでは変換が全体時間の75%以上を占めた - 2つ目のアプローチでは、8-bit入力と出力を持つ同等のモデルグラフをプログラムで構成する
- この
quantized compute and I/O方式は、float I/O版より通常約3倍高速 - プロファイリングでは、時間の大半が期待どおり行列乗算に使われている
- この
性能測定で考慮した変数
- Compute boundかどうかを考慮し、行列の形状をより正方形に近づけた
- 現代のtransformerモデルは、古いconvolutionモデルと異なり、大きな行列乗算に基づいている
- レイヤーが行列ベクトル積に近づくと重みの再利用が減り、DRAMからの値の取得がボトルネックになり得る
- tiny Whisperの元の行列の
k次元は64だったが、このベンチマークではSIMD最適化の余地を広げるため256に上げた
- 電源設定は性能を高める方向に合わせた
- Windowsのエネルギー設定はすべて
Best Performanceにする意図だった - タブレットを電源に接続した状態でベンチマークを実行した
- Qualcomm Onnx Runtimeの
htp_performance_modeセッションオプションは、実験で全体レイテンシが最も低かったsustained_high_performanceに設定した
- Windowsのエネルギー設定はすべて
- モデル構造は解釈しやすいよう単一の行列乗算に限定した
- 複数レイヤー、convolution、静的重みも可能だったが、LLMと現代的モデルで広く使われるtransformer構造を反映するため、動的入力を持つ単一のMatMulを選択した
- 構成ミスの可能性も残っている
- unsigned 8-bit量子化とグラフ内の
qdq要素を使用している - ドキュメントのベストプラクティスに従おうとしたが、ドライバーやアクセラレーター実装の高速パスから外れた可能性がある
- unsigned 8-bit量子化とグラフ内の
- WindowsでAIアクセラレーションにアクセスするAPIの選択肢も検討された
- DirectMLはGPUアクセスのみをサポートしているように見える
- OpenVinoは該当するArmハードウェアでは動作しないように見える
- Qualcomm QNN SDKを直接使っても似た性能結果が見られた
- TensorFlow LiteはWindows for Armをサポートしていない
- この調査と実験では、OnnxがMicrosoftとQualcommの双方でサポートされており、NPUアクセラレーション性能を得るうえで最も適したフレームワークに見える
結果の解釈
- 結果は2024年10月2日時点のもので、Microsoft Surface Pro 11th Editionで測定された
- SoCは
Snapdragon(R) X 12-core X1E80100 @ 3.40 GHz
- SoCは
- float変換を除いてもNPUの結果はCPUより遅い
- アクセラレーターの観点では理想的ではない
- ただし、エネルギー効率や持続性能で利点がある可能性は排除しない
- 測定された最高NPU性能である573 billion ops/sは、Microsoft Surface Pro 11th Editionのマーケティング資料の45 trillion ops/sに対して1.3%
- 同じモデルをNvidia Geforce RTX 4080 Laptop GPUで実行すると3.2msかかる
- これは2,160 billion ops/sに相当する
- SurfaceのNPU測定値よりほぼ4倍高いスループット
2件のコメント
RyzenのNPUはCPUより少し速いくらいだと思っていましたが、Snapdragonへの物欲が一気にしぼみますね
Hacker Newsの意見
結果を見る限り、全体として 計算資源の活用 があまりうまくできていないように思える。CPUが8.4ms、GPUが3.2msでは差が小さすぎ、このケースなら10〜20倍の差を期待していてもおかしくない
原因はonnxruntimeかもしれない。一部のハードウェアベンダーは演算ユニットだけを出して、十分なサポートはまだ後追いになっているようにも見え、どれだけ早く改善されるかは見守る必要がある
また、NPUの目的を「速度」だと誤解しているケースも多いが、肝は 低消費電力 にある。速度を狙うならメモリボトルネックをなくす必要があり、結局は専用メモリを持つASICを設計することになる。大半のデバイスのNPUは、CPU周辺のSoCに載ってAI計算をオフロードする用途だ
CPU/NPU/GPUの3デバイスでこのベンチマークを無限ループで回し、消費電力を測ってみると面白そうだ。NPUが最も低く、ワット当たり性能も最も良いと予想する
例えばAI写真フィルターでは、GPUがプレビュー描画を担当し、CPUはUIやユーザー入力の処理で忙しい可能性が高い
Apple Accelerateは、M4以前のCPU行列ユニットとNPUにアクセスする事実上唯一の経路だった。それ以外を使いたいなら、選んだ機械学習フレームワークにパッチを送る覚悟をするか、学習と推論のコードを自分で書く覚悟をする必要がある
結局PythonもCを呼んではいるが、どれほど性能が失われるのかはかなり興味深い
GPUを使うなら、タイミングの中に 非同期処理 が含まれているかを考慮する必要がある
time.time()を素朴に使うと、CPUが時間を記録するだけで、model(input.cuda()).cuda()はデータをGPUメモリに送り、計算を開始するが非同期なので、結果が実際に準備できているかに関係なく終了時刻が記録されることがあるシステムとハードウェアを知らなければ期待しにくい挙動だ。Pythonに限らず大半の言語は、書いたコードより最適化された形にコンパイルされるよう設計されており、ロックがないのでCPU処理を止めない
GPU処理を実際に測るにはCUDAイベントタイマーを見るべきだ。PyTorchでは
torch.cuda.Event(enable_timing=True)を使う方法になるさらにメモリのサイズと形状も複雑だ。このベンチマークはNPUに不利な形を使っている。NPUとGPUは通常channels lastを望むので、
[1,6,1500,1500]より[1,1500,1500,6]の方が適している1500と6という数字も中途半端でNPU向きではなく、こうしたデバイスがまだ新しいことを考えると、性能低下はかなり大きい可能性がある
詳しくは https://news.ycombinator.com/item?id=41864828 に書いてある
こうしたNPUはかなりの シリコン面積 を占めているので、結局あまり使われないのだとしたら本当に惜しい。Snapdragon XでNPUだけを切り出したダイ解析は見つけられなかったが、同様に約50 TOPSを狙ったAMD側の例はここで見られ、高性能CPUコア3個分ほどの面積を占めている
https://www.techpowerup.com/325035/amd-strix-point-silicon-p...
さらに悪いことに、大半の人にも必要ないように見え、最近の調査ではAI浸透への反感が優勢に出ることもある。こうしたものに追加費用を払うべきではなく、オプションであるべきだ
そうすれば、「AI」プレミアムを払いたがる人がどれだけ少ないかが販売数で明らかになり、これがどれほど誇張され不要なものかがはっきりするだろう
だからこうした部品の目的は、使われることではあるが、使われすぎないことでもある
NPUの代わりにそのトランジスタとダイ面積を別用途に回すことはできたかもしれないが、高性能CPUコアを増やすことはしなかったはずだ。そうすると電力密度が高くなりすぎ、恒常的なスロットリングなしでは解決しにくい熱問題が生じる
[1] https://en.wikipedia.org/wiki/Dark_silicon
モデルを動かさない人にとっては無駄に見えるし、ほかの用途に使える方法があるのか気になる
コンシューマー向けソフトウェアはそこまでスケールしないので、トランジスタをCPUにさらに割り当てて何ができるのかという気もする
AppleがSoCにビデオエンジンを多く載せる理由とも似ている。賄えるトランジスタ予算の範囲では、ほかに使い道があまりない。シングルスレッド性能の改善はもはやトランジスタ数だけの問題ではなく、ソフトウェアはマルチスレッド化があまり得意ではない
こうした装置の目的は高速化ではなく、小さなモデルをきわめて低消費電力で動かすことだと思っていた。NPUを搭載した最新のAMDノートPCを使っているが、NPUで動くという映像効果を有効にしても消費電力は変わらない一方、Nvidia Studio Effectsを使うと消費電力は増える
NPUは、視線補正、背景ぼかし、自動補正モデル、文字起こし、OCRのような小さな処理を行う、非常に最適化されたモデル向けに見える。特にWindowsでは、rewind機能のために全画面OCRと検索用の埋め込みを回しているのだと思う
AMDは最近すばらしい仕事をしているのに、それをあまり大きく宣伝していないように見える。これは特に興味深い: https://lore.kernel.org/lkml/DM6PR12MB3993D5ECA50B27682AEBE1...
修正: FPGAではなかった。今日学んだ
macOSでCoreMLモデルを評価してみると確認できる。ANEはGPUの半分程度の時間で済み、GPUはCPUの半分程度の時間で済む。実際の倍率はモデルによって変わる
Nvidiaを脅かすチップは、スマートフォンのような個人向けデバイスで十分に使えるモデルを動かせるほど安価なチップとメモリだろう
一般大衆がLLMの有用性に同意し、デバイス価格に多少のプレミアムを払う意思があるなら、この技術の未来は本質的にプライバシー保護を備えた個人モデルだと思う
人々がChatGPTのような場所に注ぎ込んでいる個人情報の量は驚くべきものだ。Redditを見ると、AIバーチャル彼女アプリの中毒者たちは、最も暗い嗜好、弱さをさらけ出した告白、さらには犯罪になり得る会話まで、名もないアプリ企業に渡していることが多いように見える
GoogleもGeminiの履歴を有効にすると会話内容をレビューできると明記している
より大きなモデルが必要な複雑なトークン予測はクラウドLLMに尋ねればよいとしても、消費者にはプライバシー保護が必ず保証されるべきだ
日常的な個人アシスタント、チャット、情報探索に、最先端の推論や曲芸的なLLMが必要だとは思わない
ただし、記事の具体的な結論には少し疑いがある。QualcommのONNXの話で、古い可能性もある。Android界隈ではQualcommのソフトウェアエンジニアリングはかなり酷評されがちだった
それでも方向性は合っている。消費者向けハードウェアのAIアクセラレーションの主張の大半は、ほぼ例外なく誇張に近く、例外は A) 1Pソフトウェアを使う場合か、B) 1P内部の誰かが本気でその機能を使ってほしいと思っている場合だ
TensorRTのようなものを使っても、最初から直接書くほど速くはならないだろうし、Nvidiaが多くの人員を投入しているのにも理由がある。それでもかなり近いところまでは行けるし、実装時間も大きく短縮できる
こうした装置はたいてい、繰り返される似たような処理に最適化されている。だから、ここで集められた情報の一部は不正確かもしれないと思う
これらのNPUチップを直接使ったことはないが、タイミングは信頼しにくい。最後の方のCUDAタイミングは、コード上で正しく測定されていない可能性が高い。タイミング測定は思ったより簡単ではない
広告されている演算数はNPUで直接実行された演算だけを数えている一方で、元記事はNPUとGPUの測定にCPU処理まで含めていた可能性がある。ドキュメントにはベンチマークツールがあるので、似たような方法を使ったのだろうし、ウォームアップ後の分散がどうなるのかも気になる
データ形式も誤っているようだ。ここでは
channels lastが必要だ。ドキュメントもそれを確認している1500という数値も不自然で、追加のミスが入り込んでいるかもしれない。1536、2048、256、あるいはもっと小さい値では結果が変わる可能性がある。実際のモデルはフル解像度の画像を処理せず、アーキテクチャをモデル向けに最適化するなら形状情報が重要になる。機械学習では形状最適化がかなり重要だ
ドキュメントをざっと見る限り、設定も不適切に見える。“Model Workflow”ではデータを8ビットまたは16ビット浮動小数点にしてほしいとしているが、浮動小数点にもいろいろある。PyTorchのbfloatは
torch.halfやtorch.float16と同じではない混合精度はいまでも混乱しやすいテーマなので、こうした問題があるならきちんと確認する価値がある。標準の量子化手順だけ回して終わりにするのは勧めない。出発点としてはよいが、「十分に良い」のでなければそこで止まるべきではない
それでも、この結果が無意味だとは思わない。ただ改善が必要だ。こうしたことは思った以上に複雑で、そのかなりの部分は技術が新しく、細部がまだ整理されている最中だからだ
CPUやGPU、特にCUDAと比べると、そこには数十万時間の工数が投じられており、Pythonのような高水準ライブラリにも少なくとも数万時間の工数が入っていることを忘れてはいけない。これらの装置は、平均的なユーザーが好む言語抽象化レベルでそのまま使うにはまだ準備不足だが、ハードウェアに近いところで作業する意思があるならかなり有用だ
PyTorchでGPUの非同期処理を測るなら、CPUタイマーでモデル出力を囲むのではなく、CUDAイベントと
torch.cuda.synchronize()を使うべきだ[1] https://www.thonking.ai/p/what-shapes-do-matrix-multiplicati...
NPU にモデルをデプロイするには、プロファイルベースの最適化がかなり必要。CPU でうまく動くモデルを NPU 向けに最適化せずに持ち込むと、たいてい期待外れな結果になる
GitHub リポジトリの説明のほうがブログよりはるかに有益
onnx で int8 行列積を回すと性能は約 0.6TF
https://github.com/usefulsensors/qc_npu_benchmark
入力行列をより正方形に近づけてタイル化と再利用を可能にしたとのことだが、可能な最適化のかなりの部分が Onnx に入っていなくても驚かない
Qualcomm は NPU に直接アクセスさせず、ユーザーがフレームワークでモデルを変換して渡すことを想定しているように見える。私の経験では変換ツールはたいてい出来が悪く、多くの最適化を取りこぼす
したがって「NPU がよくない」というより「変換ツールがよくない」のかもしれない。直接アクセスできるようになるまで待つつもりだし、変換ツールは信用していない
NPU はとても小さな機械学習モデルと非常に高速な関数近似に向いていると思う。それが私の意図している用途だ。LLM が最近話題なのは確かだが、小さなモデルが本当に役立つ専門的な仕事は非常に多い
そういうものがないと言いたいわけではないが、正直それが何なのかよくわからないので知りたい
Elite X の NPU は Copilot+ のおかげでアクセスしやすくなっていてほしいが、要点は「汎用モデルを実行すれば魔法のように NPU に瞬間移動するだろう」みたいに簡単なはずがない、ということだ
RTX 4080 は約 40 TFLOPS 出るはずなのに、ここでは毎秒 2,160 億演算しか報告されていない。この程度ならベンチマークを見直すべきではないかと思う
FLOPS 測定に重大な誤りがあった可能性が高い。CPU が NPU に勝つこと自体はあり得るが、適切に比較するにはアプリケーション同期なしで複数の行列積をベンチマークする必要がある
ベンチマークは
(6, 1500, 256) X (6, 256, 1500)形の行列積だが、AI の世界ではそれほど大きなサイズではない。もっと大きな行列なら差はさらに広がるはずだ例えば小さなモデルの一つである Llama 3.1 8B でも
(batch, 14336, 4096) x (batch, 4096, 14336)のような行列積を持つこのベンチマークは十分に現実的ではないと思う
このベンチマークで Qualcomm の NPU プロファイラ qprof を回してみた。プロファイル結果を見ると、処理は NPU の演算能力の大半を担うテンザーコアではなくベクターコアに割り当てられていた
概算すると HMX は HVX より 30 倍強力に見える
ワークロードが比較的小さいため、入出力量子化/逆量子化と NCHW-NHCW マッピングのオーバーヘッドのせいでハードウェア能力を十分に使えていない。重みと入力を 64 の倍数にパディングするのも性能改善に役立つはずだ
プロファイリンググラフ: https://imgur.com/a/2OKR93e
推定 HVX 演算性能は int8 で
4 * 2 * 1.43 * 1024 / 8 = 1.46TOPS。ここで 4 はベクターコア数、2 はサイクル当たりの演算数、1.43GHz は HVX 周波数、1024bit はベクターレジスタ幅、8bit は精度を表す4 * 2 * 1.43 * 1024 / 8であるべき実際のタイトルは「Benchmarking Qualcomm's NPU on the Microsoft Surface Tablet」であるべき
これは NPU 一般についての記事ではなく、特定の NPU を特定のベンチマークと特定のライブラリ/フレームワークの組み合わせで見たものだ。だから実質的には何も証明していない