双 R9700 跑 Qwen3.8-27B:vllm-radiance 部署与 128K/256K 全场景实测
看了论坛里的大神 paul hou 的分享,也发一个双9700的帖子。ai帮我总结的,大家可以 参考一下。我是小白。质量不好,大家见谅。
2026-09-12 · 双卡 Radeon AI PRO R9700 (gfx1201) · 单 TRX40 平台 · 实测数据
一、结论先行
- 双 R9700 TP2 + DFlash2 投机解码,单流文本 130–210 tok/s,128K 档 4 并发,KV 容量 59 万 token(约 4.5× 并发余量)
- 128K/C4 并发实测:推理场景 C4 聚合 249 t/s(C1 102 → 近 2.4× 扩展),重复 C4 154 / 散文 C4 81 t/s
- 256K 长上下文档:252K 满长度冷 prefill ≈2080 tok/s(121 秒),prefix cache 命中后 1.9 秒(快 63 倍)
- 三个标准场景(重复/推理/散文)全面打平或反超单卡 llama.cpp Vulkan (7900 XTX) 基线 97/62/39 tok/s
- 踩过的最大的坑:
:latest镜像静默回归,DFlash 接受率掉到 0.1% 且不报错,只能靠 pin 资格 build 的 digest 解决 - 模型自带视觉塔,图片/视频多模态输入直接可用
二、机器配置
| 项 | 规格 |
|---|---|
| CPU | AMD Ryzen Threadripper 3960X 24 核 48 线程(TRX40 平台,单 CPU,1 个 NUMA 节点) |
| 主板 | ASRock TRX40 |
| GPU(本部署) | 2× AMD Radeon AI PRO R9700 32GB(Gigabyte + XFX,gfx1201) |
| GPU(其他,不冲突) | RTX PRO 5000 48GB(跑 CUDA ComfyUI/sglang) |
| 系统 | Ubuntu 26.04(宿主 不装 ROCm,驱动栈全在容器内) |
| Docker | 29.7.2 + Compose v5.5.0 |
拓扑要点:两张 R9700 都挂在同一颗 CPU 的原生 PCIe lane(host bridge 00:03.1 / 00:03.2 兄弟关系),单 NUMA,没有跨 socket P2P 问题。启动自检输出 P2P access: ENABLED 0↔1 ✓,CPU
GPU 约 28 GB/s(PCIe 4.0 x16)。
三、方案与组件
- 镜像:
magiccodingman/vllm-radiance= vLLM v0.28 + ROCm 7.14 + libr4d(手写 RDNA4 kernel,专为 gfx1201 调优)- 必须 pin 资格 build:
@sha256:8df90677c0f1fb013d958184aa0bf24af91e34688b61b924fa4facd7da333430(详见坑 1)
- 必须 pin 资格 build:
- 目标模型:
amd/Qwen3.8-27B-Quark-AWQ-MXFP4(19.8GB,MXFP4 量化) - 投机解码 drafter:
tcclaviger/Qwen3.8-27B-DFlash2-FP8(2.1GB,FP8,K=7)- drafter 必须与目标模型配套(Quark-MXFP4 配 DFlash2-FP8;配错 NVFP4 版会接受率崩)
- 量化策略:MXFP4 权重 + W4A8 计算(低 M 值走 W8A8 子集)
四、启动参数
.env 关键项(128K/C4 生产档):
IMAGE=magiccodingman/vllm-radiance@sha256:8df90677... # pin 资格 build
MODELS=/home/liubo/models/vllm-models
MODEL_PATH=/models/Qwen3.8-27B-Quark-AWQ-MXFP4
SERVED_MODEL_NAME=qwen3.8-27b-vllm
# 容量档
MAX_MODEL_LEN=131072 # 128K 档;256K 档为 262144
MAX_NUM_SEQS=4 # 128K 档 4 并发;256K 档为 1
GPU_UTIL=0.90
# MXFP4 / W4A8
WEIGHT_QUANTIZATION=auto
RADIANCE_MXFP4=1
RADIANCE_MXFP4_W4A8=1
RADIANCE_MXFP4_W4A8_MIN_M=0
RADIANCE_MXFP4_DECODE_MAX_M=64
RADIANCE_MXFP4_TN4_MIN_M=2048
RADIANCE_MXFP4_WPERM=1 # WPERM + DECODE_NT 必须开(RX5-safe decode 子集)
RADIANCE_MXFP4_DECODE_NT=1
# DFlash2 投机解码
VLLM_USE_V2_MODEL_RUNNER=1
RADIANCE_COMPILATION_CONFIG='{"cudagraph_mode":"PIECEWISE"}'
RADIANCE_FAST_DRAFT=1
RADIANCE_SPECULATIVE_CONFIG='{"method":"dflash","model":"/models/Qwen3.8-27B-DFlash2-FP8","num_speculative_tokens":7,"draft_tensor_parallel_size":2,"attention_backend":"TRITON_ATTN","max_model_len":131072,"disable_padded_drafter_batch":true}'
端口 :8000,OpenAI 兼容 API。
五、部署过程
- 拉镜像:
docker pull magiccodingman/vllm-radiance@sha256:8df90677...(14.1GB,约 15 分钟)。注意docker compose pull对 digest 引用有 bug 会卡死,必须裸docker pull直拉。 - 下模型:
huggingface_hub snapshot_download拉两个仓库到~/models/vllm-models/(aria2 不支持 HF 逐文件-o,别用)。 - 写配置:目录
/home/liubo/vllm-radiance/放docker-compose.yml(官方 repo 拉取)+.env。 - 启动:
docker compose up -d。冷启动约 3 分钟(引擎 init 169s,其中 Triton kernel 编译 120s;有编译缓存后 <1 分钟)。 - 验收(黄金指标):
docker compose logs vllm | grep SpecDecoding- 正常:
Avg Draft acceptance rate: 78-91%,per-position0.93, 0.87, 0.81, 0.76, ... - 只看 health=200 / 出字正确是不够的——出字正确不代表投机解码生效(见坑 1)
- 正常:
日常运维:桌面快捷方式一键启停(128K / 256K 两档各一个 Start 图标 + 一个 Stop 图标,切档时 start 脚本自动停旧档起新档)。
六、测试结果
6.1 128K/C4 档(生产档)
| 指标 | 实测 |
|---|---|
| 单流文本 decode | 130–210 tok/s(引擎 generation 均值 ~147 tok/s) |
| DFlash2 接受率 | 78.5% / 91.4%(per-position 0.93 → 0.76),平均接受长度 6–7 |
| KV cache 容量 | 593,115 tokens ≈ 4.53× 并发余量(4 路并发长上下文不爆 KV) |
| 官方容量矩阵参考 | 单流 weighted 183 t/s,prose 118 t/s;c4 总吞吐 462,c8 523 |
128K/C4 三场景 × C1–C4 并发压测(2026-09-13,pin 8df90677,K7 DFlash,enable_thinking: false 纯 content 计数;每场景 docker compose down + up 清 cache + 单次 warmup 填 prefix cache,测聚合 decode t/s = C 路总吞吐):
| 场景 | 冷 prefill TTFT | C1 | C2 | C3 | C4 |
|---|---|---|---|---|---|
| 重复(~113K 输入) | 37.2s | 102.3 | 129.3 | 131.3 | 154.3 |
| 推理(120 题) | 1.8s | 102.4 | 156.8 | 198.2 | 249.2 |
| 散文(1200 节) | 33.4s | 45.2 | 60.4 | 73.4 | 81.4 |
读法:
- 推理是并发甜点:C1→C4 聚合吞吐 102→249 t/s(近 2.4× 线性扩展),KV 余量 4.5× 吃得住 4 路长上下文
- 重复场景 4 路 154 t/s 单流均 73 t/s,长 prompt 吃 KV 后单流被摊薄
- 散文 输出短(模型提前停 ~235 tok)+ prefill 重,是全场最低档;C4 聚合 81 t/s
- C4 全场景 16/16 请求成功,无 KV 爆、无超时——4 并发生产档稳
6.2 256K/C1 档三场景压测(与 llama.cpp 基线同协议)
每场景 docker compose down + up 清 prefix cache,保证冷 prefill 可复现。
| 场景 | 冷 prefill TTFT | 热 TTFT(prefix cache) | decode |
|---|---|---|---|
| 重复(满 252K 输入) | 121.0s(≈2080 tok/s) | 1.92s(快 63×) | 160.1 t/s |
| 推理(120 题) | 1.6s | 0.51s | 100.0 t/s |
| 散文(1200 节概括) | 33.1s | 0.97s | 46.3 t/s(231 tok 模型自认为答完提前停) |
6.3 对照 llama.cpp 单卡基线(RX 7900 XTX,Vulkan,Q6_K)
| 场景 | llama.cpp 单卡 | vllm-radiance 双卡 |
|---|---|---|
| 重复 | 97 t/s | 160 t/s |
| 推理 | 62 t/s | 100 t/s |
| 散文 | 39 t/s | 46 t/s |
三场景全面 ≥ 基线。256K 满长上下文下 2080 tok/s 的 prefill 吞吐和 prefix cache 63× 加速是这套部署最大的收益点。
七、踩过的坑(按疼痛程度排序)
1. :latest 静默回归——最坑
README 里的 183 tok/s / 60% 接受率是 pin 8df90677(8/25 资格 build)测的。:latest(当时 digest 83a9dc02)DFlash2 集成回归,接受率掉到 0.1% 但不报任何错,只变慢到 ~24 tok/s,症状等同于双卡在裸跑自回归。
教训:拉镜像/升级前必须先跑 SpecDecoding 指标确认接受率 60%+;出字正确 ≠ 投机解码生效。
2. drafter 必须 target-matched
Quark-MXFP4 目标配 DFlash2-FP8;配错(比如拿 RadixArk-NVFP4 那套 drafter)接受率直接崩。
3. DFlash2 是实验档
greedy 等价门禁未通过(tool-call 测试 98/100)。生产若在意工具调用严格性,退回 Fast MTP(qualified,单流 ~102 tok/s)或纯 non-spec。
4. 宿主机看不到 GPU——显存为 0 的假象
宿主 Ubuntu 没装 ROCm,rocm-smi 输出空、显存恒为 0,不代表服务没跑。必须进容器看:docker compose exec vllm rocm-smi --showuse。本部署冷启动前 2 分钟(kernel 编译期)GPU 占用也是真低,重启窗口期看到 0 属正常。
5. 别在容器内单独升 PyTorch / Triton / vLLM
它们是编译器栈,版本错配会导致持续 TP hang。作者明确警告,镜像内置 ROCm 7.14 + vLLM 0.28 原样用。
6. 思考模型的压测陷阱
Qwen3.8 是思考模型:推理类 prompt 的输出全走 reasoning_content 通道,content 字段为空。压测客户端只认 content 会误判"生成失败"(引擎日志明明接受了几百个 token)。客户端必须两个通道都计数,或请求里传 chat_template_kwargs: {"enable_thinking": false}。
7. vLLM 流式 API 的两个必须项
stream_options: {"include_usage": true}否则拿不到completion_tokens- SSE 要逐行读(
readline()),按块读会制造 decode 速度的假象(usage 块在流末尾,块缓冲会把窗口压扁)
8. prefix cache 污染
第二轮"冷 prefill"其实全命中缓存,数据作废。可靠清法 = 每场景 docker compose down + up(比 flush_cache API 彻底)。
9. docker compose pull 对 digest 引用会卡死
卡 12 分钟不动、网络 0 B/s。改用裸 docker pull repo@sha256:... 直拉。
10. 其他小坑速记
- 252K 输入要先用容器内
AutoTokenizer校准:直接估会超 262144 上限被 HTTP 400 拒(266,820 > 262,144) - 256K 档冷启动 kernel 编译 ~84s,health 要等 ~300s,别在窗口期 panic
- 远程 nohup 长任务:
setsid nohup ... < /dev/null &(不重定向 stdin 会挂住 SSH) - 绝对不要
pkill -f匹配含脚本名的模式——会命中 SSH 命令行自身,杀掉自己的会话,且同批的 scp 可能静默失败(远端还是旧版脚本) - 启动窗口关闭 = 停服务(trap 设计),所以切档/重启别用关窗,用 Stop 图标或
docker compose down
八、成本与代价
- 显存:双卡各占 ~30GB/32GB(KV 15.97 GiB/GPU @256K 档 + 权重 + drafter + CUDAGraph)
- 256K 档是单流档(MAX_NUM_SEQS=1),想要并发回 128K 档,两档桌面图标一键切换
- 冷启动 3–6 分钟(首次/清缓存后),日常热重启 <1 分钟(编译缓存已挂载持久化)
九、可复现清单
| 项 | 值 |
|---|---|
| 镜像 digest | sha256:8df90677c0f1fb013d958184aa0bf24af91e34688b61b924fa4facd7da333430 |
| 目标模型 | amd/Qwen3.8-27B-Quark-AWQ-MXFP4 |
| Drafter | tcclaviger/Qwen3.8-27B-DFlash2-FP8(K=7, TP2) |
| 128K 档 | MAX_MODEL_LEN=131072, MAX_NUM_SEQS=4, GPU_UTIL=0.90 |
| 256K 档 | MAX_MODEL_LEN=262144, MAX_NUM_SEQS=1, GPU_UTIL=0.90 |
| 验收指标 | SpecDecoding 接受率 78–91%;<30% = 投机解码失效 |