双 NVIDIA L20 上 Qwen3.8-27B-FP8 + MTP 调优测试报告
双 NVIDIA L20 上 Qwen3.8-27B-FP8 + MTP 调优测试报告
测试日期:2026-08-18
测试服务器:172.16.41.251
模型:Qwen/Qwen3.8-27B-FP8
推理框架:vLLM OpenAI-Compatible Server
一、结论摘要
本轮在同一模型、同一镜像、同一双卡 TP2 拓扑和同一测试负载下,完成了 MTP2/MTP3/MTP4、O2/O3、max-num-batched-tokens 4096/8192/16384、MTP4+4096 组合以及 L20 专用 M=1 FP8 Kernel 的受控 A/B。
主要结论如下:
- 均衡生产配置仍建议 MTP3 或改为 MTP4,保留 O2 和 8192。
- MTP3/O2/8192 双次基线平均为 67.53 decode tok/s。
- MTP4/O2/8192 为 71.37 decode tok/s,提升 **5.70%**;缓存输入也提升约 **4.76%**,4 路吞吐均值下降约 **3.39%**,小于部分并发轮次波动。
- 如果只追求单请求速度,MTP4/O2/8192 是最简单、最均衡的调整。
max-num-batched-tokens=4096也把单流提高到 71.74 tok/s,但 4 路吞吐下降约 **9.84%**,不如 MTP4 均衡。- O3 对单流没有收益:67.50 vs 67.53 tok/s;4 路仅提高约 **0.91%**,不值得引入额外优化复杂度。
- 16384 没有提升本轮 4 路负载,不能假设“更大的 batch token 上限一定更快”。
- 专用 M=1 FP8 Kernel 将 Decode 提高到 77.05 tok/s(+14.10%),但冷输入和缓存输入分别下降 **32.63%、28.94%**。它是“长输出优先”实验配置,不是均衡生产配置。
- 输入侧最大的实际收益来自稳定前缀。 基线中约 3.5K Token 输入从冷前缀的 924 ms / 3,784 tok/s 攌善到缓存前缀的 314 ms / 11,038 tok/s:TTFT 下降约 **66%**,有效输入吞吐约为 2.92 倍。
即使采用本轮输出最快的 Kernel 配置,Qwen3.8 稠密模型仍未达到先前 Qwen3.6-35B-A3B MoE 的约 152 decode tok/s。软件调优可以改善 Qwen3.8,但无法消除 27B 稠密模型与约 3B 激活 MoE 的架构差距。
二、服务器配置
| 项目 | 配置 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS,Linux 5.15.0-186-generic |
| CPU | 2 × Intel Xeon Gold 6530 |
| CPU 规格 | 64 物理核心,128 线程 |
| 内存 | 251 GiB |
| GPU | 2 × NVIDIA L20 |
| 单卡显存 | 46,068 MiB |
| 总显存 | 约 92 GiB(标称 96 GB) |
| NVIDIA Driver | 580.173.02 |
| GPU 拓扑 | GPU0 位于 NUMA0,GPU1 位于 NUMA1,GPU 间为 SYS |
| Docker | 29.4.1 |
| Docker Compose | v5.1.3 |
SYS 表示 GPU 间通信需要经过 PCIe 和 CPU UPI/QPI,而不是 NVLink。Qwen3.8 使用 TP2 时,每层通信都会受到这一拓扑约束。
三、模型与部署基线
模型路径:
1 | /data/ai/models/Qwen3.8-27B-FP8 |
部署目录:
1 | /home/qwen |
镜像:
1 | vllm/vllm-openai:qwen38 |
运行容器的实际基线参数为:
1 | --tensor-parallel-size 2 |
需要注意:服务器当前 /home/qwen/docker-compose.fp8-mtp.yml 写的是 max-model-len: 49152,而正在运行容器的实际参数是 32768。本轮为保持与真实运行基线一致,所有测试都使用 32768;本轮没有修改该 Compose 文件。
四、测试方法
每个条件只改变一个待测参数;组合项单独标记。任何时刻只加载一个 Qwen3.8 模型实例,两张 L20 由该实例独占。
4.1 输入测试
- 输入长度:约 3,500 Token。
- 冷前缀:每次请求的唯一随机标识放在提示词最前面,阻止长前缀复用。
- 缓存前缀:长前缀保持逐字节一致,动态标识放在末尾;先预热一次,再测 5 次。
- 每次只输出 1 Token,用首 Token 到达时间衡量输入侧延迟。
- 有效输入吞吐定义为:
逻辑提示 Token 数 / 端到端 TTFT。
这里的输入 tok/s 包含 HTTP、排队、Prefill 和第一个输出 Token,并不是单独的 GPU Kernel Prefill 数字;因此必须与 TTFT 一起理解。
4.2 输出测试
- 输入长度:3,465 Token,使用稳定前缀。
- 输出长度:固定 512 Token。
temperature=0、top_p=1、seed=42、ignore_eos=true。- 单流:每个条件 5 次。
- 4 路并发:每轮 4 个请求,共 3 轮。
- Decode 速度定义为:
(输出 Token - 1) / 首 Token 到末 Token 的时间。 - 端到端输出速度定义为:
输出 Token / 请求总时间。
基线在测试序列前后各跑一次,单流分别为 67.54 和 67.52 tok/s,说明单流测量稳定。并发分别为 220.44 和 227.72 tok/s,报告中的百分比以两次均值 224.08 tok/s 为基准,并对几个百分点的小差异保持谨慎。
五、输入速度对比
| 条件 | 冷输入 TTFT | 冷输入有效 tok/s | 缓存输入 TTFT | 缓存输入有效 tok/s | 相对基线判断 |
|---|---|---|---|---|---|
| 基线平均:MTP3 / O2 / 8192 | 924 ms | 3,784 | 314 ms | 11,038 | 基准 |
| MTP2 / O2 / 8192 | 935 ms | 3,749 | 313 ms | 11,078 | 基本相同 |
| MTP4 / O2 / 8192 | 944 ms | 3,709 | 300 ms | 11,563 | 冷输入 -1.99%,缓存输入 +4.76% |
| MTP3 / O3 / 8192 | 939 ms | 3,730 | 313 ms | 11,053 | 基本相同 |
| MTP3 / O2 / 4096 | 931 ms | 3,760 | 314 ms | 11,018 | 基本相同 |
| MTP3 / O2 / 16384 | 937 ms | 3,739 | 314 ms | 11,049 | 基本相同 |
| MTP4 / O2 / 4096 | 945 ms | 3,707 | 302 ms | 11,488 | 缓存输入快,但输出组合失败 |
| MTP3 / O2 / 8192 + M=1 Kernel | 1,373 ms | 2,549 | 442 ms | 7,843 | 冷输入 -32.63%,缓存输入 -28.94% |
输入侧结论:
- MTP 数量、O2/O3、4096/8192/16384 对冷 Prefill 的影响都很小。
- 稳定前缀带来的缓存收益远大于这些服务端参数差异。
- MTP4 的缓存输入最好,同时没有显著损害冷输入。
- 只为 M=1 调优 Kernel 会让其他 batch 尺寸使用不合适的最近配置,因此 Prefill 明显变慢。
六、输出速度对比
| 条件 | 单流 Decode tok/s | 相对基线 | 单流端到端 tok/s | 4 路总吞吐 tok/s | 4 路相对基线 |
|---|---|---|---|---|---|
| 基线平均:MTP3 / O2 / 8192 | 67.53 | 基准 | 64.88 | 224.08 | 基准 |
| MTP2 / O2 / 8192 | 63.83 | -5.47% | 61.45 | 198.33 | -11.49% |
| MTP4 / O2 / 8192 | 71.37 | +5.70% | 68.43 | 216.48 | -3.39% |
| MTP3 / O3 / 8192 | 67.50 | -0.03% | 64.85 | 226.13 | +0.91% |
| MTP3 / O2 / 4096 | 71.74 | +6.23% | 68.75 | 202.03 | -9.84% |
| MTP3 / O2 / 16384 | 69.55 | +3.00% | 66.64 | 198.38 | -11.47% |
| MTP4 / O2 / 4096 | 67.23 | -0.44% | 64.61 | 196.10 | -12.49% |
| MTP3 / O2 / 8192 + M=1 Kernel | 77.05 | +14.10% | 72.20 | 227.35 | +1.46% |
输出侧结论:
- MTP2 虽有更高的草稿接受率,但每步可接受 Token 更少,最终比 MTP3 慢。
- MTP4 的平均草稿接受率下降,但平均接受长度增加,单流因此更快。
- 4096 也能偏向单流 Decode,但会更明显地牺牲并发吞吐。
- MTP4 与 4096 组合后接受率进一步下降,两个单项收益没有叠加。
- O3 对当前稠密模型、L20 和跨 NUMA TP2 组合没有单流价值。
- M=1 Kernel 是输出最快项,但其输入损失意味着它只适合输出很长、首 Token 不敏感的业务。
七、MTP 接受率观察
日志中的末次 SpecDecoding 窗口如下。它们不是全程平均,但可以解释性能方向:
| 条件 | 平均接受长度 | 平均 Draft 接受率 | 各位置接受率 |
|---|---|---|---|
| MTP2 / O2 / 8192 | 2.75 | 87.3% | 91.3%、83.2% |
| MTP3 / O3 / 8192 | 3.38 | 79.2% | 90.1%、78.8%、68.7% |
| MTP3 / O2 / 4096 | 3.19 | 73.0% | 85.3%、72.5%、61.2% |
| MTP4 / O2 / 8192 | 3.77 | 69.2% | 86.8%、73.6%、62.6%、53.7% |
| MTP4 / O2 / 4096 | 3.47 | 61.7% | 84.7%、65.8%、52.9%、43.4% |
这说明不能只看“接受率百分比”。MTP4 虽然百分比低于 MTP2/MTP3,但成功时一次接受更多 Token;真正需要比较的是最终 Decode 和业务延迟。
八、L20 FP8 Kernel 调优实验
启动日志确认 Qwen3.8 有 5 个 W8A8 Block FP8 矩阵形状缺少 NVIDIA L20 配置:
1 | N=17408,K=5120 |
镜像没有附带调优脚本,因此下载了 vLLM 官方 benchmark_w8a8_block_fp8.py,并通过包装器将脚本默认的 DeepSeek 形状替换成日志确认的 Qwen3.8 形状。两张 L20 并行搜索 batch=1 的 1,280 组候选配置,约 17 分钟完成。
文件校验:
1 | f9dc7a266da6ea0b94e57ddec9e7994193ac314ca3a84b14a3803ed90efa7d30 benchmark_w8a8_block_fp8.py |
测试容器通过 5 个只读单文件挂载加载 JSON,没有覆盖镜像文件。两张 GPU 的日志均确认 Using configuration from ...。
限制:本轮只调了 M=1。vLLM 会为其他 batch 尺寸选择 JSON 中最近的配置,因此 3.5K 冷 Prefill 和十几个 Token 的缓存尾部 Prefill 被迫使用 M=1 配置,导致输入退化。若要生产使用,至少还需要调 M=16/32 和 M=2048/4096,再把多组配置合并后重新 A/B。该过程耗时较长,并且配置与 GPU、vLLM Kernel 实现和镜像版本绑定。
九、推荐配置
9.1 均衡、保守生产配置
保持当前:
1 | MTP=3 |
优点是官方常用 MTP3 路径、并发稳定、维护成本最低。
9.2 单请求优先的推荐配置
只将 MTP3 改为 MTP4:
1 | MTP=4 |
本轮收益:
- Decode:67.53 → 71.37 tok/s,约 +5.70%。
- 端到端输出:64.88 → 68.43 tok/s,约 +5.47%。
- 缓存输入:11,038 → 11,563 tok/s,约 +4.76%。
- 4 路吞吐均值下降约 3.39%,但并发样本波动较大,建议用真实 Agent 请求再跑 30~60 分钟 P50/P95。
9.3 长输出、首 Token 不敏感的实验配置
使用基线 MTP3/O2/8192,加 M=1 L20 Kernel:
- Decode 可达到 77.05 tok/s。
- 冷输入 TTFT 增至 1.37 秒,缓存输入 TTFT 增至 0.44 秒。
- 不建议直接作为通用 Agent 服务;更适合离线生成或很长输出。
十、业务侧输入优化建议
本轮最确定的输入优化是提高 Prefix Cache 命中:
1 | 固定 system prompt |
动态字段不要放在请求最前面。对于工具较多的 Agent,应只注入当前任务需要的工具,并保持 JSON 字段和工具顺序稳定。
减少 Thinking 和输出长度没有纳入本轮“引擎参数 A/B”,因为它会改变输出内容和 Token 数,无法与固定 512 Token 的纯速度测试混在一起。它仍然可能是业务端到端延迟收益最大的下一步,应使用真实 Agent 任务单独做质量/延迟联合评估。
十一、未推荐项
optimization-level=3:单流无提升。max-num-batched-tokens=16384:本轮 4 路吞吐反而下降。MTP4 + 4096:收益不能叠加,单流和并发均退步。- 单独启用 M=1 Kernel:输入损失过大。
- 提高
gpu-memory-utilization或降低max-model-len:主要影响容量,不解决单流 Decode 核心瓶颈。 - 当前重新启用 Custom AllReduce:此前在这套 L20/镜像组合上触发 CUDA Graph
invalid argument,本轮继续使用稳定的--disable-custom-all-reduce。
十二、服务器最终状态与安全边界
- 原容器
qwen38-fp8-mtp已恢复运行,/health返回 HTTP 200。 - 其他原有服务保持运行。
- 所有测试和调优容器均已停止。
- 没有删除任何容器、镜像、模型或文件。
- 模型目录
/data/ai/models未修改。 - 新增脚本、结果和 Kernel 配置全部位于
/home/qwen。
由于遵守“不删除”约束,服务器保留了成功和失败的测试容器。失败项包括一次优化级别格式校验失败和一次官方调优脚本多进程 pickle 失败;两次都发生在正式推理/Kernel 搜索前,且保护脚本均恢复了原服务。
十三、结果与脚本位置
服务器:
1 | /home/qwen/qwen_tuning_benchmark.py |
本项目:
1 | QWEN38_TUNING_REPORT.md |
十四、最终建议
如果目标是替换现有 Agent 模型并优先改善单请求体感,建议下一阶段在真实业务流量上比较:
- 当前 MTP3/O2/8192;
- MTP4/O2/8192;
- 同一批真实任务下的 Thinking 开/关与输出长度上限。
本轮参数测试的推荐候选是 MTP4/O2/8192。它是唯一同时改善单流输出和缓存输入、且没有明显破坏冷输入的简单配置。若必须追求更高的纯 Decode,下一步应补齐多 batch 尺寸的 L20 Kernel 网格,而不是直接部署只有 M=1 的实验配置。