Featured image of post MINISFORUM HX370でQwen3.5-35B-A3Bをチューニングする:batch-sizeとKVキャッシュの最適化

MINISFORUM HX370でQwen3.5-35B-A3Bをチューニングする:batch-sizeとKVキャッシュの最適化

前回の記事で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

結果としては、すでに利用していたパラメータで問題ないということがわかっただけだが数値的なものが出ていると納得感がでてくる。次回はもう少し別のモデルでも比較をしてみたい。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。