0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Qwen3.8-27BとRX9060xt16GBx2で40t/s

0
Last updated at Posted at 2026-08-30

RX 9060 XT ×2 + ROCm 10 + llama.cpp + Qwen3.8 27Bで40 tok/s超を出すまで

はじめに

RX 9060 XT 16GBを2枚使って、Qwen3.8 27Bをできるだけ高速に動かしたくなりました。

最終的には、

  • RX 9060 XT 16GB ×2
  • ROCm 10
  • llama.cpp
  • Qwen3.8 27B Q4_K_M
  • tensor split
  • MTP
  • PCIe P2P

という構成で、

平均生成速度: 42.20 tok/s
瞬間速度:     46.71 tok/s

まで到達しました。

最初の素のベンチは、

13.69 tok/s

だったので、最終的には約3.08倍です。

ただし、ここまで来るまでにかなり苦労しました。

この記事では、成功した手順だけではなく、

  • WindowsネイティブROCm 10でクラッシュ
  • WSLでは2GPU認識するのに2枚目が落ちる
  • Ubuntuへ移行
  • Secure Bootでamdgpuがロード不能
  • P2Pが無効
  • MTPやGPU分割比率の調整

といった失敗も含めて順番にまとめます。


環境

今回の構成です。

項目 構成
CPU Ryzen 7 3700X
Motherboard MSI MEG X570 UNIFY
GPU Radeon RX 9060 XT 16GB ×2
GPU arch gfx1200
OS Ubuntu 24.04
Kernel 7.0.0-30-generic
ROCm ROCm Core 10.0
amdgpu DKMS 7.1.3-2390945.24.04
llama.cpp 0.3.0-dev
llama.cpp build bebc9350e / build 10700
Model Qwen3.8 27B
Quant Q4_K_M
GGUF size 15.65 GiB

使用したGGUFはこちらです。

OBLITERATUS/Qwen3.8-27B-OBLITERATED
Qwen3.8-27B-OBLITERATED-Q4_K_M.gguf

Step 1. 最初はWindowsネイティブROCm 10を試した

ROCm 10が出たので、まずWindowsネイティブでTheRock版ROCm 10を導入しました。

ROCm自体は正常にRX 9060 XTを2枚認識しました。

Device 0: AMD Radeon RX 9060 XT, gfx1200
Device 1: AMD Radeon RX 9060 XT, gfx1200

llama.cppもROCm 10向けにビルドできました。

しかしモデルをロードすると、

cudaMemGetInfo failed (invalid argument)

が発生し、最終的に、

-1073741819

つまり、

0xC0000005
Access Violation

でクラッシュ。

1GPUに限定しても同じでした。

Flash Attentionを無効にしても、

-fa off

warmupを切っても、

--no-warmup

改善なし。

AMDドライバもROCm 10検証系の26.6.4へ合わせましたが結果は変わりませんでした。

Windowsネイティブは一旦断念

この時点で、

ROCm 10 + Windows + llama.cpp + gfx1200

の組み合わせ自体がまだかなり不安定だと判断しました。


Step 2. WSL2を試す

次に、

Windows 11
↓
WSL2
↓
Ubuntu 24.04
↓
ROCm 10

を試しました。

WSLではROCDXGを利用します。

ROCm 10を導入すると、

Agent 2
  Name: gfx1200
  Marketing Name: AMD Radeon RX 9060 XT

Agent 3
  Name: gfx1200
  Marketing Name: AMD Radeon RX 9060 XT

と、2枚とも認識。

さらにllama.cppでも、

ggml_cuda_init: found 2 ROCm devices

ROCm0: AMD Radeon RX 9060 XT
ROCm1: AMD Radeon RX 9060 XT

まで通りました。

これは期待できそうでした。


Step 3. WSLでモデルをロードすると2枚目のGPUが落ちる

Qwen3.8 27Bをロードすると、

load_tensors: offloaded 66/66 layers to GPU

ROCm0 model buffer size = 7898.69 MiB
ROCm1 model buffer size = 7189.63 MiB

と、66/66レイヤーを完全GPUオフロードできました。

しかし、

load_all_data: using async uploads for device ROCm0
load_all_data: using async uploads for device ROCm1

あたりでAMDドライバエラー。

GPU0には約9GB残っているのに、

GPU1 VRAM = 0GB

となりました。

