- 硬件:4× NVIDIA RTX A6000 48GB(Ampere sm_86,无原生 FP8 Tensor Core)
- 服务框架:SGLang(镜像
lmsysorg/sglang:qwen38-27b),OpenAI 兼容 API
一、选型变迁
部署共经历四个阶段,目标从"先跑起来"逐步演进为"更快、更省卡"。
阶段 1:BF16 + TP4 + MTP(基线)
- 模型:
Qwen3.8-27B原始 BF16 权重(约 52 GB),4 卡 TP=4。 - 投机解码:模型自带 MTP 头,EAGLE 模式(steps=3, topk=1, draft tokens=4)。
- 结果:单流 decode 73.4 tok/s。权重占满 4 卡,KV 池 69 万 token。
- 问题:27B BF16 权重使 decode 强内存带宽受限,4 卡全被占用。
阶段 2:FP8 + TP4 + MTP(量化提速)
- 换用 FP8 量化权重
Qwen3.8-27B-FP8(约 29 GB,fp8 e4m3 block-128,lm_head保持 BF16)。 - 关键点:A6000 是 Ampere,没有 FP8 Tensor Core,SGLang 自动走 Marlin weight-only FP8(W8A16)——权重存 FP8、计算 BF16。收益来自权重从每卡约 38 GB 降到约 7.6 GB,decode 读带宽大幅下降。
- 结果:单流 decode 109.1 tok/s(+49%),KV 池增至 89.7 万 token。这是全部配置中最快的一档,但仍占 4 卡。
阶段 3:FP8 + TP2 + MTP(省卡,缩到 GPU 2+3)
- 应需求把服务收缩到物理 GPU 2+3,TP=2,释放 GPU 0+1 给其他任务。
- 结果:单流 decode 76.4 tok/s。每卡权重翻倍(约 15 GB)、单步计算量增大,比 TP4 慢,但仍略快于 4 卡 BF16 基线。
- 此为"驻留配置"的第一版。
阶段 4:FP8 + TP2 + DFlash2(当前驻留配置)
- 用 DFlash2 草稿模型(z-lab/Qwen3.8-27B-DFlash2,3.8 GB)替换 MTP 投机解码:
-speculative-algorithm=DFLASH,8 个 draft token。DFLASH 与 MTP/EAGLE 互斥,是替换而非叠加。 - 两个部署约束:
- 镜像:DFlash2 支持是 SGLang PR 35371,2026-08-19 才合入,Docker Hub 最新 nightly(08-18)还没包含。因此在
qwen38-27b镜像上打了本地 overlay 包sglang-qwen38-27b-dflash2:local(从合入 commit 拷贝 4 个文件,另做了 3 处 qwen38 分支兼容性修补)。 - 不能独立放 GPU 0:DFlash2 需要注入目标模型第 5/19/33/47/61 层的 hidden states,草稿 worker 与目标 TP rank 同卡运行,必须与目标模型共置在 GPU 2+3。
- 镜像:DFlash2 支持是 SGLang PR 35371,2026-08-19 才合入,Docker Hub 最新 nightly(08-18)还没包含。因此在
- 结果:单流 decode 96.1 tok/s(比同卡 MTP +26%),accept length 约 3.3–3.6 / 8。代价是 conc8 聚合吞吐 356 → 227(verify CUDA graph 只采集到 bs≤7),KV 池 31.5 万 → 24.6 万 token(草稿模型占了同卡显存)。
草稿模型的放置与并行方式
草稿模型与目标模型共置在同一组卡上,并随目标一起做 TP2 切分,这也是 hidden-state 注入式草稿的标准跑法:
- 共置:EAGLE、MTP、DFlash 这类草稿都要消费目标模型的中间层 hidden states(DFlash2 是第 5/19/33/47/61 层),每个 decode step 都要传一次。若草稿放在独立卡上,这些激活每步都得跨卡搬运,延迟会抵消投机收益。vLLM、SGLang 的投机解码默认都与目标同卡运行。
- TP2 切分,不是每卡一份完整副本:SGLang 中 DFlash2 的线性层(
QKVParallelLinear/ColumnParallelLinear/RowParallelLinear)直接复用目标模型的 TP 通信组,按注意力头切分(要求头数能被 tp_size 整除)。3.8 GB 的 checkpoint 在每张卡上实际占约 2.1 GB,与启动日志一致。candidate selector 每步做一次 all-gather 合并两卡候选后取最优。 - 独立卡方案(decoupled / disaggregated speculative decoding)适用于不依赖目标 hidden states 的独立小模型草稿,靠流水线重叠 draft 与 verify,不适用于本场景:DFlash2 架构上没有 embedding 和 lm_head,本身就不是独立模型,无法单独运行。
选型结论:追求极限单流速度且 4 卡可用 → 阶段 2(FP8 TP4 MTP,109 tok/s);只用 2 卡、以单流/低并发为主 → 阶段 4(当前配置);高并发(≥8 路)场景下阶段 4 反而不如阶段 3 的 MTP。
二、对比测试
测试方法:流式 OpenAI chat completions,temperature=0,ignore_eos,decode 速率不含 TTFT。单流三组 max_tokens=256,并发两组每路 128 token、thinking 关闭。同一脚本、同一组 prompt 跑完四种配置。
五组吞吐对比(tok/s)
| 配置 | GPU | Decode(短prompt) | Think-on | Long-prefill | Conc4 聚合 | Conc8 聚合 |
|---|---|---|---|---|---|---|
| BF16 + MTP | 0–3, TP4 | 73.4 | 63.8 | 75.5 | 176.7 | 327.1 |
| FP8 + MTP | 0–3, TP4 | 109.1 | 95.6 | 112.7 | 224.3 | 373.2 |
| FP8 + MTP | 2–3, TP2 | 76.4 | 70.9 | 77.0 | 202.7 | 356.4 |
| FP8 + DFlash2(当前) | 2–3, TP2 | 96.1 | 88.1 | 90.4 | 220.3 | 227.5 |
延迟与容量
| 配置 | TTFT(短prompt) | KV 池容量(token) | 每卡权重占用 |
|---|---|---|---|
| BF16 + MTP, TP4 | 101 ms | 691,584 | ~13 GB |
| FP8 + MTP, TP4 | 89 ms | 897,088 | ~7.6 GB |
| FP8 + MTP, TP2 | 94 ms | 315,136 | ~15 GB |
| FP8 + DFlash2, TP2 | 92 ms | 245,696 | ~15 GB + 2.1 GB 草稿 |
注:Long-prefill(约 1,655 token 输入)首次 TTFT 含冷缓存约 0.6–1.2 s,前缀缓存命中后约 100–150 ms。
同卡(GPU 2+3, TP2)MTP vs DFlash2 逐项
| 指标 | MTP | DFlash2 | 变化 |
|---|---|---|---|
| Decode | 76.4 | 96.1 | +26% |
| Think-on | 70.9 | 88.1 | +24% |
| Long-prefill decode | 77.0 | 90.4 | +17% |
| Conc4 聚合 | 202.7 | 220.3 | +9% |
| Conc8 聚合 | 356.4 | 227.5 | −36% |
| KV 池 | 315,136 | 245,696 | −22% |
DFlash2 单流优势来自更高的平均接受长度(约 3.3–3.6 token/步 vs MTP 约 2.5–3.2)且草稿开销低;并发劣势来自 draft+verify 双阶段开销随 batch 增大、以及 verify CUDA graph 仅覆盖 bs=1–7。
三、复现配置
最终配置(FP8 TP2 + DFlash2)的关键启动参数,模型分别挂载为 /models(FP8 目标模型)和 /draft(z-lab/Qwen3.8-27B-DFlash2):
|
|
换回 MTP 只需删掉 5 个 --speculative-* 参数、换成 EAGLE 配置(steps=3, topk=1, draft tokens=4)。两卡合计显存占用约 76 GB(含 24.6 万 token 的 KV 池)。DFlash2 支持在 SGLang PR 35371 合入后的版本可用;更早的镜像需要自行从合入 commit 打 overlay 包(见下一节第 5 条)。
四、实施要点与坑
- Ampere 上 FP8 不是"原生 FP8 推理":走 Marlin W8A16,收益全部来自权重带宽,不要期待激活也变 FP8。
- DFlash2 与 MTP 互斥:
-speculative-algorithm=DFLASH替换 EAGLE/MTP,不能叠加。 - 草稿模型必须与目标同卡,且随目标 TP 切分:DFlash2 依赖目标模型中间层 hidden states,独立放到空闲 GPU 0 的方案不成立(解耦投机
decoupled-spec-role在该分支只有 IPC 桩,未接 DFLASH)。详见"草稿模型的放置与并行方式"一节。 - 不要用改
config.json架构名的方式把 DFlash2 灌进 DFlash v1:v1 的 load_weights 会静默丢弃attention_conv/mlp_conv/candidate_selector权重,而主干是带 conv 训练的,结果会错。 - 本地 overlay 的兼容修补(qwen38 分支 vs main):
dflash_worker_v2.py中get_schedule().page_size改为server_args.page_size、去掉compute_spec_logprobs调用(该分支签名不同,logprobs 路径不用即可);dflash_utils.py中sample_simulated_acc_len需从_sample_simulated_acc_len别名导入。 - FP8 checkpoint 的
lm_head必须保持 BF16 稠密(在modules_to_not_convert中),DFlash2 的 candidate selector 依赖它。