Windows + RX 9060 XT×2でQwen3.8-27BをOllamaからllama.cppへ移行した実測記録
VulkanではOllamaより遅く、ROCm/HIPでは最大28.68 tok/sを確認するまで
はじめに
Windows環境で、AMD Radeon RX 9060 XT 16GBを2枚使用し、Qwen3.8-27B をローカル推論した。
もともとの目的は単純で、
Ollamaからllama.cppへ変更すると推論速度が上がるのか
を実機で確認することだった。
最終的には、同じハードウェア・同じ unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL を使用して、
- Ollama:12.89 tok/s
- llama.cpp Vulkan:最大8.96 tok/s(MTP使用時)
- llama.cpp ROCm/HIP:28.68 tok/sを確認
- ROCm/HIPで別run:29.25 tok/sを確認
となった。
ただし、28〜29 tok/sは常に出る値ではない。MTP(Multi-Token Prediction)のdraft acceptanceによって速度が大きく変動した。
また、調査途中でベンチマーク方法を変更している。
この記事では、その点を隠さず、
llama-benchllama-server /completion- Ollama
/api/generate qwen38-mtp/probe.py
を使ったフェーズを分けて記録する。
実行環境
GPU
AMD Radeon RX 9060 XT × 2
VRAM: 16GB × 2
llama.cpp Vulkan版からは次のように認識された。
Available devices:
Vulkan0: AMD Radeon RX 9060 XT (16304 MiB, 15443 MiB free)
Vulkan1: AMD Radeon RX 9060 XT (16304 MiB, 15443 MiB free)
AMD公式のWindows HIP SDK対応表では、RX 9060 XTはRDNA4のgfx1200としてサポート対象になっている。
AMDドライバ
PowerShellで確認した値。
Get-CimInstance Win32_VideoController |
Select-Object Name, DriverVersion, DriverDate
結果:
Name DriverVersion DriverDate
---- ------------- ----------
AMD Radeon RX 9060 XT 32.0.31035.1003 2026/07/24
AMD Radeon RX 9060 XT 32.0.31035.1003 2026/07/24
PCIe
WindowsのPnPプロパティで確認した。
GPU 1
CurrentWidth = x16
MaxWidth = x16
CurrentSpeed = 5
MaxSpeed = 5
GPU 2
CurrentWidth = x16
MaxWidth = x16
CurrentSpeed = 5
MaxSpeed = 5
少なくともWindowsが報告するPCIeリンク幅・速度について、片方だけx4/x1等へ落ちている状態ではなかった。
llama.cpp
検証に使用したbuild:
build: 9d77fa172 (10488)
使用したWindows配布物:
llama-b10488-bin-win-vulkan-x64
llama-b10488-bin-win-rocm-7.14-x64
モデル
全検証の中心として使用したモデル:
unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL
llama-bench上では以下のサイズとして表示された。
size : 16.68 GiB
params : 27.32 B
なお、llama-benchのモデル名表示は、
qwen35 27B Q4_K - Small
となっていたが、実際に-hfで指定しているファイルは、
Qwen3.8-27B-UD-Q4_K_XL.gguf
だった。
きっかけ
当初はOllamaでコード生成時におよそ、
約10 tok/s
だった。
WindowsでOllamaからllama.cppへ変更して高速化した事例を調べたことから、llama.cppへの移行を試した。
最初に選択したbackendはVulkanだった。
理由は単純で、Windows版llama.cppのVulkan buildではRX 9060 XTが2枚とも即座に認識された一方、ROCm 7.14版では最初はGPUを認識しなかったためである。
1. Vulkanで2GPUを認識
.\llama-cli.exe --list-devices
結果:
Available devices:
Vulkan0: AMD Radeon RX 9060 XT (16304 MiB, 15443 MiB free)
Vulkan1: AMD Radeon RX 9060 XT (16304 MiB, 15443 MiB free)
そこで最初はVulkanで進めた。
2. Qwen3.8-27BをVulkanで起動
最初のサーバー起動例:
.\llama-server.exe `
-hf "unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL" `
-ngl 100 `
-sm layer `
-ts 1,1 `
-fa on `
-c 32768 `
--parallel 1 `
--host 127.0.0.1 `
--port 8080
モデル本体とmmproj-BF16.ggufが自動ダウンロードされ、サーバー自体は正常起動した。
model loaded
listening on http://127.0.0.1:8080
Qwen3.8の追加blockについて、
model has unused tensor blk.64....
という警告も出た。
後にMTPを明示的に有効化すると、このモデルに含まれるMTP用contextが使用可能であることを確認した。
3. 「GPUが1枚しか使われていない」と見えた問題
サーバー起動後に、
.\llama-cli.exe --list-devices
を実行すると、
Vulkan0: ... 7045 MiB free
Vulkan1: ... 15455 MiB free
となった。
この表示だけを見ると、Vulkan1にはモデルがロードされていないように見えた。
しかしWindowsのプロセス単位GPUカウンターを確認すると結果が異なった。
$pid_llama = (Get-Process llama-server).Id
Get-Counter '\GPU Process Memory(*)\Dedicated Usage' |
Select-Object -ExpandProperty CounterSamples |
Where-Object {$_.InstanceName -match "pid_$pid_llama"} |
Select-Object InstanceName,
@{N="VRAM_GB";E={[math]::Round($_.CookedValue/1GB,2)}}
結果:
pid_13100_luid_...06db7d1a... 8.25 GB
pid_13100_luid_...06dd78d9... 7.50 GB
異なる2つのLUIDにVRAM使用量が計上されていた。
したがって、少なくともWindows側ではllama-serverが2つのGPUアダプタにDedicated GPU Memoryを確保していた。
この時点で、
llama-cli --list-devicesのfree表示だけを見て「2枚目を使用していない」と判断するのは適切ではない
ことが分かった。
4. 最初のベンチマーク:llama-bench
ここから最初の速度測定を行った。
注意:この時点の測定方法
ここでは、
llama-bench
を使っている。
後半のllama-serverやprobe.pyとは測定方法が違うので、数値を完全な同条件比較として扱わない。
測定内容:
pp512 = Prompt Processing 512 tokens
tg256 = Token Generation 256 tokens
Vulkan / layer split / 1:1 / FA ON
.\llama-bench.exe `
-hf "unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL" `
-dev Vulkan0/Vulkan1 `
-ngl 999 `
-sm layer `
-ts 1/1 `
-fa on `
-p 512 `
-n 256 `
-r 1
結果:
pp512 = 27.91 tok/s
tg256 = 4.68 tok/s
これは、当初Ollamaで見ていた約10 tok/sより明確に遅かった。
Vulkan / layer / 自動split
-ts 1/1を外した。
結果:
pp512 = 26.58 tok/s
tg256 = 4.70 tok/s
ほぼ変わらなかった。
Vulkan / layer / FA OFF
pp512 = 26.19 tok/s
tg256 = 4.88 tok/s
Flash Attentionを無効化しても生成速度はほぼ同じだった。
Vulkan / tensor split
.\llama-bench.exe `
-hf "unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL" `
-dev Vulkan0/Vulkan1 `
-ngl 999 `
-sm tensor `
-ts 1/1 `
-fa on `
-p 512 `
-n 256 `
-r 1
結果:
pp512 = 45.56 tok/s
tg256 = 5.35 tok/s
Prompt Processingは大きく伸びた。
一方、Token Generationは、
4.68 → 5.35 tok/s
に留まった。
5. VulkanでMTPを有効化
ここで測定方法を変更した。
ベンチマーク方法変更①
それまで:
llama-bench
ここから:
llama-server
↓
HTTP /completion
↓
response.timings.predicted_per_second
を使用した。
したがって、llama-bench tg256と/completionのTPSは同じ測定器による値ではない。
MTPを有効化した。
--spec-type draft-mtp
--spec-draft-n-max N
サーバーログには、
creating MTP draft context against the target model
と表示された。
Vulkan / tensor + MTP
同じ256-token生成でn-maxを変更した。
| n-max | 生成速度 | Draft acceptance |
|---|---|---|
| 2 | 7.50 tok/s | 71.8% |
| 5 | 8.52 tok/s | 52.1% |
| 6 | 8.96 tok/s | 48.1% |
| 7 | 8.77 tok/s | 45.0% |
| 9 | 8.79 tok/s | 35.2% |
| 12 | 7.45 tok/s | 27.9% |
このVulkan環境ではn-max=6が測定した中で最速だった。
それでも、
8.96 tok/s
であり、Ollamaの約10 tok/sを上回らなかった。
6. Ollamaを同一GGUF・同一リクエストで測定
ここまでのOllama約10 tok/sという値は、普段のコード生成時に確認していた速度だった。
より厳密に比較するため、Ollamaにも同じUnsloth GGUFを読み込ませた。
hf.co/unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL
Unslothのモデルページでも、このGGUFをOllamaから直接使用する方法が案内されている。
測定条件:
model = UD-Q4_K_XL
num_ctx = 8192
num_predict = 256
temperature = 0
draft_num_predict = 6
PowerShell:
$model = "hf.co/unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL"
$body = @{
model = $model
prompt = "Write a Python program that implements a thread-safe LRU cache with detailed comments and example usage."
stream = $false
options = @{
num_ctx = 8192
num_predict = 256
temperature = 0
draft_num_predict = 6
}
} | ConvertTo-Json -Depth 5
結果:
Tokens = 256
Seconds = 19.87
TPS = 12.89
つまり、この時点の同一GGUF比較では、
Ollama 12.89 tok/s
llama.cpp Vulkan + MTP 8.96 tok/s
となった。
この環境ではVulkan版llama.cppへの変更は高速化にならなかった。
7. ROCm/HIPの高速事例を知る
その後、2026年8月のコミュニティ情報として、
RX 9060 XT ×2
Qwen3.8-27B UD-Q4_K_XL
ROCm/HIP
layer split
MTP
で約30 tok/sという事例を参照した。
共有されていた設定は以下。
env:
- "HIP_VISIBLE_DEVICES=0,1"
cmd: |
llama-server
-hf unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL
-ngl 999
-sm layer
-ts 0.5,0.5
-c 131072
-ctk q8_0
-ctv q8_0
-fa on
--spec-type draft-mtp
--spec-draft-n-max 3
--jinja
ここで重要なのは、
-sm layer
-ts 0.5,0.5
は「tensor parallel」ではないという点。
llama.cppのmulti-GPU仕様では、
-sm layer
がlayer/pipeline parallelismで、
-ts 0.5,0.5
は各GPUへ割り当てる比率。
tensor parallelismそのものは、
-sm tensor
で指定する。
8. qwen38-mtpの公開データ
sudoingX/qwen38-mtpにはQwen3.8-27BのMTP測定結果が公開されている。
RX 9060 XT 16GBのコミュニティ行には、
Baseline : 15.2 tok/s
MTP : 28.7 tok/s
n-max : 2
Acceptance: 0.62 - 0.93
という値が掲載されていた。
これは今回のdual GPU構成と同一条件ではない。
ただし、
RX 9060 XT
ROCm
Qwen3.8
MTP
で高い速度が出る例が存在することを確認する材料になった。
9. Windows ROCm 7.14版が最初はGPUを認識しなかった
Vulkanでは即座に2GPUを認識したが、
llama-b10488-bin-win-rocm-7.14-x64
は最初GPUを認識しなかった。
同時期のllama.cpp Issue #26996では、Windows ROCm 7.14配布物について、
ggml-hip.dll
↓
hipblas.dll を要求
↓
配布zipに hipblas.dll が存在しない
↓
--list-devices が空になる
という問題が報告されていた。
Issueで確認されているbuildはb10400であり、今回使用したb10488と完全に同じbuildではない。
ただし、
ROCm 7.14版単体ではGPUを認識しない
という症状は一致していた。
10. WindowsにTheRock / ROCm 7.14 runtimeを追加
AMDのROCm 7.14 multi-arch tarballを使用した。
New-Item -ItemType Directory -Force C:\TheRock
New-Item -ItemType Directory -Force C:\TheRock\build
cd C:\TheRock
curl.exe -L `
-o therock-dist-windows-multiarch-7.14.0.tar.gz `
"https://repo.amd.com/rocm/tarball-multi-arch/therock-dist-windows-multiarch-7.14.0.tar.gz"
展開:
tar -xzf `
C:\TheRock\therock-dist-windows-multiarch-7.14.0.tar.gz `
-C C:\TheRock\build `
--strip-components=1
確認:
Get-ChildItem C:\TheRock\build -Recurse -Filter "hipblas.dll"
Get-ChildItem C:\TheRock\build -Recurse -Filter "rocblas.dll"
PATHへ追加:
$env:PATH = "C:\TheRock\build\bin;$env:PATH"
その後、hipInfo.exeとllama.cpp ROCm版の両方でRX 9060 XTを認識した。
$env:HIP_VISIBLE_DEVICES="0,1"
.\llama-cli.exe --list-devices
ここでWindows上のROCm/HIP 2GPU環境が使用可能になった。
11. ROCmで最初の速度測定
最初は切り分けのためcontext 8192で測定した。
.\llama-server.exe `
-hf "unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL" `
--no-mmproj `
-ngl 999 `
-sm layer `
-ts 0.5,0.5 `
-c 8192 `
-fa on `
--spec-type draft-mtp `
--spec-draft-n-max 3 `
--parallel 1 `
--jinja `
--host 127.0.0.1 `
--port 8080
同じ19-token prompt / 256-token生成で:
prompt_per_second = 57.19
predicted_per_second = 18.50
draft_n = 354
draft_n_accepted = 136
Acceptance:
136 / 354 = 38.4%
ここで初めて、
llama.cpp ROCm 18.50 tok/s
>
Ollama 12.89 tok/s
となった。
12. 元の128K設定へ寄せる
コミュニティ事例に合わせ、
-c 131072
-ctk q8_0
-ctv q8_0
を追加した。
結果:
predicted_per_second = 13.61 tok/s
draft_n = 467
draft_n_accepted = 98
Acceptance = 21.0%
つまり、自環境では128K設定にしただけでは30 tok/sにならなかった。
むしろ8K時の18.50 tok/sから低下した。
13. 8KのままKV cacheだけQ8_0へ変更
128KとQ8 KVを同時に変更していたため、原因を分離した。
context = 8192
K cache = q8_0
V cache = q8_0
結果:
predicted_per_second = 19.05 tok/s
draft_n = 344
draft_n_accepted = 139
Acceptance = 40.4%
つまり、
8K + F16 KV = 18.50 tok/s
8K + Q8 KV = 19.05 tok/s
だった。
この測定ではQ8 KV自体は速度低下の原因ではなかった。
128Kへ拡張した時の方が大きく速度が落ちていた。
14. ROCmでMTP n-maxを再探索
Vulkanで最適だったn-max=6をそのまま使わず、ROCmで再測定した。
条件:
ROCm
layer
0.5/0.5
context 8192
KV q8_0
結果:
| n-max | TPS | Acceptance |
|---|---|---|
| 2 | 17.69 | 34.2% |
| 3 | 19.05 | 40.4% |
| 4 | 13.13 | 19.6% |
| 5 | 14.27 | 25.3% |
この条件ではn-max=3が測定した中で最速だった。
Vulkanでn-max=6が最速だった結果とは異なる。
15. PCIeと2GPU使用率を確認
PCIeリンク:
GPU0: x16 / x16
GPU1: x16 / x16
CurrentSpeed = MaxSpeed = 5
推論中のWindows GPU Engineカウンターでは、
GPU LUID A
VRAM ≈ 8.506 GB
Compute ≈ 36 - 39%
GPU LUID B
VRAM ≈ 9.517 GB
Compute ≈ 50 - 53%
を確認した。
両GPUともROCm推論中にCompute Engineが動作していた。
16. layer split比率を変更
50:50から、
0.6 / 0.4
へ変更した。
使用コマンド:
.\llama-server.exe `
-hf "unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL" `
--no-mmproj `
-ngl 999 `
-sm layer `
-ts 0.60,0.40 `
-c 8192 `
-ctk q8_0 `
-ctv q8_0 `
-fa on `
--spec-type draft-mtp `
--spec-draft-n-max 3 `
--parallel 1 `
--jinja `
--host 127.0.0.1 `
--port 8080
ここからMTP acceptanceによる速度変動がはっきり見えるようになった。
同じ256-token系の実測ログには、
17.99 tok/s
Acceptance 36.5%
29.25 tok/s
Acceptance 81.1%
12.19 tok/s
Acceptance 13.8%
が含まれていた。
同じハードウェア・同じサーバー設定でも、MTPのacceptanceにより速度が大きく変化していた。
17. ベンチマーク方法変更②:qwen38-mtp probe.py
ここで再びベンチマーク方法を変更した。
それまで
/completion
1つの固定prompt
256-token生成
llama-serverが返すtimingsを使用
ここから
sudoingX/qwen38-mtpのprobe.pyを使用。
このprobeは、
/v1/chat/completions
stream = true
enable_thinking = false
を使用し、
3種類のpromptをそれぞれ3回実行する。
RUNS = 3
MAX_TOKENS = 400
各promptの中央値を出し、最後に全runのmeanとmedianを出力する。
つまり、ここからの値は前の/completion 256 tokenと同じ測定方法ではない。
18. probe.py / split 0.6,0.4
結果:
14.8 tok/s median
runs: [14.8, 14.7, 18.1]
11.6 tok/s median
runs: [11.6, 10.5, 22.3]
19.4 tok/s median
runs: [21.1, 16.4, 19.4]
全体:
OVERALL: mean 16.5 median 16.4
したがって、
29.25 tok/s
という単発値と、
probe.py overall median 16.4 tok/s
は両方実測値だが、同じ意味の数値ではない。
29.25は高acceptanceだった1回の256-token生成。
16.4は3prompt×3runのストリーミング測定中央値。
19. probe.py / split 0.5,0.5
確認できた途中結果:
10.0 tok/s median
runs: [10.0, 9.8, 18.2]
10.5 tok/s median
runs: [10.8, 0.0, 10.5]
その後、
KeyError: 'choices'
でprobe.pyが停止した。
したがって0.5/0.5については、このrunではOVERALL mean/medianを取得できなかった。
20. chat parser / 出力破損も発生
一部のprobe/chat実行では、
common_chat_peg_parse: unparsed peg-native output
が発生した。
実際のログにも文字化け・不正な出力が記録された。
したがって、この状態のrunについて速度値だけを採用して「高速化した」とは扱わなかった。
21. ROCm tensor splitも試した
layer以外に、
-sm tensor
も試した。
起動ログでは、
internal AllReduce init failed
falling back to meta-backend butterfly
さらに、
backend sampling not supported with SPLIT_MODE_TENSOR; using CPU
backend offload failed ... using CPU sampler
が出た。
実行例:
-sm tensor
-ts 0.5,0.5
および、
-sm tensor
-ts 0.6,0.4
を試した。
一部runでは、
7.06 tok/s
draft acceptance = 0%
となり、文字化けした出力も発生した。
このため、ROCm tensor modeは今回の有効な最高速結果には含めていない。
llama.cppのmulti-GPUドキュメントでもtensor modeはexperimentalとされている。
22. --spec-draft-p-min 0.5
MTPの無駄draftを減らす目的で、
--spec-draft-p-min 0.5
を試した。
実機では、
約8 tok/s
まで低下した。
この値はユーザー観測値であり、詳細なtimingsログは保存していない。
少なくとも今回の環境では、p-min=0.5を採用する理由になる結果は得られなかった。
23. 最終的に28.68 tok/sを確認した設定
p-minを0へ戻した。
使用コマンド:
.\llama-server.exe `
-hf "unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL" `
--no-mmproj `
-ngl 999 `
-sm layer `
-ts 0.6,0.4 `
--fit off `
-c 8192 `
-ctk q8_0 `
-ctv q8_0 `
-fa on `
--spec-type draft-mtp `
--spec-draft-n-max 3 `
--spec-draft-p-min 0 `
--parallel 1 `
--jinja `
--host 127.0.0.1 `
--port 8080
結果:
prompt_n : 19
prompt_per_second : 63.17
predicted_n : 256
predicted_ms : 8892.282
predicted_per_second : 28.6766
draft_n : 226
draft_n_accepted : 179
Acceptance:
179 / 226
= 79.2%
llama-serverログ:
eval time = 8892.28 ms / 256 tokens
28.68 tokens per second
draft acceptance = 0.79204
179 accepted / 226 generated
mean len = 3.36
この256-token測定では、
28.68 tok/s
を確認した。
別runでは、
29.25 tok/s
Acceptance 81.1%
も確認している。
24. MTP acceptanceと速度
今回のログでは、MTP acceptanceが高いrunほどTPSも高い例が多数確認された。
例えば:
| Acceptance | TPS |
|---|---|
| 5.8% | 10.02 |
| 13.8% | 12.19 |
| 36.5% | 17.99 |
| 37.6% | 18.02 |
| 53.9% | 22.21 |
| 57.7% | 23.31 |
| 79.2% | 28.68 |
| 81.1% | 29.25 |
これは今回取得したサンプルの対応関係であり、一般的な変換式を意味しない。
ただし、本環境で28〜29 tok/sが出たrunはいずれもMTP acceptanceが約80%だった。
25. backend samplingを無効化
最終基準設定に、
--no-spec-draft-backend-sampling
だけを追加した。
結果:
predicted_per_second = 24.22
draft_n = 270
draft_n_accepted = 165
Acceptance = 61.1%
28.68 tok/sを記録したrunより低かった。
サーバー起動時には、
ROCm1 does not have support for op TOP_K needed for sampler 'top-k'
という警告が出ていたが、今回の実測ではbackend samplingを明示的に無効化したrunの方が速くはならなかった。
26. MTP draft側KV cacheをQ8_0へ変更
次に、
--spec-draft-type-k q8_0
--spec-draft-type-v q8_0
だけを追加した。
結果:
predicted_per_second = 21.59
draft_n = 298
draft_n_accepted = 154
Acceptance = 51.7%
このrunも28.68 tok/sより低かった。
27. backend sampling無効 + draft KV Q8_0
両方を同時に指定した。
結果:
predicted_per_second = 21.49
draft_n = 301
draft_n_accepted = 154
Acceptance = 51.2%
今回測定した範囲では、
--no-spec-draft-backend-sampling
も、
draft KV q8_0
も、28.68 tok/sを記録した基準runを上回らなかった。
ベンチマーク方法を途中で変更した点のまとめ
この記事で最も重要な注意点の一つ。
Phase 1: llama-bench
使用した指標:
pp512
tg256
主にVulkan backendの基礎性能を見るために使用。
代表値:
Vulkan layer : tg256 4.68 tok/s
Vulkan tensor : tg256 5.35 tok/s
Phase 2: llama-server /completion
固定promptを投げ、
predicted_per_second
draft_n
draft_n_accepted
を測定。
主にMTPの調整に使用。
代表値:
Vulkan MTP6 : 8.96 tok/s
ROCm : 18.50 tok/s
ROCm Q8 KV : 19.05 tok/s
ROCm high-acceptance run:
28.68 tok/s
29.25 tok/s
Phase 3: Ollama /api/generate
同じUnsloth GGUFをOllamaから直接実行。
256 tokens
12.89 tok/s
これはOllamaとの比較をそれまでの「約10 tok/s」より厳密にするために行った。
Phase 4: qwen38-mtp probe.py
測定方法:
/v1/chat/completions
streaming
thinking disabled
3 prompts
×
3 runs
max_tokens = 400
0.6/0.4の結果:
OVERALL mean = 16.5 tok/s
OVERALL median = 16.4 tok/s
したがって、
28.68 tok/sと16.4 tok/sは矛盾している
わけではない。
測定対象が違う。
28.68:
固定prompt
256 token
高MTP acceptanceだった単発run
16.4:
3種類のprompt
各3回
最大400 token
streaming
全9 runのmedian
である。
主要結果一覧
llama-bench系
| Backend | Split | FA | 測定 | TPS |
|---|---|---|---|---|
| Vulkan | layer 1:1 | ON | pp512 | 27.91 |
| Vulkan | layer 1:1 | ON | tg256 | 4.68 |
| Vulkan | layer auto | ON | pp512 | 26.58 |
| Vulkan | layer auto | ON | tg256 | 4.70 |
| Vulkan | layer 1:1 | OFF | pp512 | 26.19 |
| Vulkan | layer 1:1 | OFF | tg256 | 4.88 |
| Vulkan | tensor 1:1 | ON | pp512 | 45.56 |
| Vulkan | tensor 1:1 | ON | tg256 | 5.35 |
llama-server / Ollama系
| 実行系 | 主な条件 | TPS |
|---|---|---|
| Ollama 普段のコード生成 | 初期観測 | 約10 |
| llama.cpp Vulkan | tensor + MTP6 | 8.96 |
| Ollama | 同一UD-Q4_K_XL / 8K / 256 tokens / MTP6 | 12.89 |
| llama.cpp ROCm | 8K / layer / MTP3 | 18.50 |
| llama.cpp ROCm | 8K / Q8 KV / layer / MTP3 | 19.05 |
| llama.cpp ROCm | 128K / Q8 KV / MTP3 | 13.61 |
| llama.cpp ROCm | 8K / Q8 / 0.6:0.4 / high acceptance | 29.25 |
| llama.cpp ROCm | 8K / Q8 / 0.6:0.4 / p-min 0 | 28.68 |
| ROCm + backend sampling無効 | 同じ256-token prompt | 24.22 |
| ROCm + draft KV Q8 | 同じ256-token prompt | 21.59 |
| ROCm + 上記2つ併用 | 同じ256-token prompt | 21.49 |
probe.py系
split 0.6 / 0.4
14.8 median
11.6 median
19.4 median
OVERALL mean 16.5
OVERALL median 16.4
split 0.5 / 0.5
途中まで:
10.0 median
10.5 median
その後、
KeyError: 'choices'
で停止。
OVERALL値なし。
最終的に確認できた事実
1. Vulkan版llama.cppはこの環境ではOllamaより遅かった
同一GGUFを使用した後半の比較では、
Ollama 12.89 tok/s
llama.cpp Vulkan + MTP 8.96 tok/s
だった。
したがって、
Ollamaからllama.cppに変更すれば必ず速くなる
とは言えない。
少なくとも、
Windows
RX 9060 XT ×2
Qwen3.8-27B UD-Q4_K_XL
llama.cpp b10488
Vulkan
ではOllamaを下回った。
2. 同じllama.cppでもROCm/HIPでは結果が大きく変わった
Vulkan + MTP : 8.96 tok/s
ROCm : 18.50~19.05 tok/s
まで上昇した。
高いMTP acceptanceになった256-token runでは、
28.68 tok/s
29.25 tok/s
も確認した。
3. Ollama 12.89 tok/sに対し、ROCmの28.68 tok/s runは約2.22倍
28.68 / 12.89 ≈ 2.22
ただし、28.68は単発の高acceptance runであり、全promptで常に出る値ではない。
4. probe.pyによる複数prompt測定では16.4 tok/s medianだった
split = 0.6 / 0.4
OVERALL median = 16.4 tok/s
したがって、
最高観測値 ≈ 29 tok/s
と、
複数prompt median = 16.4 tok/s
は分けて記録する必要がある。
5. 128Kで30 tok/sは今回の測定では再現できなかった
128K / Q8 KV / MTP3の256-token測定:
13.61 tok/s
だった。
今回約29 tok/sが出たのは8K contextでMTP acceptanceが約80%になったrunだった。
6. MTP acceptanceによる変動が大きかった
同じサーバー構成でも、
Acceptance 13.8% → 12.19 tok/s
Acceptance 36.5% → 17.99 tok/s
Acceptance 57.7% → 23.31 tok/s
Acceptance 79.2% → 28.68 tok/s
Acceptance 81.1% → 29.25 tok/s
という実測になった。
7. n-maxの最適値はbackendによって異なった
今回の実測では、
Vulkan → n-max 6が最速
ROCm → n-max 3が最速
だった。
8. p-min=0.5は今回の環境では遅くなった
観測値:
約8 tok/s
だった。
9. backend sampling無効化とdraft KV Q8化も最高値を更新しなかった
基準high-acceptance run 28.68 tok/s
backend sampling無効 24.22 tok/s
draft KV Q8 21.59 tok/s
両方 21.49 tok/s
10. ROCm tensor splitは今回採用しなかった
ログには、
AllReduce init failed
falling back to meta-backend butterfly
backend sampling not supported with SPLIT_MODE_TENSOR
using CPU sampler
が出た。
さらに出力破損も確認した。
そのため、この記事のROCm最高値はすべて、
-sm layer
で得た結果である。
現時点で28.68 tok/sを記録したコマンド
Windows PowerShell:
$env:PATH = "C:\TheRock\build\bin;$env:PATH"
$env:HIP_VISIBLE_DEVICES="0,1"
cd Downloads\llama-b10488-bin-win-rocm-7.14-x64
.\llama-server.exe `
-hf "unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL" `
--no-mmproj `
-ngl 999 `
-sm layer `
-ts 0.6,0.4 `
--fit off `
-c 8192 `
-ctk q8_0 `
-ctv q8_0 `
-fa on `
--spec-type draft-mtp `
--spec-draft-n-max 3 `
--spec-draft-p-min 0 `
--parallel 1 `
--jinja `
--host 127.0.0.1 `
--port 8080
256-token測定時:
28.6766 tok/s
Acceptance 79.2%
まとめ
今回の検証結果を一文にすると、
Windows + RX 9060 XT 16GB×2 + Qwen3.8-27B UD-Q4_K_XLでは、llama.cpp VulkanはOllamaより遅かったが、ROCm/HIPへ切り替えることでOllamaを上回り、高MTP acceptanceのrunでは約29 tok/sまで確認できた。
ただし、
29 tok/s = 常時性能
ではない。
qwen38-mtp/probe.pyで複数promptを測った0.6/0.4構成のoverall medianは、
16.4 tok/s
だった。
したがって今回の結果は、
最高観測値
定型promptの256-token速度
複数promptのmedian
を区別して扱う必要がある。
また、検証中に明確だったのは、
Ollama vs llama.cpp
というランタイム名だけで性能が決まるのではなく、
Vulkan / ROCm
multi-GPU split mode
split ratio
context size
KV cache形式
MTP n-max
MTP acceptance
によって結果が大きく変化したことだった。
参考資料
llama.cpp multi-GPU documentation
https://github.com/ggml-org/llama.cpp/blob/master/docs/multi-gpu.md
layerがpipeline parallel、tensorがexperimental tensor parallelであること、-tsがGPUごとの割当比率であることを確認できる。
llama.cpp speculative decoding documentation
https://github.com/ggml-org/llama.cpp/blob/master/docs/speculative.md
MTP speculative decodingの仕様。
llama.cpp server documentation
https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md
--spec-draft-p-min、backend sampling、draft device等のserver option。
Windows ROCm 7.14 hipblas.dll issue
https://github.com/ggml-org/llama.cpp/issues/26996
Windows ROCm 7.14版でhipblas.dll不足により--list-devicesが空になる問題報告。
AMD ROCm Windows system requirements
https://rocm.docs.amd.com/projects/install-on-windows/en/latest/reference/system-requirements.html
RX 9060 XT / gfx1200のWindows HIP SDK対応状況。
AMD ROCm multi-arch tarball
https://repo.amd.com/rocm/tarball-multi-arch/
今回TheRock / ROCm 7.14 runtime取得に使用。
Unsloth Qwen3.8-27B GGUF
https://huggingface.co/unsloth/Qwen3.8-27B-GGUF
UD-Q4_K_XLをllama.cppおよびOllamaから使用可能。
qwen38-mtp
https://github.com/sudoingX/qwen38-mtp
Qwen3.8-27BのMTPコミュニティ測定値とチューニング情報。
probe.py
https://github.com/sudoingX/qwen38-mtp/blob/master/probe.py
この記事後半で使用した複数prompt・streamingベンチマーク。
GPU割り当てを0.7,0.3では、片方のGPUが100%となり、13t/sまで低下。