WSLでは2GPUを列挙できても、実際のmGPU負荷で問題が発生しました。

ここでWSLで粘るより、

ネイティブLinuxへ移行することにしました。


Step 4. Windowsとのデュアルブート構成へ

幸いPCにはSSDが2台ありました。

Disk 0: 1TB
  └ Windows 11

Disk 1: 512GB
  └ Ubuntu

という構成にしました。

Windows SSDを触らずにUbuntuを別SSDへ入れられるので、かなり安全です。

最終的には、

Windows 11
Ubuntu 24.04

のデュアルブート環境になりました。


Step 5. ネイティブUbuntuへROCm 10を導入

まずAMD公式のamdgpu-installを利用。

wget https://repo.radeon.com/amdgpu-install/31.50/ubuntu/noble/amdgpu-install_31.50.315000-1_all.deb

sudo apt install -y \
  ./amdgpu-install_31.50.315000-1_all.deb

ROCm + graphicsを導入します。

sudo amdgpu-install \
  --usecase=rocm,graphics \
  --gfxversion=auto \
  -y

ユーザーをGPUグループへ追加。

sudo usermod -a -G render,video "$USER"

再起動。


Step 6. llama.cppビルド用のROCm 10開発環境

最初はここでもハマりました。

CMakeすると、

The ROCm root directory:

 /opt/rocm

does not contain the HIP runtime CMake package

そして、

hip-lang-config.cmake

が見つからない。

ROCm runtimeだけではなく、開発パッケージが必要でした。

gfx1200用ROCm 10開発環境を追加。

sudo apt install -y amdrocm-core-dev10.0-gfx1200

確認。

find /opt/rocm \
  -name 'hip-lang-config.cmake' \
  -o -name 'hip-config.cmake'

結果:

/opt/rocm/core-10.0/lib/cmake/hip/hip-config.cmake
/opt/rocm/core-10.0/lib/cmake/hip-lang/hip-lang-config.cmake

Step 7. llama.cppをROCm 10 + gfx1200向けにビルド

環境変数。

export ROCM_PATH=/opt/rocm/core-10.0
export HIP_PATH=/opt/rocm/core-10.0
export PATH="$ROCM_PATH/bin:$ROCM_PATH/lib/llvm/bin:$PATH"
export LD_LIBRARY_PATH="$ROCM_PATH/lib:$ROCM_PATH/lib64:${LD_LIBRARY_PATH:-}"

llama.cpp取得。

cd ~
git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp

ビルド。

HIP_LLVM="$ROCM_PATH/lib/llvm/bin"

cmake -S . -B build-rocm10 -G Ninja \
  -DGGML_HIP=ON \
  -DGPU_TARGETS=gfx1200 \
  -DCMAKE_HIP_COMPILER="$HIP_LLVM/clang" \
  -DCMAKE_PREFIX_PATH="$ROCM_PATH" \
  -DCMAKE_BUILD_TYPE=Release \
  -DLLAMA_BUILD_TESTS=OFF

cmake --build build-rocm10 -j "$(nproc)"

GPU確認。

./build-rocm10/bin/llama-bench --list-devices

結果:

ggml_cuda_init: found 2 ROCm devices

Device 0: AMD Radeon RX 9060 XT
Device 1: AMD Radeon RX 9060 XT

ROCm0: AMD Radeon RX 9060 XT
ROCm1: AMD Radeon RX 9060 XT

ネイティブLinuxではきれいに2枚とも認識しました。


Step 8. まず素のllama-bench

モデルを、

MODEL="$HOME/models/Qwen3.8-27B-OBLITERATED-Q4_K_M.gguf"

としてベンチ。

./build-rocm10/bin/llama-bench \
  -m "$MODEL" \
  -ngl 99 \
  -p 512 \
  -n 256 \
  -r 5 \
  -sm layer

結果:

pp512 = 499.15 ± 35.46 t/s
tg256 = 13.69 ± 0.68 t/s

正直、

「思ったより遅い……」

という結果でした。

しかし、これはMTPを使わない通常decodeの速度です。


Step 9. Qwen3.8のMTPを有効化

今回のGGUFには、

qwen35.nextn_predict_layers = 1

があり、MTPを利用できます。

llama-serverを、

./build-rocm10/bin/llama-server \
  -m "$MODEL" \
  -ngl 99 \
  -sm layer \
  -ts 0.4,0.6 \
  -c 8192 \
  -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

