tk3.biz
ブログ一覧に戻る Back to Blog

Ryzen AI PCのローカルLLMを7倍速くした話(llama.cpp + Vulkan + Qwen) Ryzen AI PCのローカルLLMを7倍速くした話(llama.cpp + Vulkan + Qwen)

AIローカルLLMAMDRyzen AIllama.cppQwenVulkanWindows

はじめに

前回、Ryzen AI 9 HX 370 搭載のミニPCから ローカルLLM環境を全部消しました。68.84 GB を削除して、まっさらな状態です。

今回はそこに新しく環境を作ります。条件は3つ。

  • コマンドだけで完結すること(GUIに依存しない)
  • GPU を使って速くすること
  • Qwen を動かすこと

結論から言うと、生成速度は旧環境の約7倍になりました。 そして途中で 最新モデルを選んで失敗しました。その話も含めて書きます。

結論

winget install --id ggml.llamacpp
llama-server -hf unsloth/Qwen3.6-35B-A3B-MTP-GGUF:UD-Q4_K_XL
  --port 8080 -c 16384 -ngl 99
  -b 8192 -fa on --cache-reuse 256 --kv-unified --jinja
  --spec-type draft-mtp --spec-draft-n-max 2
項目旧環境新環境
生成(コード)2.6 t/s 相当20.7〜22.6 t/s
生成(散文)〃17.5 t/s
プロンプト処理—353 t/s

品質を落とす設定は一切使っていません。以下、どうやってここに辿り着いたか。

なぜ Vulkan なのか

前回の掃除で設定ファイルを読んでいたとき、こんな記述を見つけました。

"llamacpp": { "backend": "cpu", ... }

CPU で推論していました。 Radeon 890M も NPU も積んでいるのに。 96 GB のメモリのおかげで大きいモデルが動いてしまうので、 「動いてるからいいか」で半年放置されていたわけです。

では GPU を使うとして、AMD には ROCm と Vulkan の2つの道があります。 同じ Ryzen AI 9 HX 370 + DDR5-5600 での実測が公開されていました。

バックエンド別の生成速度。CPU 24スレッドが2.59 t/s、ROCm(iGPU)が4.76 t/s、Vulkan(iGPU)が13.41 t/s。VulkanはROCmの2.8倍、CPUの5.2倍

Vulkan が ROCm の2.8倍、CPU の5.2倍でした。 AMD 公式の ROCm が Vulkan に負けるのは意外ですが、理由もはっきりしています。

  • gfx1150(890M)は ROCm の公式サポート表に今も載っていない
  • プロンプト処理だけは ROCm が 41% 速いが、対話用途では生成速度のほうが効く

CPU が遅いのはメモリ帯域ではなく Infinity Fabric がボトルネックだから。 CPU コアはメモリコントローラを使い切れませんが、iGPU は使い切れます。

WSL は使いませんでした

Linux のツールチェーンが使えるので最初は有力候補でした。調べた結果、この用途では不利です。

  • WSL2 の Vulkan はエミュレーション経由で性能が落ちる
  • ROCm on WSL2 は Strix 対応が入ったが、乗る先が「遅いほうの ROCm」
  • amdgpu.gttsize などの GTT カーネルパラメータが WSL2 では効かせられない

WSL を選ぶと「遅い道」か「もっと遅い道」しかありませんでした。

導入はコマンド一発だった

winget install --id ggml.llamacpp

これだけです。winget の公式パッケージが Vulkan ビルドを配布していて、 llama-cli / llama-server / llama-bench が PATH に載ります。

> llama-cli --list-devices
Vulkan0: AMD Radeon(TM) 890M Graphics (78739 MiB, 74802 MiB free)

BIOS をいじる覚悟をしていたのに、不要だった

事前に調べたとき、複数の記事に同じ警告がありました。

BIOS の UMA Frame Buffer Size が Auto だと Vulkan が VRAM を 0 バイトと誤認し、 モデルロードに失敗する。Fixed に変更して容量を割り当てること。

再起動して BIOS に潜る手順まで書いて身構えていたのですが、 当環境では起きませんでした。

ドライバが報告する専用 VRAM は 2 GB のままです。 それなのに llama.cpp は 約 77 GB(空き 73 GB) を認識しています。 最近の llama.cpp が共有メモリを自動検出するようになったためでした。

結果として BIOS は一切触らず、再起動もなしで済みました。 先に --list-devices で実測しておいて良かった例です。

最新モデルを選んで失敗した

ここからが本題です。

モデルは Qwen を使うと決めていました。調べると Qwen3.8-27B(2026年8月リリース)が最新世代で、 「Qwen-Max クラスを初めてオープン化」と書いてあります。新しいほうがいいだろうと選びました。

測った結果がこれです。

pp512tg128
Qwen3.8-27B Q4_K_M (16.23 GiB)29.6 t/s3.6 t/s

3.6 t/s。 1000 トークンのプロンプトで初回応答まで34秒、 そのあと毎秒3.6トークン。対話には使えません。

設定では1ミリも動かなかった

