ローカルLLMで広く使われているGGUFは、llama.cppとともにCPUやNVIDIA GPUで動かす例がよく知られています。GGUF自体はモデルの重みやメタデータを格納する形式であり、実際の処理をCPU、GPU、NPUのどこで実行するかは、ランタイムとバックエンドによって決まります。CUDAなどのバックエンドが使われているのと同じように、GGUFをQualcommのHexagon NPUで実行するバックエンドもあります。
QualcommのGenieXは、GGUFを扱うllama.cpp経路と、Qualcomm AI HubでコンパイルされたQAIRT (Qualcomm AI Runtime) 経路の2つを備えています。どちらもHexagon NPUを利用できますが、対応するモデル形式、導入方法、性能特性は同じではありません。
本記事では、Dragonwing IQ-9075 EVKで同じQwen3-4B系列を異なる配布形式と量子化で動かした実測をもとに、この2経路の違いと、それぞれに現れた性能特性をご説明します。比較にはCPU実行を加え、llama.cpp CPU、llama.cpp NPU、QAIRT NPUの3経路で測定しました。
執筆者
岩脇 秀隆
株式会社マクニカ マクニカフィネッセカンパニー
技術統括部技術第5部 第2課
この記事でわかること
・GGUFをHexagon NPUで実行できる仕組み
・GenieXが備える2つのNPU実行経路の違い
・GGUFでNPUを使う場合にQ4_0を選ぶ理由
・3経路の実測差(prefill、応答開始時間、CPU占有)
・経路ごとに異なる持ち込みやすさとNPU性能
検証環境
・ボード:Dragonwing IQ-9075 EVK(CPUは8コア)
・モデル:Qwen3-4B(llama.cpp経路はunsloth/Qwen3-4B-GGUF:Q4_0、QAIRT経路はqualcomm/Qwen3-4B:W4A16)
・ランタイム:GenieX v0.4.0、QAIRT 2.45、llama.cpp Hexagon backend(GenieX v0.4.0同梱)
測定は速度とCPU占有が中心です。Q4_0とW4A16の出力品質が同等であることを示す比較ではありません。
Dragonwing IQ-9075のスペック
IoT・組込み向けの高性能エッジAI SoCで、本記事のEVKは100 TOPS版 (IQ9075-AA)です。 主要スペックは以下のとおりです。
|
項目 |
内容 |
|
SoC |
Qualcomm QCS9075 |
|
NPU |
Hexagon V73 × 2(デュアルCDSP) |
|
演算性能 |
100 TOPS(IQ9075-AA、NPU2基合計。単一モデル実行は1基を使用) |
|
動作温度 |
-40〜+115℃ |
|
評価機メモリー |
36GB |
GenieXには2つの推論ランタイムがある
GenieXは、LLMやVLMをQualcommデバイス上で実行するための推論環境です。CLI、Python API、OpenAI互換APIなどの共通インターフェースから、2種類のランタイムを利用できます。llama_cppはllama.cppエンジンを使い、その内部のGGML HexagonバックエンドがNPU実行を担当します。qairtはQualcomm AI Engine Direct (QNN/QAIRT)を使う別のランタイムです。
公式ドキュメントでは、GGUFを扱うランタイムをllama_cpp、Qualcomm AI Hubのプリコンパイル済みバンドルを扱うランタイムをqairtと区別しています。
|
項目 |
llama.cpp経路 |
QAIRT経路 |
|
主なモデル形式 |
GGUF |
チップセット別のQAIRTバンドル |
|
モデルの入手先 |
Hugging Face、ModelScope、ローカルファイルなど |
Qualcomm AI Hub |
|
実行先 |
CPU / GPU / Hexagon NPU |
Hexagon NPU |
|
Hexagon NPU向けの代表的な量子化 |
Q4_0 |
W4A16が中心 |
|
自分のモデルの持ち込みやすさ |
高い |
対象SoC向けのexport作業が必要 |
|
特徴 |
幅広いGGUFを試しやすい |
チップセット向けに最適化しやすい |
この2経路は、どちらが常に優れているという関係ではありません。任意のモデルをすばやく試したい場合と、対象SoCで高いNPU性能を狙う場合では、適した経路が異なります。
GGUFの実行先はランタイムのバックエンドが決める
GGUFはファイル形式であって、実行先そのものは規定しません。GenieXが対応するGGUFであれば、同じファイルのままCPU、GPU、Hexagon NPUのいずれでも実行できます。
GGUFにはモデルの重み、量子化形式、ネットワーク構造に関する情報などが格納されます。一方、行列演算をどのプロセッサーで実行するかは、GGUFそのものではなくllama.cppのバックエンドが決めます。
GenieXのllama.cppランタイムは、CPU、Adreno GPU、Hexagon NPUに対応しています。そのため、GenieXの対象アーキテクチャー、量子化、演算に対応するGGUFであれば、同じファイルを使って処理先を変更できます。任意のGGUFがすべてのQualcommデバイスで動くという意味ではありません。
GenieXがモデルを取得する先は--model-hubで選択でき、Qualcomm AI Hub、Hugging Face、ModelScope、Dockerレジストリー、ローカルファイルシステムに対応します。一度取得したGGUFをローカルから読ませる運用もできます。
以下はGenieX v0.4.0を導入済みのIQ-9075 EVKで使った最小例です。初回はモデルのダウンロードにネットワーク接続と保存容量が必要です。GenieXの導入や対応プラットフォームは公式Quickstartをご確認ください。
# Hexagon NPUで実行
geniex infer unsloth/Qwen3-4B-GGUF:Q4_0 \
--compute npu \
--ngl -1 \
--think=false
# CPUで実行
geniex infer unsloth/Qwen3-4B-GGUF:Q4_0 \
--compute cpu \
--ngl 0 \
--think=false
# 対応tensorをNPU、残りをCPUへ割り当てるhybrid経路(`--think=false`はQwen3の思考モードを無効化する指定)
geniex infer unsloth/Qwen3-4B-GGUF:Q4_0 \
--compute hybrid \
--ngl -1 \
--think=false
--ngl -1は、llama.cppに全レイヤーのオフロードを要求する指定です。llama.cpp由来の名前でGPU layersの略ですが、Hexagon NPUへのオフロードにも同じ指定を使います。--compute npuはHTP(Hexagon Tensor Processor、Hexagon NPUの演算部)へ固定する経路で、対応しない演算やNPU側のメモリー確保失敗により、モデルのロードまたは推論が失敗する場合があります。
一方、--compute hybridは対応tensorをNPUへ割り当て、残りをCPUで処理する経路です。固定NPUとCPU fallbackを含むhybridは区別して評価する必要があります。
Hexagon NPUに載る量子化形式は限られる
GGUFにはQ4_0、Q8_0、Q4_K_Mなど複数の量子化形式があります。CPUでの利用ではK-quantがよく使われますが、GenieXの公式ドキュメントでは、Hexagon NPUバックエンド向けにQ4_0を推奨しています。理由は、Hexagonバックエンドが扱える量子化形式が限られているためです。
本検証時点のupstream llama.cppのソースコードを確認すると、Hexagonバックエンドが受け付けるのはF16、F32、IQ4_NL、MXFP4、Q4_0、Q4_1、Q8_0で、K-quantは対象外でした。さらに、NPU側でrepackされるのはQ4_0、Q8_0、MXFP4に限られます。
ここが実務上の落とし穴になります。一般に配布されているGGUFはQ4_K_Mが多く、そのまま持ってくるとエラーにはならず、NPUに載らないままCPUで実行されます。「NPUで動かない」ではなく「気づかずにCPUで動いている」という形で現れます。
量子化名は次のように読みます。
|
表記 |
意味 |
|
Q4_0 |
4bit中心の基本量子化。末尾の0は同系列の方式番号 |
|
Q4_K_M |
K-quantの4bit中心、Mediumレシピ。重要tensorには高い精度を使う混合量子化 |
|
W4A16 |
Weight(重み)4bit、Activation(活性化)16bit |
Q4_K_Mは全tensorが一律4bitという意味ではなく、モデルによって実効bits/weightが変わります。llama.cppのLlama 3.1 8B例では4.8944 bits/weightです。
|
量子化形式 |
Hexagonバックエンドでの扱い |
備考 |
|
Q4_0 |
受理され、NPU向けにrepackされる |
公式が推奨する形式。本検証で使用 |
|
Q8_0、MXFP4 |
受理され、NPU向けにrepackされる |
Q8_0はQ4_0よりファイルサイズが大きい |
|
F16、F32、IQ4_NL、Q4_1 |
受理されるがrepackの対象外 |
本検証では測定していない |
|
Q4_K_M、Q5_K_MなどのK-quant |
受理されない |
CPUで実行される |
本検証で実測したのはQ4_0のみです。配布物にQ4_0版があるモデルは、そのファイルをそのままNPU経路で実行できます。
Q4_0版がない場合、llama.cppの標準ツールでは、元のBF16またはFP16チェックポイントからGGUFを作成し、そのGGUFをQ4_0へ量子化する2段階になります。
python convert_hf_to_gguf.py \
--remote <organization>/<model> \
--outfile model-bf16.gguf \
--outtype bf16
./build/bin/llama-quantize \
model-bf16.gguf \
model-Q4_0.gguf \
Q4_0
すでに量子化されたQ4_K_MなどのGGUFを、さらにQ4_0へ変換する二重量子化は推奨されません。llama.cppの公式ドキュメントでは、量子化済みモデルの再量子化は16bitまたは32bitの元データから量子化する場合に比べて品質を大きく損なう可能性があると説明されています。
QAIRT経路はW4A16バンドルで動く
もう一つのQAIRT経路では、Qualcomm AI Hubから対象チップセット向けにコンパイルされたモデルバンドルを取得します。LLMでは、4bitの重みと16bitの活性化を組み合わせたW4A16が主に使われています。
# GenieX v0.4.0での実測時モデルID
geniex infer qualcomm/Qwen3-4B:W4A16 \
--compute npu \
--think=false
モデル名前空間はGenieXの版によって異なります。本記事の検証時点ではqualcomm/Qwen3-4B、公式ドキュメントでは同系統のAI Hubモデルをai-hub-models/Qwen3-4B形式で案内しています。実際の表記は、利用中の版のモデルガイドとgeniex infer -hの出力で確認できます。
QAIRTバンドルはNPU専用です。GGUFのように実行時にCPUへ切り替える形式ではなく、量子化方式、対象チップセット、コンテキスト長などがバンドル作成時に決まります。
対応済みモデルを利用する場合は導入が容易ですが、任意のLLMを自分でバンドル化する場合は、モデル固有のexportパイプライン、量子化用データ、KVキャッシュ、分割グラフ、Context Binaryなどを考慮する必要があります。GGUFをそのままW4A16へ変換することはできず、元のモデルチェックポイントと対応するexport手順へ戻る必要があります。
IQ-9075で3つの経路を実測
以降の測定では、用語を次の意味で使います。
|
用語 |
意味 |
|
prefill |
入力tokenをまとめて処理する段階 |
|
decode |
出力を1 tokenずつ生成する段階 |
|
TTFT |
リクエスト開始から最初の出力tokenが届くまでの時間 |
|
E2E |
リクエスト開始から128出力tokenの受信完了までの時間 |
|
tok/s |
1秒あたりに処理または生成したtoken数 |
Dragonwing IQ-9075 EVK上で、同じQwen3-4B系列を次の3経路で測定しました。QAIRTはqualcomm/Qwen3-4B:W4A16、llama.cppはunsloth/Qwen3-4B-GGUF:Q4_0です。
・llama.cpp CPU
・llama.cpp Q4_0 / Hexagon NPU
・QAIRT W4A16 / Hexagon NPU
llama.cppのNPU経路は--compute npuの固定実行です。hybrid経路は今回の測定対象に含めていません。
まず、入力を読み込むprefillの速度を比較します。llama.cppのCPUとNPUをllama-benchの同じ測定境界で比較すると、512 token相当の入力処理はNPUが444.69 tok/s、CPUが75.82 tok/sとなり、llama.cpp NPUは約5.9倍高速でした。QAIRTはllama-benchでは測れないため、この5.9倍はllama.cpp経路内の比較です。
なお、この値はllama-benchが測る純粋な入力処理スループットであり、後述するTTFTから逆算した値とは一致しません。TTFTには入力処理以外の時間も含まれ、入力が長くなるほど1 tokenあたりの処理コストも上がるためです。この5.9倍は、配布されているGGUFをそのまま実行した経路の値です。専用のバンドルを用意しなくても、Hexagon NPUはprefillでこの差を出します。
次に、同じ処理にどれだけCPUを使ったかを見ます。短いプロンプトから128 tokenを生成し、計測区間のプロセスCPU時間を実時間で割って論理コア数に換算しました。
|
実行経路 |
生成速度 |
平均CPU占有 |
|
QAIRT W4A16 NPU |
13.5 tok/s |
0.83コア |
|
llama.cpp Q4_0 NPU |
11.3 tok/s |
0.99コア |
|
llama.cpp CPU |
14.4 tok/s |
6.64コア |
短い応答の生成速度は3経路とも近く、差は数%から2割の範囲に収まりました。大きく開いたのはCPU占有です。QAIRT NPU経路はCPU経路の約94%の生成速度を保ちながら、平均CPU占有を6.64コアから0.83コアへ抑えています。8コアのうち約5.8コアが他の処理のために空く計算です。
つまり、短い応答の生成速度だけを比べても3経路の差は小さく、この指標だけではNPUを使う意味が見えません。エッジLLMでNPUが効くのは、次に見る長い入力の処理と、前述のCPU占有です。
長い入力ほどNPUが効く
続いて、256〜3,840 tokenの入力を使い、3経路をGenieXの同じ/v1/completions API境界で比較しました。条件はtemperature 0、top-k 1、seed 42、128出力tokenです。6種類の入力長を各5回、昇順と降順を交互に実行し、試行の間に45秒の冷却時間を置きました。
経路を切り替えると前の経路が確保したメモリーが完全には戻らないため、各経路はボードを再起動した直後の状態から推論サーバーを1つだけ常駐させて測定しています。モデルの初回ロードを含む1回目のリクエストは集計から除外し、残る全90回が正常に完了しています。
3,840 token入力時のTTFT(最初のtokenが返るまでの時間)とE2E(128 tokenの受信完了まで)の中央値は、次の結果となりました。
|
実行経路 |
TTFT中央値 |
TTFTのCPUとの比較 |
E2E中央値 |
|
QAIRT W4A16 NPU |
4.511秒 |
17.6倍高速 |
15.674秒 |
|
llama.cpp Q4_0 NPU |
11.230秒 |
7.1倍高速 |
32.797秒 |
|
llama.cpp CPU |
79.499秒 |
基準 |
91.779秒 |
TTFTだけでなく、応答が出そろうまでのE2Eでも差は残ります。3,840 token入力では、CPUの91.779秒に対しQAIRT NPUは15.674秒でした。
この差は短い入力でも現れています。256 token入力でのE2Eは、QAIRT NPUが8.195秒で最も短く、CPUとllama.cpp NPUは11.488秒と11.243秒でほぼ同等でした。入力が長くなるにつれて差はさらに拡大します。短いプロンプトだけで評価すると、NPUが持つprefill性能の価値を過小評価する可能性があります。
長い入力ではdecode速度自体も低下しますが、低下幅は経路ごとに異なりました。256 tokenから3,840 tokenへ入力を伸ばすと、低下が最も小さかったのはQAIRT NPUで16.3→11.5 tok/s(約-29%)、次がCPUで16.0→10.4 tok/s(約-35%)、llama.cpp NPUは12.1→5.9 tok/s(約-51%)でした。
なお、decodeの順位は測定境界によって変わります。前節の短文条件ではCPUが14.4 tok/s、QAIRT NPUが13.5 tok/sでしたが、本節の/v1/completionsの256 token条件ではQAIRT NPUが16.3 tok/s、CPUが16.0 tok/sで順位が入れ替わります。decode速度を比較する際は、どのAPI境界で測ったかを揃える必要があります。
NPUの価値は長文入力とCPU解放にある
LLMの処理は、大きく入力を読み込むprefillと、1 tokenずつ生成するdecodeに分かれます。
prefillは大きな行列演算をまとめて処理するため、NPUの並列演算能力を活かしやすい処理です。一方、decodeは毎回少量の演算を繰り返し、大きなモデル重みを読み続けるため、メモリ帯域の影響が強くなります。
そのため、長いプロンプト、検索結果、会話履歴などを入力する用途では、prefillの高速化がそのまま応答開始時間の短縮につながります。一方、decodeはメモリ帯域の影響が強く、NPUを使えば必ず速くなるとは限りません。
さらに、CPU占有の差は、同じデバイス上で他の処理と並走させるときに効いてきます。前節の実測では、CPU経路は8コアのうち平均6.64コアを使い続けたのに対し、QAIRT NPU経路は0.83コアでした。
LLM推論に8コアの大半を渡す構成では、次のような処理に残せる余力が小さくなります。
・カメラやセンサー入力の処理
・音声認識や音声合成
・データベース検索
・エージェントのツール実行
・Web APIやUIの応答
本検証で測ったのはLLM推論側のCPU占有であり、これらの処理と組み合わせた状態での性能を測ったものではありません。
NPUは単にLLMを速くする演算器ではなく、LLM推論からCPUを解放し、デバイス全体の処理余力を作る役割も持っています。
2つの経路に現れた性格の違い
GGUF + llama.cpp経路の性格
・公開されているGGUFをそのまま持ち込める(Q4_0版が存在する場合)
・同じモデルファイルで実行先をCPU、GPU、NPUへ切り替えられる
・コンテキスト長などを実行時に指定できる
・対象SoC向けQAIRTバンドルが無いモデルでも試せる
本検証では、Q4_0版の有無と、固定NPU経路でロードと推論が通るかを実機ログで確認しました。ロードや推論が通るかどうかはモデルと構成に依存します。
QAIRT経路の性格
・Qualcomm AI Hubに対象SoC向けバンドルがあるモデルは、そのまま利用できる
・バンドルが無いモデルも、元のチェックポイントからexportして自分で用意できる
・チップセット向けに事前コンパイルされた状態で動く
・本検証ではTTFTが最も短く、CPU占有も最も低かった
・量子化方式、対象チップセット、コンテキスト長がバンドル作成時に固定される
本検証の条件では、対象SoC向けバンドルが提供されているモデルにおいて、QAIRT経路がTTFTとCPU占有の両方で最も良い値を示しました。ただし品質は本検証の範囲外です。
Q4_0とW4A16は量子化方式が異なるため、速度の順位がそのまま出力品質の順位を意味するわけではありません。バンドルが提供されていないモデルでも、元のチェックポイントからexportすればQAIRT経路に載せられます。一方、実行時に構成を変えながら比較する段階では、GGUF経路の方が手数が少なく済みます。どちらの経路が適するかは、対象SoC、モデル、要件によって変わります。
まとめ
GGUFをどこで実行するかは、形式ではなくランタイムのバックエンドが決めます。GenieXのllama.cppランタイムを使うことで、対応するGGUFをHexagon NPU上で実行できます。GenieXには、次の2つのNPU経路があります。
・GGUF + llama.cpp Hexagon:幅広いモデルを持ち込みやすい汎用経路
・Qualcomm AI Hub + QAIRT:対象チップセット向けに事前コンパイルされたNPU経路
IQ-9075の実測では、llama.cpp NPUは同じllama.cpp CPU経路に対し、512 tokenのprefillを約5.9倍高速化しました。3,840 token入力時のTTFTでは、QAIRT NPUがllama.cpp CPUの17.6倍、llama.cpp NPUが7.1倍高速でした。一方、短いdecodeではCPUが速い場合もあります。生成速度だけを見ると経路の差は小さく、入力長、応答開始時間、CPU占有まで含めると順位が変わりました。
以上はIQ-9075とQwen3-4Bという条件での実測結果です。対象SoC、モデル、バンドルの提供状況が変われば、数値も経路間の順位も変わります。
お問い合わせ
Qualcomm Dragonwing SoCを使ったオンデバイスLLMの構築や、GenieX、Qualcomm AI Hubを用いたモデル実装にご興味をお持ちでしたら、お気軽にお問い合わせください。