で起動。

結果:

predicted_per_second = 21.33 t/s
draft_n = 252
draft_n_accepted = 170

Acceptanceは約67.5%。

MTPだけで、

13.69
↓
21.33 t/s

約1.56倍になりました。


Step 10. layer splitからtensor splitへ

ここが大きな転換点でした。

最初は、

-sm layer

でした。

これを、

-sm tensor

へ変更。

./build-rocm10/bin/llama-server \
  -m "$MODEL" \
  -ngl 99 \
  -sm tensor \
  -ts 0.4,0.6 \
  -c 8192 \
  -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

すると、

30.47 t/s

まで上昇。

21.33
↓
30.47 t/s

**+43%**です。

2枚GPUではtensor splitがかなり効きました。


Step 11. MTPのn-maxを調整

次に、

--spec-draft-n-max

を比較。

n-max = 4

29.29 t/s
Acceptance 55.70%
mean len 3.23

n-max = 3

30.47 t/s
Acceptance 62.64%
mean len 2.87

n-max = 2

31.54 t/s
Acceptance 72.60%
mean len 2.45

今回の構成では、

n-max = 2

が明確に最速でした。

長くdraftするほど良いわけではなく、外れたdraftのコストが効きます。


Step 12. GPU分割比率を最適化

次に、

-ts

を調整。

40:60

-ts 0.4,0.6

結果:

31.54 t/s
Acceptance 72.60%

45:55

-ts 0.45,0.55

結果:

32.81 t/s
Acceptance 75.37%
tg_3s 36.91 t/s

50:50

-ts 0.5,0.5

結果:

30.18 t/s
Acceptance 67.28%

意外にも50:50ではなく、

45:55

が最速でした。

Ubuntuの画面表示をGPU0側が担当していることも影響している可能性があります。


Step 13. P2Pを調査

起動ログには毎回、

internal AllReduce init failed
falling back to meta-backend butterfly

という警告が出ていました。

そこでGPU間P2Pを調査。

最初のamd-smi topologyは、

ACCESS TABLE:

GPU0 -> GPU1 DISABLED
GPU1 -> GPU0 DISABLED

でした。

PCIe構成自体は、

GPU0: CPU Root Port 03.1
GPU1: CPU Root Port 03.2

で、両方CPU直結。

カーネル側も、

CONFIG_PCI_P2PDMA=y
CONFIG_HSA_AMD_P2P=y
CONFIG_DMABUF_MOVE_NOTIFY=y

さらに、

cat /sys/module/amdgpu/parameters/pcie_p2p

は、

Y

でした。


Step 14. Resizable BARを確認

BIOSで、

Above 4G Decoding
Resizable BAR
IOMMU

を有効化。

確認すると、

BAR 0: current size: 16GB

両方とも16GB BARになりました。

GPU0 BAR0 = 16GB
GPU1 BAR0 = 16GB

ここは正常。


Step 15. BIOS変更後、突然GPUドライバが消える

ここでまたハマりました。

Ubuntuを起動すると、

/dev/kfd

が消え、

/dev/dri/renderD*

も存在しない。

HIPテストも、

devices = 0

になりました。

Windows側ではGPUは正常。

つまりハード故障ではありません。

確認すると、

sudo modprobe amdgpu

で、

Key was rejected by service

原因はSecure Bootでした。

DKMS自体は、

amdgpu/7.1.3-2390945.24.04,
7.0.0-30-generic,
x86_64:
installed

なのに、Secure Bootがamdgpuモジュールを拒否していました。

BIOS側でSecure Bootを無効化。

その結果GPUは復活しました。


Step 16. HIPでP2Pを直接確認

簡単なHIPプログラムを作りました。

#include <hip/hip_runtime.h>
#include <cstdio>

int main() {
    int n = 0;
    hipError_t err = hipGetDeviceCount(&n);

    printf("devices = %d, %s\n",
           n,
           hipGetErrorString(err));

    for (int i = 0; i < n; i++) {
        hipDeviceProp_t p{};
        hipGetDeviceProperties(&p, i);
        printf("GPU%d: %s\n", i, p.name);
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (i == j) continue;

            int can = -1;

            err = hipDeviceCanAccessPeer(
                &can,
                i,
                j
            );

            printf(
                "GPU%d -> GPU%d: canAccessPeer=%d (%s)\n",
                i,
                j,
                can,
                hipGetErrorString(err)
            );

            if (can) {
                hipSetDevice(i);

                err = hipDeviceEnablePeerAccess(
                    j,
                    0
                );

                printf(
                    "  enablePeerAccess: %s\n",
                    hipGetErrorString(err)
                );
            }
        }
    }
}