チューニングで何とかならないかと一通り試しました。

試したこと結果
FlashAttention -fa on誤差範囲(むしろ微減)
ubatch 512 / 1024 / 2048全部横ばい
-b 8192pp のみ +18%、生成は不変

生成速度だけがびくともしません。帯域計算をしたら理由が分かりました。

16.23 GiB × 3.6 t/s = 実効 60.5 GB/s(理論 89.6 GB/s の 68%)

ハードウェアはちゃんと性能を出していました。 27B の密モデルは 1 トークンごとに全重み 16 GB を読むので、 DDR5-5600 の 128bit(理論 89.6 GB/s)では物理的にこれ以上出ません。

設定の問題ではなく、モデルの選び方の問題でした。

ファイルが大きいほうが速かった

比較対象として、前の環境で使っていた Qwen3.6-35B-A3B(MoE)を測りました。 総パラメータは 35B で 27B より大きく、ファイルも 4.5 GB 大きいモデルです。

ファイルサイズは MoE 20.74 GiB に対し密27Bが16.23 GiB で MoE のほうが4.5GB大きい。それなのに生成速度は MoE 21.5 t/s に対し密27Bが3.6 t/s で MoE が5.7倍速い。理由は1トークンごとに読む量が違うため

ファイルpp512tg128
Qwen3.8-27B(密)16.23 GiB29.6 t/s3.6 t/s
Qwen3.6-35B-A3B(MoE)20.74 GiB281 t/s20.7 t/s
差MoE が 4.5 GiB 大きい9.5 倍5.7 倍

大きいほうが5.7倍速い。 直感に反しますが、理由は単純です。

  • 密モデルは毎トークン 全重み 16.23 GiB を読む
  • MoE は 256 エキスパートのうち 8 + 共有1 しか読まない(実効 3B 相当)

実効帯域を検算すると MoE も 60 GB/s 前後で、密モデルと同じでした。 ハードウェアは同じ性能を出していて、違うのはモデル構造だけです。

体感で言うと、1000 トークンのプロンプトで初回応答が 34秒 → 3.6秒 になります。

この機体でのモデルの選び方 総パラメータ数でもファイルサイズでもなく、 **「1トークンあたり何バイト読むか」**で選ぶ。 帯域が天井なので、ここが速度に直結します。

最新世代を追うより、構造が合っているモデルを選ぶほうが効きました。

品質を落とさないチューニング

主役を MoE に決めてから、速度を詰めていきます。 方針は「計算を省くか並べ替えるだけの手しか使わない」です。

設定効果品質
-b 8192プロンプト処理 +26%(281→353 t/s)影響なし
--cache-reuse 256共通prefixの 95% を再計算せず(※訂正あり。下記)影響なし
-fa onFlashAttention影響なし
--kv-unifiedKV の断片化回避影響なし

-b はバッチサイズです。2048 / 4096 / 8192 / 16384 で振ったところ 8192 が最良で、16384 まで上げると微減しました。 一方 -ub(マイクロバッチ)は密・MoE のどちらでも効果ゼロだったので触っていません。

--cache-reuse が効いている証拠

llama-server は応答に timings を返します。 長めの system プロンプトを固定して連続リクエストすると、

cache_n  : 425    ← キャッシュから再利用したトークン
prompt_n : 21     ← 実際に計算したトークン

446 トークンのプロンプトのうち 425 トークン(95%)を再計算していません。 system プロンプトを固定する使い方なら、2回目以降の待ち時間がほぼ消えます。

訂正(2026-09-27): この再利用は --cache-reuse の効果ではありませんでした。 起動ログに cache_reuse is not supported by multimodal, it will be disabled と出ており、 画像用の mmproj を読み込んでいるため --cache-reuse は最初から無効でした。 mmproj を外しても、SSM とのハイブリッド構成のため not supported by this context で無効になります。 上の cache_n 425 は、既定で有効な --cache-prompt(先頭が一致する部分を再計算しない)によるものです。 現象は本物で、効果を別の設定のものと取り違えていました。 詳しくは長い文書を使い回す回に書いています。

あえて使わなかったもの: KV キャッシュ量子化

-ctk q8_0 でKVキャッシュを量子化する手があります。 品質劣化はほぼ無い(f16 と同等の perplexity)とされる定番の手ですが、不採用にしました。

f16 が収まる環境では f16 のほうが速いからです。 量子化すると毎トークン逆量子化が走り、深いコンテキストほど不利になります (64K 深度で f16 比 55% まで落ちるという計測もあります)。

当環境は Vulkan が 73 GB を認識していて f16 で十分収まるので、 量子化しないほうが速いという結論でした。メモリが足りなくなったときの予備手です。

投機デコードは「伸ばしすぎると遅くなる」

最後に MTP(Multi-Token Prediction)投機デコードを試しました。

小さなドラフトが数トークン先を先読みし、本体モデルがまとめて検証する仕組みです。 外れたら破棄して本体の出力を使うので、出力の分布は変わりません。 つまり無損失で速くなります。

