本記事にはアフィリエイト広告(Amazon アソシエイト)が含まれます。
実測日: Sun Apr 05 2026 09:00:00 GMT+0900 (Japan Standard Time)
Qualcomm の NPU(Hexagon Tensor Processor、以下 HTP)で 9B 級の LLM を動かすと、どの程度の速さが出るのか。 カタログの TOPS からは判らないので、Radxa Fogwise AirBox Q900 に Qwen3.5-9B を載せて測った。 本記事は、固定長の入力を 1 回だけ流す「一発実行」の結果と、その数字の読み方を記録する。
変換の経路
Qualcomm の SDK には LLM 専用の変換導線があるが、今回は ONNX を経由する一般の経路を使った。 Qwen3.5 は線形 attention(Gated DeltaNet)と通常の attention が混在する構造で、専用導線がそのまま通る保証がなかったからである。
ONNX から QNN の実行形式(DLC)へ変換する過程で、通常の attention 側の RoPE に含まれる rotate_half が問題になった。
この計算は動的な Slice を含み、converter が 63 と 64 の broadcast 形状の衝突を起こす。
対処は、Slice を静的な形に書き換えた ONNX を作り直すことだった。
この修正を入れた実行用モデルを、以後の比較対象にしている。
一発実行の結果
batch 1、系列長 128 の固定形状で、グラフの実行区間だけを測った。
equiv tokens/sec は 128 を実行秒で割った便宜的な換算値である。
| 実行系 | 実行区間 | equiv tokens/sec |
|---|---|---|
| FP16、QNN/HTP | 127.8 ms | 1,001.6 |
| FP32、QNN/HTP | 129.5 ms | 988.4 |
| FP32、QNN/CPU | 2,413.0 ms | 53.0 |
HTP では FP16 と FP32 の差がほとんど無い。 一方、同じ QNN でも CPU backend は 19 倍遅く、NPU に載る意味はここにある。
FP32 を HTP に載せる場合、グラフの実行そのものは 130 ms でも、その前の context 準備に 160 秒級の時間がかかる。 起動のたびに払う時間なので、常駐させる設計が前提になる。
通らなかった経路も記録しておく。
FP16 の CPU backend は Gather の検証で止まり、値を取れていない。
ONNX Runtime の CPU 実行は、FP16 と FP32 のどちらもモデル読み込みの時点で型の不整合になり、比較に使えなかった。
1,000 tokens/sec は生成速度ではない
上の表の換算値は、128 token を一度に処理する prefill 相当の実行から出した数字である。 対話で体感する速さは、1 token ずつ順に生成する decode の速さで決まり、これは別の測定が要る。
そこで、以後の比較で使う指標を次のように決めた。
- TTFT。prefill が終わってから最初の token が出るまでの時間。
- TPOT。decode 区間の token あたりの平均時間。
- ITL。token と token の間隔。p50 と p95 で見る。
一発実行の記録にもこの項目を持たせ、値が無いときは null として残す。
生成ループを通した計測が揃うまで、本記事の 1,000 tokens/sec を「Qwen3.5 9B の生成速度」として引用してはいけない。
実効 TOPS は未算出
今回の実行は profiling を有効にしていないため、実効 TOPS は出していない。 判っているのは SoC の公称ピークだけで、実アプリの値とは別物である。 次の段階では、INT8 への量子化と、生成ループを通した decode の計測に進む。
この記事で扱った機材
Crucial DDR5-5600 SO-DIMM 128 GB キット(64 GB × 2、CT2K64G56C46S5)
27B 級の重みを bf16 のまま読み込んだり、1 bit への変換の中間状態を持ったりするには、ミニ PC のメモリをこの容量まで積む必要がある。