コンパイル。

hipcc /tmp/hip-p2p.cpp \
  -o /tmp/hip-p2p

結果:

devices = 2, no error

GPU0: AMD Radeon RX 9060 XT
GPU1: AMD Radeon RX 9060 XT

GPU0 -> GPU1:
canAccessPeer=1
enablePeerAccess: no error

GPU1 -> GPU0:
canAccessPeer=1
enablePeerAccess: no error

ついに、

GPU0 ⇔ GPU1の双方向P2Pが有効

になりました。


Step 17. P2Pをllama.cppで利用

環境変数を追加。

export HSA_FORCE_FINE_GRAIN_PCIE=1
export GGML_CUDA_P2P=1

そして現時点の最適設定。

./build-rocm10/bin/llama-server \
  -m "$MODEL" \
  -ngl 99 \
  -sm tensor \
  -ts 0.45,0.55 \
  -c 8192 \
  -fa on \
  --spec-type draft-mtp \
  --spec-draft-n-max 2 \
  --spec-draft-p-min 0 \
  --parallel 1 \
  --jinja \
  --host 127.0.0.1 \
  --port 8080

Step 18. ついに40 tok/sへ

256-token生成では、

eval time = 6487.26 ms / 256 tokens

39.31 tokens per second

途中値は、

tg = 39.51 t/s
tg_3s = 43.21 t/s

MTP acceptanceは、

153 accepted / 203 generated
75.37%

ここでほぼ40 t/sへ到達。


Step 19. 長めの生成では42 tok/s超

さらに長めに生成すると、

n_gen = 115
tg = 37.29 t/s

n_gen = 242
tg = 39.42 t/s

n_gen = 384
tg = 41.84 t/s

n_gen = 518
tg = 42.53 t/s

n_gen = 654
tg = 42.95 t/s

最終結果:

eval time =
17606.96 ms / 744 tokens

23.70 ms per token

42.20 tokens per second

瞬間速度:

tg_3s = 46.71 t/s

MTP:

draft acceptance =
0.80810

459 accepted / 568 generated

mean len = 2.62

最終的に、

42.20 tok/s

まで到達しました。


最終結果まとめ

最初から比較するとこうなります。

設定 生成速度
llama-bench / MTPなし 13.69 t/s
layer + MTP 21.33 t/s
tensor + MTP3 30.47 t/s
tensor + MTP2 / 40:60 31.54 t/s
tensor + MTP2 / 45:55 32.81 t/s
P2P有効化後 39.31 t/s
長めの実生成 42.20 t/s
瞬間最大 46.71 t/s

最初の、

13.69 t/s

から、

42.20 t/s

なので、

約3.08倍

になりました。


最終設定

現在使っている設定はこちらです。

export ROCM_PATH=/opt/rocm/core-10.0
export HIP_PATH=/opt/rocm/core-10.0

export PATH="$ROCM_PATH/bin:$PATH"

export LD_LIBRARY_PATH="$ROCM_PATH/lib:$ROCM_PATH/lib64:${LD_LIBRARY_PATH:-}"

export HSA_FORCE_FINE_GRAIN_PCIE=1
export GGML_CUDA_P2P=1

MODEL="$HOME/models/Qwen3.8-27B-OBLITERATED-Q4_K_M.gguf"

cd ~/llama.cpp

./build-rocm10/bin/llama-server \
  -m "$MODEL" \
  -ngl 99 \
  -sm tensor \
  -ts 0.45,0.55 \
  -c 8192 \
  -fa on \
  --spec-type draft-mtp \
  --spec-draft-n-max 2 \
  --spec-draft-p-min 0 \
  --parallel 1 \
  --jinja \
  --host 127.0.0.1 \
  --port 8080

起動スクリプト化

毎回入力するのは面倒なので、例えば、

nano ~/start-qwen38.sh

として、

#!/usr/bin/env bash

export ROCM_PATH=/opt/rocm/core-10.0
export HIP_PATH=/opt/rocm/core-10.0