Qwen3.6-35B-A3B には MTP 層を含むビルドがあり、公称「1.5〜2倍」。 --spec-draft-n-max(何トークン先読みするか)を振って測りました。

投機デコードのn-max別の生成速度。投機なしが散文コードとも15.5 t/s。n-max=2でコード20.7、散文17.5と最速。n-max=4で散文13.5、n-max=6で散文11.1と投機なしより遅くなる

n-max散文コード
なし15.5 t/s15.5 t/s
217.5 t/s (+12.6%)20.7 t/s (+33.4%)
413.5 t/s (−13%)18.3 t/s (+16%)
611.1 t/s (−28%)16.5 t/s (+3%)

伸ばすほど速くなる、ではありませんでした。 n-max=6 の散文は投機なしより 28% 遅いです。

  • コードは次のトークンが予測しやすく、当たり率が高い → 伸ばしても効く
  • 散文は予測しにくく外れやすい → 伸ばすほど捨てる計算が増えて遅くなる

両方扱うなら短めが安全で、2 が最適でした。 公称の1.5〜2倍には届きませんでしたが、確実にプラスです。

投機デコードは「とりあえず有効にすれば速くなる」ものではありません。 必ず有効・無効の両方を測って比較してください。

測り方をしくじった話

最初の計測では、対照群(投機なし)の起動がタイムアウトで失敗し、 投機ありの数字しか残りませんでした。 モデル 22.9 GB のダウンロードが終わる前に計測を始めてしまい、 起動待ちの上限を使い切っていたのです。

片側だけの数字は「速いか」の証拠になりません。取り直しました。 初回ダウンロードを伴う計測は、先にモデルを取得してから比較を回すのが教訓です。

まとめ

旧環境新環境
バックエンドCPUVulkan(iGPU)
モデル—Qwen3.6-35B-A3B MTP(MoE)
生成2.6 t/s 相当17.5〜22.6 t/s

約7倍になりました。効いた順に並べると、

  1. MoE モデルを選ぶ(密27B比 5.7倍)
  2. Vulkan を使う(CPU比 5.2倍)
  3. MTP 投機デコード(+12.6〜33.4%)
  4. -b 8192(プロンプト処理 +26%)

上2つが圧倒的で、細かいフラグは最後の味付けでした。 大きい判断を間違えなければ、細かいチューニングは大して要らないというのが実感です。

今回いちばんの学びは、最新モデルが最適とは限らないことでした。 Qwen3.8-27B は確かに新しく強いモデルですが、 89.6 GB/s しかない機体では構造が合いませんでした。 スペック表の「最新」「高性能」より、自分の機体のボトルネックに合うかで選ぶべきでした。

検証で4モデル・61.2 GB まで膨らんだキャッシュは、 役割の済んだものを消して 23.3 GB に戻しました。前回の掃除を無駄にしないためにも、 検証用に落としたモデルはその場で始末するのを習慣にしたいところです。

関連記事 Related Posts

Ryzen AI PCのローカルLLM環境を一度まっさらにする Ryzen AI PCのローカルLLM環境を一度まっさらにする

AMD Ryzen AI 9 HX 370搭載機に積み上げたローカルLLM環境を、最新の方法で組み直すために一度全部消します。何が残っているかの棚卸しから、削除の手順、消してはいけないものまでをまとめました。 AMD Ryzen AI 9 HX 370搭載機に積み上げたローカルLLM環境を、最新の方法で組み直すために一度全部消します。何が残っているかの棚卸しから、削除の手順、消してはいけないものまでをまとめました。

Ryzen AI ミニPCでローカルLLMを実用速度で動かす完全ガイド(2026年版) Ryzen AI ミニPCでローカルLLMを実用速度で動かす完全ガイド(2026年版)

Ryzen AI 9 HX 370(Radeon 890M)のミニPCで、ローカルLLMを毎秒20トークン前後で動かすまでの手順を1本にまとめました。llama.cppのVulkan版を入れ、MoEモデルを選び、効く設定だけを入れて、llmと打つだけで使える状態にします。つまずいた点も全部載せています。 Ryzen AI 9 HX 370(Radeon 890M)のミニPCで、ローカルLLMを毎秒20トークン前後で動かすまでの手順を1本にまとめました。llama.cppのVulkan版を入れ、MoEモデルを選び、効く設定だけを入れて、llmと打つだけで使える状態にします。つまずいた点も全部載せています。

Ryzen AI の NPU を使わなかった理由 Ryzen AI の NPU を使わなかった理由

50 TOPS の NPU を積んだ機体でローカルLLMを動かしてきましたが、一度も NPU を使っていません。使えるのか調べたら、動く保証がなく、動いても iGPU より遅く、最大の利点が自分の使い方では効かないと分かりました。インストーラを落としたところで止めた記録です。 50 TOPS の NPU を積んだ機体でローカルLLMを動かしてきましたが、一度も NPU を使っていません。使えるのか調べたら、動く保証がなく、動いても iGPU より遅く、最大の利点が自分の使い方では効かないと分かりました。インストーラを落としたところで止めた記録です。