双 R9700 跑 Qwen3.8-27B:vllm-radiance 部署与 128K/256K 全场景实测
-
双 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(当时 digest83a9dc02)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-MXFP4Drafter 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% = 投机解码失效 -
数据很扎实,补几个可以固化的点:
-
DFlash 接受率掉到 0.1% 且不报错,这是投机解码最危险的失效模式。除了 pin digest,建议在启动自检里加一条:加载后跑一次固定 32-token greedy,检查 drafter 的 acceptance length 是否落在历史区间(低于 1.5 就拒绝启动);镜像用 digest 而不是 tag,并把
docker inspect结果记进部署文档。 -
256K 满长冷 prefill 2080 t/s 能跑到,说明 59 万 token KV 下 gfx1201 的带宽还够;C4 聚合 2.4× 扩展也说明没撞带宽墙。不过你 256K 只测了 C1,有时间补一组「128K/C4 对 256K/C2」的 TTFT 对比,能区分瓶颈是 KV 扫描还是调度。
-
电源那条:长城 F16 1600W ATX3.1 带双 R9700 + TRX40 够用,但双卡瞬态可到约 1.2 kW,注意别把两张卡和 CPU 串在同一路 12V 上。
-