export PATH="$ROCM_PATH/bin:$PATH"

export LD_LIBRARY_PATH="$ROCM_PATH/lib:$ROCM_PATH/lib64:${LD_LIBRARY_PATH:-}"

export HSA_FORCE_FINE_GRAIN_PCIE=1
export GGML_CUDA_P2P=1

MODEL="$HOME/models/Qwen3.8-27B-OBLITERATED-Q4_K_M.gguf"

cd "$HOME/llama.cpp"

exec ./build-rocm10/bin/llama-server \
  -m "$MODEL" \
  -ngl 99 \
  -sm tensor \
  -ts 0.45,0.55 \
  -c 8192 \
  -fa on \
  --spec-type draft-mtp \
  --spec-draft-n-max 2 \
  --spec-draft-p-min 0 \
  --parallel 1 \
  --jinja \
  --host 127.0.0.1 \
  --port 8080

保存。

chmod +x ~/start-qwen38.sh

以後は、

~/start-qwen38.sh

だけです。


APIから動作確認

curl -s \
  http://127.0.0.1:8080/completion \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt":
      "Write a Python program that implements a fast LRU cache.",
    "n_predict": 512,
    "temperature": 0
  }' \
  > /tmp/qwen38.json

速度確認。

python3 - <<'PY'
import json

d = json.load(
    open("/tmp/qwen38.json")
)

t = d["timings"]

print(
    "speed:",
    t["predicted_per_second"],
    "tok/s"
)

print(
    "draft:",
    t.get("draft_n")
)

print(
    "accepted:",
    t.get("draft_n_accepted")
)

if t.get("draft_n"):
    print(
        "acceptance:",
        t["draft_n_accepted"]
        / t["draft_n"] * 100,
        "%"
    )
PY

生成された文章を見るなら、

python3 - <<'PY'
import json

d = json.load(
    open("/tmp/qwen38.json")
)

print(d["content"])
PY

今回分かったこと

1. MTPは非常に重要

Qwen3.8ではMTPの有無で、

13.69
↓
21~40+ tok/s

と、大きな差が出ました。

ただし、

n-maxを大きくすれば速い

という単純な話ではありません。

今回の場合、

n-max=2

が最速でした。


2. multi-GPUではsplit modeが非常に重要

layer

より、

tensor

の方がかなり高速でした。

今回、

21.33
↓
30.47 tok/s

まで改善。

GPUを2枚挿しただけでは性能は出ません。


3. 50:50が最速とは限らない

同じ9060 XTを2枚使っていても、

50:50

より、

45:55

の方が高速でした。

ディスプレイ出力やVRAM使用量なども影響している可能性があります。


4. P2Pはかなり重要

P2P有効化後、

32.81
↓
39~42 t/s

まで伸びました。

少なくとも今回の構成では、ここが最後の大きなブレイクスルーでした。


5. Secure Bootには注意

今回一番焦ったのがこれ。

突然、

devices = 0

になり、

/dev/kfd

も消滅。

原因は、

modprobe: ERROR:
could not insert 'amdgpu':
Key was rejected by service

でした。

つまりSecure BootによるDKMS署名拒否。

ROCmを再インストールする前に、

sudo modprobe amdgpu

を試すのがおすすめです。


最後に

最初は、

ROCm 10にすれば少し速くなるかな?

くらいの気持ちでした。

しかし実際には、

Windowsネイティブでクラッシュ
↓
WSLへ
↓
2GPU目が落ちる
↓
Ubuntuデュアルブート
↓
ROCm開発環境不足
↓
llama.cppビルド
↓
13.69 t/s
↓
MTP
↓
tensor split
↓
split比率調整
↓
P2P調査
↓
Secure Bootでドライバ死亡
↓
P2P復旧
↓
42.20 t/s

という、かなり長い道のりになりました。

それでも最終的には、

RX 9060 XT 16GB ×2
Qwen3.8 27B Q4_K_M
ROCm 10
llama.cpp

という比較的コンシューマ向けの構成で、

平均42 tok/s超

まで出せました。

特にQwen3.8のMTPとllama.cppのtensor splitは効果が非常に大きく、設定次第で同じハードウェアでも3倍近く速度が変わるというのが今回一番面白かったところです。

現在の設定で十分速いため、ひとまずここで完成とします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?