前回の記事でllama.cppとQwen3.5-35B-A3BをVulkanバックエンドで動かすところまでを紹介した。今回はその後に実施したパラメータチューニングの記録として、ベンチマーク結果を中心にまとめる。
環境
- マシン: MINISFORUM HX370
- GPU: AMD Radeon 890M(RDNA 3.5 / gfx1150、UMA 64GB)
- OS: CachyOS Linux
- バックエンド: Vulkan(ROCmより高速なため)
- モデル:
Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf(Unsloth製)
きっかけ:Redditのベンチマーク情報
r/LocalLLaMAのスレッドで、Subject-Tea-5253氏がllama-benchによるbatch-size比較を投稿していた。RTX 3090環境でのデータだが、ubatch-sizeを128から512に変えるだけで2.6倍の速度向上(175 t/s → 460 t/s)が報告されていた。
HX370(Vulkanバックエンド)でも同様の効果があるか検証することにした。
Unslothモデルの再ダウンロード(2025年3月2日更新対応)
Unslothが3月2日にQwen3.5-35B-A3BのGGUFを更新した。主な変更点は以下の通り。
- ツール呼び出し(tool calling)の修正
- Q4_K_XLからMXFP4レイヤーを廃止
手元のファイルが3月1日付だったため、最新版に更新した。
from huggingface_hub import hf_hub_download
path = hf_hub_download(
repo_id='unsloth/Qwen3.5-35B-A3B-GGUF',
filename='Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf',
local_dir='./models',
force_download=True
)
print('Done:', path)
検証1:batch-sizeとubatch-sizeの最適化
テスト条件
--n-prompt 1024 --n-gen 0(prompt処理速度のみ計測)--n-gpu-layers 99 --flash-attn 1- batch-sizeとubatch-sizeを128/256/512の組み合わせで計測
結果
| batch-size | ubatch-size | pp速度(t/s) |
|---|---|---|
| 128 | 128 | 215.54 |
| 128 | 256 | 212.99 |
| 128 | 512 | 212.47 |
| 256 | 128 | 211.95 |
| 256 | 256 | 256.70 |
| 256 | 512 | 256.06 |
| 512 | 128 | 212.15 |
| 512 | 256 | 256.08 |
| 512 | 512 | 275.50 |
考察
batch-size 512 / ubatch-size 512 が最速。デフォルト設定(128/128)と比べて約28%の向上。ただしCUDAほど劇的なスケール(2.6倍)はなく、Vulkanバックエンドではスケールの上限が異なることがわかった。
この結果をsystemdサービスに反映した。
# systemd ExecStartに追加
--batch-size 512 --ubatch-size 512
検証2:KVキャッシュ量子化の組み合わせ比較
背景
KVキャッシュの量子化を下げると帯域削減でgeneration速度が上がる可能性があるという仮説を検証した。context長が長くなるほどKVキャッシュの読み書きがボトルネックになりやすい。
テスト条件
--n-prompt 1024 --n-gen 128(prompt処理速度とgeneration速度の両方を計測)--batch-size 512 --ubatch-size 512(検証1で確定した最適値)- cache-k/vをq8_0・q4_0・f16の全組み合わせ(9パターン)で計測
結果
| cache-k | cache-v | pp t/s | tg t/s |
|---|---|---|---|
| q8_0 | q8_0 | 279.62 | 17.73 |
| f16 | f16 | 276.65 | 17.58 |
| q4_0 | q4_0 | 276.14 | 17.57 |
| f16 | q8_0 | 219.97 | 15.24 |
| f16 | q4_0 | 219.23 | 15.13 |
| q4_0 | q8_0 | 221.91 | 14.46 |
| q8_0 | f16 | 237.26 | 13.30 |
| q4_0 | f16 | 227.33 | 12.86 |
| q8_0 | q4_0 | 221.20 | 12.65 |
考察
現在の設定(q8_0/q8_0)が最速だった。「q4_0に下げれば速くなる」というGeminiの提案は、少なくともVulkan環境では当てはまらなかった。
重要な発見として、KとVで異なる型を混在させた非対称な組み合わせが軒並み遅いことが明らかになった。q4_0/q4_0やf16/f16のように対称な組み合わせは速く、非対称(例:q8_0/q4_0)になると大幅に遅くなる。これはCUDAとは異なる挙動で、Vulkanバックエンド特有の制約と考えられる。
また、f16/f16はq8_0/q8_0とほぼ同速度だった。品質を最優先にする場合はf16/f16も選択肢になる。
変更なし
KVキャッシュはq8_0/q8_0が最適と判断し、設定変更は行わなかった。
最終的なsystemd設定
[Unit]
Description=llama.cpp server
After=network.target
[Service]
Type=simple
User=<user>
WorkingDirectory=/home/<user>/src/llama.cpp
ExecStart=/home/<user>/src/llama.cpp/build-vulkan/bin/llama-server \
-m /home/<user>/src/llama.cpp/models/Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf \
-ngl 99 \
-n -1 -c 65536 \
--flash-attn on \
--cache-type-k q8_0 --cache-type-v q8_0 \
--temp 1.0 --top-k 20 --top-p 0.95 \
--min-p 0.0 --presence-penalty 0.0 --repeat-penalty 1.0 \
--jinja \
--reasoning-format none \
--batch-size 512 --ubatch-size 512 \
--host 0.0.0.0 --port 11434
Environment=GGML_VK_DEVICE=0
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
パラメータ設定について
Unsloth公式ドキュメントが推奨するQwen3.5の設定と現在の設定の比較。
| パラメータ | Unsloth推奨(一般チャット) | Unsloth推奨(コーディング) | 現在の設定 |
|---|---|---|---|
| temp | 1.0 | 0.6 | 1.0 |
| top_p | 0.95 | 0.95 | 0.95 |
| top_k | 20 | 20 | 20 |
| min_p | 0.0 | 0.0 | 0.0 |
| presence_penalty | 1.5 | 0.0 | 0.0 |
| repeat_penalty | 1.0 | 1.0 | 1.0 |
| reasoning-format | deepseek | deepseek | none |
コーディングエージェント(Claude Code / OpenCode)として主に使用しているため、presence_penalty=0.0はコーディング推奨値に合っている。reasoning-format noneは意図的な設定で、<think>トークンをそのまま出力に含める形にしている。
参考:Qwen3.5-27B(Dense)との比較
35B-A3Bの検証後、比較目的でQwen3.5-27B-Q4_K_M.ggufでも同じベンチマークを実施した。
検証1:batch-size比較(27B)
| batch-size | ubatch-size | pp t/s |
|---|---|---|
| 128 | 128 | 80.06 |
| 128 | 256 | 79.97 |
| 128 | 512 | 79.97 |
| 256 | 128 | 80.28 |
| 256 | 256 | 83.07 |
| 256 | 512 | 83.14 |
| 512 | 128 | 80.61 |
| 512 | 256 | 83.41 |
| 512 | 512 | 82.03 |
35B-A3Bでは128/128→512/512で28%向上したのに対し、27Bでは最大でも4%程度しか改善しなかった。Denseモデルはbatch-sizeの恩恵を受けにくい。
検証2:KVキャッシュ比較(27B)
| cache-k | cache-v | pp t/s | tg t/s |
|---|---|---|---|
| q8_0 | q8_0 | 81.56 | 4.18 |
| f16 | f16 | 81.32 | 4.15 |
| q4_0 | q4_0 | 81.11 | 4.16 |
| q4_0 | f16 | 70.65 | 3.61 |
| q8_0 | f16 | 70.68 | 3.63 |
| q4_0 | q8_0 | 66.31 | 3.79 |
| q8_0 | q4_0 | 66.30 | 3.69 |
| f16 | q8_0 | 64.85 | 3.52 |
| f16 | q4_0 | 64.85 | 3.52 |
「対称なら速い、非対称なら遅い」というVulkanの特性は27Bでも完全に再現された。モデル依存ではなくVulkanバックエンド固有の特性と確認できた。
35B-A3B vs 27B 総合比較
| 35B-A3B(MoE) | 27B(Dense) | |
|---|---|---|
| モデルサイズ | 19.16 GiB | 15.58 GiB |
| pp t/s | 279.62 | 81.56 |
| tg t/s | 17.73 | 4.18 |
| KVキャッシュ最適 | q8_0/q8_0 | q8_0/q8_0 |
生成速度(tg)が4.18 t/sは実用限界以下。HX370のUMA帯域幅がDenseモデルの全27Bパラメータ読み出しに追いつかない。Unslothのドキュメントには「27Bの方がやや高精度」とあるが、速度差(約4倍)を考えるとHX370では35B-A3B一択という結論になった。
ベンチマークスクリプト
検証1:batch-size / ubatch-sizeの比較
cd ~/src/llama.cpp
./build-vulkan/bin/llama-bench \
-m ./models/Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf \
--n-prompt 1024 \
--n-gen 0 \
--batch-size 128,256,512 \
--ubatch-size 128,256,512 \
--n-gpu-layers 99 \
--flash-attn 1
検証2:KVキャッシュ量子化の全組み合わせ比較
cd ~/src/llama.cpp
for k in q8_0 q4_0 f16; do
for v in q8_0 q4_0 f16; do
echo "=== cache-k=$k cache-v=$v ==="
./build-vulkan/bin/llama-bench \
-m ./models/Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf \
--n-prompt 1024 \
--n-gen 128 \
--batch-size 512 \
--ubatch-size 512 \
--n-gpu-layers 99 \
--flash-attn 1 \
--cache-type-k $k \
--cache-type-v $v
done
done
結果としては、すでに利用していたパラメータで問題ないということがわかっただけだが数値的なものが出ていると納得感がでてくる。次回はもう少し別のモデルでも比較をしてみたい。