# Decode 吞吐专项实验分析报告 日期:2026-07-14 ## 一、结论摘要 本轮实验确认:当前服务的主要短板是 decode 阶段吞吐,而不是 prompt prefill、tool parser 或 reasoning parser。 核心证据: - 短 prompt、单并发、256 token 输出时,Output TPS P10 只有 `8.3-8.7 tok/s`,明显低于官方目标 `>=20 tok/s`。 - 去掉 `--enable-auto-tool-choice`、`--tool-call-parser`、`--reasoning-parser` 后,decode TPS 没有提升,反而略低。 - 加并发后没有获得 batch 增益:并发 1 聚合约 `8.00 tok/s`,并发 2 聚合约 `7.23 tok/s`,并发 4 聚合约 `5.08 tok/s`。 - 并发 2 时硬件采样显示平均 GPU-Util 只有 `28.9%`,平均功耗约 `49.7W / 250W`,说明 GPU 没有被持续打满。 初步判断:瓶颈更像是 decode 路径中的调度、TP 同步、MoE kernel/专家路由、paged attention/kernel launch 开销,或 xFormers 后端限制,而不是 API parser 层。 ## 二、实验环境 远端服务: - 模型:`/root/public-storage/models/Qwen/Qwen3.6-35B-A3B` - API:`vllm.entrypoints.openai.api_server` - GPU:4 x Iluvatar BI-V100, 32GB - 当前主要实验配置: - `-tp 4` - `--gpu-memory-utilization 0.95` - `--max-num-seqs 2` - `--max-num-batched-tokens 8192` - `--enable-chunked-prefill` - `--enable-prefix-caching` 本地脚本: - `worklogs/decode_microbench.py` 远端脚本: - `/root/work/decode_microbench.py` 本地原始结果: - `worklogs/remote_results/2026-07-14-decode/` ## 三、实验结果 ### 1. 纯短 prompt decode 基线 完整 parser 配置,短 prompt,单并发,3 请求,每请求 `max_tokens=256`。 结果文件: - 远端:`/root/work/logs/decode_full_parser_short_c1_t256_r3.json` - 本地:`worklogs/remote_results/2026-07-14-decode/decode_full_parser_short_c1_t256_r3.json` 指标: | 指标 | 数值 | |---|---:| | 成功率 | 100% | | TTFT P90 | 1.85s | | Output TPS P10 | 8.74 tok/s | | Output TPS P50 | 8.74 tok/s | | 聚合 Output TPS | 8.34 tok/s | | 总 completion tokens | 768 | | reasoning tokens | 768 | 解释: 短 prompt 下 TTFT 已经较低,但 decode 速度仍只有约 `8.7 tok/s`。这说明长上下文不是唯一问题,短输出生成本身就偏慢。 ### 2. Tool/parser 触发场景 完整 parser 配置,携带 29 个 tools,单并发,3 请求,每请求 `max_tokens=128`。 结果文件: - 远端:`/root/work/logs/decode_full_parser_tool_c1_t128_r3.json` - 本地:`worklogs/remote_results/2026-07-14-decode/decode_full_parser_tool_c1_t128_r3.json` 指标: | 指标 | 数值 | |---|---:| | 成功率 | 100% | | TTFT P90 | 4.98s | | Output TPS P10 | 8.70 tok/s | | Output TPS P50 | 8.70 tok/s | | 聚合 Output TPS | 7.38 tok/s | | prompt tokens | 6639 | | cached tokens | 4416 | | completion tokens | 384 | 单请求细节: - 第 1 个请求:TTFT `5.97s`,cached tokens `0` - 第 2/3 个请求:TTFT 约 `1.0s`,cached tokens `2208` 解释: 工具 schema 会明显影响首次 prefill/TTFT,但前缀缓存命中后 TTFT 恢复。Output TPS 仍约 `8.7 tok/s`,与纯短 prompt 基本一致,所以 tool parser 不是 decode TPS 主瓶颈。 ### 3. Parser-off 对照 重启服务,去掉: - `--enable-auto-tool-choice` - `--tool-call-parser qwen3_coder` - `--reasoning-parser qwen3` 其余参数保持一致。 短 prompt,单并发,3 请求,每请求 `max_tokens=256`。 结果文件: - 远端:`/root/work/logs/decode_noparser_short_c1_t256_r3.json` - 本地:`worklogs/remote_results/2026-07-14-decode/decode_noparser_short_c1_t256_r3.json` 指标: | 指标 | 完整 parser | parser-off | |---|---:|---:| | 成功率 | 100% | 100% | | TTFT P90 | 1.85s | 3.18s | | Output TPS P10 | 8.74 | 8.30 | | Output TPS P50 | 8.74 | 8.33 | | 聚合 Output TPS | 8.34 | 7.87 | 解释: 关闭 parser 没有改善 decode。parser/reasoning/tool 相关启动项不是当前 decode 吞吐低的主因。 ### 4. 并发曲线 parser-off 服务,短 prompt,每请求 `max_tokens=128`。 结果文件: - `decode_noparser_short_c1_t128_r4.json` - `decode_noparser_short_c2_t128_r4.json` - `decode_noparser_short_c4_t128_r4.json` 指标: | 并发 | 请求数 | 成功率 | TTFT P90 | Output TPS P10/请求 | 聚合 Output TPS | |---:|---:|---:|---:|---:|---:| | 1 | 4 | 100% | 0.79s | 8.30 | 8.00 | | 2 | 4 | 100% | 0.92s | 3.68 | 7.23 | | 4 | 4 | 100% | 51.60s | 2.57 | 5.08 | 解释: 并发提高后没有形成有效 batch 增益。并发 2 时单请求 TPS 近似减半,聚合 TPS 也略降;并发 4 时出现明显排队,TTFT P90 被拉到 `51.6s`。 这说明当前配置下 decode 并发能力很弱。由于服务参数 `--max-num-seqs=2`,并发 4 的后两个请求排队符合预期;但并发 2 聚合吞吐仍不提升,说明 decode 内部没有把双请求 batch 变成更高硬件利用率。 ### 5. 硬件利用率采样 parser-off 服务,并发 2,4 请求,每请求 `max_tokens=128`,同时采样 `ixsmi`。 结果文件: - 远端:`/root/work/logs/decode_noparser_short_c2_t128_r4_monitor.json` - 远端:`/root/work/logs/ixsmi_noparser_short_c2_t128_r4_monitor.json` - 本地同名文件位于:`worklogs/remote_results/2026-07-14-decode/` 指标: | 指标 | 数值 | |---|---:| | 成功率 | 100% | | TTFT P90 | 0.92s | | Output TPS P10 | 3.79 tok/s | | 聚合 Output TPS | 7.40 tok/s | | ixsmi records | 52 | | parsed GPU samples | 208 | | 平均 GPU-Util | 28.91% | | 最大 GPU-Util | 100% | | 平均显存 | 30477 MiB | | 最大显存 | 30555 MiB | | 平均功耗 | 49.67 W | | 最大功耗 | 51 W | 解释: GPU 利用率和功耗都偏低。虽然瞬时 GPU-Util 能到 100%,但平均只有约 29%,功耗长期接近空载到轻载水平。这说明 decode 过程中存在大量空泡、同步等待或小 kernel 启动开销,GPU 算力没有被持续喂满。 ## 四、瓶颈判断 当前最可能的瓶颈排序: 1. Decode 调度/后端限制 - `--num-scheduler-steps 4` 曾尝试失败。 - 报错:`Multi-Step + Chunked-Prefill not supported for attention backend: xformers`。 - 当前服务日志显示使用 `XFormers backend`。 2. TP 通信或同步开销 - 模型使用 `-tp 4`。 - 短 decode 每步都可能涉及多卡同步。 - 并发 2 聚合 TPS 不升反降,符合小 batch 多卡同步效率差的特征。 3. MoE decode kernel/专家路由效率 - Qwen3.6-35B-A3B 是 MoE 模型。 - decode batch 小时,专家路由和 fused MoE kernel 可能难以形成高利用率。 4. Paged attention / attention backend 每 token 开销 - 当前 attention 后端为 xFormers。 - multi-step decode 被 xFormers + chunked prefill 组合限制。 5. API/parser 层 - 本轮实验基本排除其为主瓶颈。 - tool/schema 影响首次 TTFT,但不显著影响 decode TPS。 ## 五、下一步建议 建议下一步不要直接改大段 kernel,而是先做两个能明确指向代码修改方向的服务变体实验。 ### 实验 A:decode-only multi-step 变体 目的:验证 multi-step scheduling 是否能明显提升 decode。 做法: - 暂时关闭 `--enable-chunked-prefill` - 加回 `--num-scheduler-steps 4` - 保持 `max_num_seqs=2` - 跑短 prompt decode 并发 1/2 曲线 判断: - 如果 Output TPS 明显提升,说明 decode 调度是关键方向。 - 后续要研究如何让 multi-step 与长上下文 chunked prefill 共存,或按场景切换。 风险: - 官方长上下文负载仍需要 chunked prefill,所以这不是最终配置,只是定位实验。 ### 实验 B:attention backend 变体 目的:确认是否可以启用兼容 multi-step 的 attention backend。 做法: - 尝试设置 `VLLM_ATTENTION_BACKEND=FLASH_ATTN` - 或检查 CoreX/xFormers flash attention 能否被 vLLM selector 选中 - 如果能启动,再测试 multi-step + chunked prefill 判断: - 如果 flash attention 能启动且 multi-step 可用,优先走 backend 配置/适配路线。 - 如果不能启动,需要看 `vllm/attention/selector.py`、`vllm/attention/backends/*` 和 CoreX xFormers 补丁。 ### 实验 C:代码级 profiling 目的:把低 GPU 利用率归因到具体模块。 建议插桩位置: - `vllm/worker/model_runner.py` - `vllm/worker/multi_step_model_runner.py` - `vllm/model_executor/models/qwen3_moe.py` - `vllm/model_executor/layers/fused_moe/*` - `attention.py` - `paged_attn.py` 记录每步: - model forward 耗时 - attention 耗时 - MoE/MLP 耗时 - sampler 耗时 - 每步前后同步耗时 ### 实验 D:服务参数小网格 目的:确认当前 `max_num_seqs=2` 是否已经是最优。 建议组合: | 参数 | 候选 | |---|---| | `max_num_seqs` | 1, 2, 4 | | `max_num_batched_tokens` | 4096, 8192 | | `chunked_prefill` | on/off | | `num_scheduler_steps` | 1, 4 | 优先只在短 prompt decode 上跑,快速筛掉无效组合。 ## 六、当前推荐行动 下一步优先跑: 1. `chunked_prefill=off + num_scheduler_steps=4` 2. 若能启动,跑 decode c1/c2/c4 曲线和 ixsmi 采样 3. 若 decode TPS 明显提升,再研究如何兼容官方长上下文 prefill 4. 若没有提升,进入 qwen3_moe / fused_moe / attention 的代码级 profiling 本轮最重要的事实是:GPU 平均利用率只有约 `29%`,所以先不要把问题简单归因为“卡算不动”。更像是当前 decode 执行路径没有把四张卡持续喂满。