2026-07-14 17:11:28 +08:00
|
|
|
|
# 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 执行路径没有把四张卡持续喂满。
|
2026-07-14 18:17:42 +08:00
|
|
|
|
|
|
|
|
|
|
## 七、调度方向验证实验:multi-step decode
|
|
|
|
|
|
|
|
|
|
|
|
追加日期:2026-07-14
|
|
|
|
|
|
|
|
|
|
|
|
### 目标
|
|
|
|
|
|
|
|
|
|
|
|
验证 `--num-scheduler-steps 4` 是否能作为 decode 吞吐提升方向。
|
|
|
|
|
|
|
|
|
|
|
|
由于上一轮实验显示并发升高没有带来 aggregate TPS 增益,且平均 GPU 利用率只有约 `29%`,multi-step scheduling 是最直接的调度侧候选优化:它理论上可以减少每 token 调度往返和 Python/worker 协调开销,让 decode 连续执行多个 step。
|
|
|
|
|
|
|
|
|
|
|
|
### 实验 1:关闭 chunked prefill,直接启用 multi-step
|
|
|
|
|
|
|
|
|
|
|
|
启动变体:
|
|
|
|
|
|
|
|
|
|
|
|
- `--num-scheduler-steps 4`
|
|
|
|
|
|
- 不加 `--enable-chunked-prefill`
|
|
|
|
|
|
- `--max-model-len 100000`
|
|
|
|
|
|
- `--max-num-batched-tokens 8192`
|
|
|
|
|
|
- 其它参数沿用完整 parser 服务配置
|
|
|
|
|
|
|
|
|
|
|
|
结果:
|
|
|
|
|
|
|
|
|
|
|
|
- 服务未启动。
|
|
|
|
|
|
- 远端日志:`/root/work/logs/server_exp_sched4_nochunk_fullparser.log`
|
|
|
|
|
|
- 本地归档:`worklogs/remote_results/2026-07-14-scheduler/server_exp_sched4_nochunk_fullparser.log`
|
|
|
|
|
|
|
|
|
|
|
|
失败原因:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
ValueError: max_num_batched_tokens (8192) is smaller than max_model_len (100000).
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
解释:
|
|
|
|
|
|
|
|
|
|
|
|
关闭 chunked prefill 后,vLLM 要求 `max_num_batched_tokens >= max_model_len`,否则实际最大可处理序列会被 `max_num_batched_tokens` 限制。这个配置不能用于官方长上下文,也无法进入 decode 压测。
|
|
|
|
|
|
|
|
|
|
|
|
### 实验 2:decode-only 诊断配置
|
|
|
|
|
|
|
|
|
|
|
|
为绕过实验 1 的限制,临时降低上下文长度,只用于短 prompt decode 诊断。
|
|
|
|
|
|
|
|
|
|
|
|
启动变体:
|
|
|
|
|
|
|
|
|
|
|
|
- `--max-model-len 4096`
|
|
|
|
|
|
- `--max-seq-len-to-capture 4096`
|
|
|
|
|
|
- `--max-num-batched-tokens 8192`
|
|
|
|
|
|
- `--num-scheduler-steps 4`
|
|
|
|
|
|
- 不加 `--enable-chunked-prefill`
|
|
|
|
|
|
- attention backend 仍为自动选择
|
|
|
|
|
|
|
|
|
|
|
|
结果:
|
|
|
|
|
|
|
|
|
|
|
|
- 服务未启动。
|
|
|
|
|
|
- 远端日志:`/root/work/logs/server_exp_sched4_nochunk_len4096_fullparser.log`
|
|
|
|
|
|
- 本地归档:`worklogs/remote_results/2026-07-14-scheduler/server_exp_sched4_nochunk_len4096_fullparser.log`
|
|
|
|
|
|
|
|
|
|
|
|
关键日志:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
Using XFormers backend.
|
|
|
|
|
|
ValueError: Multi-Step not supported for attention backend: xformers.
|
|
|
|
|
|
Set VLLM_ATTENTION_BACKEND to a value from ['flash-attn', 'rocm-flash-attn', 'flashinfer'].
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
解释:
|
|
|
|
|
|
|
|
|
|
|
|
这说明当前环境不是“chunked prefill 与 multi-step 的组合不支持”这么简单,而是 `xformers` attention backend 本身不支持 multi-step worker。只要 attention backend 仍然落到 xFormers,multi-step 调度就无法启用。
|
|
|
|
|
|
|
|
|
|
|
|
### 实验 3:强制 FlashInfer backend
|
|
|
|
|
|
|
|
|
|
|
|
启动变体:
|
|
|
|
|
|
|
|
|
|
|
|
- 环境变量:`VLLM_ATTENTION_BACKEND=FLASHINFER`
|
|
|
|
|
|
- `--max-model-len 4096`
|
|
|
|
|
|
- `--num-scheduler-steps 4`
|
|
|
|
|
|
- 不加 `--enable-chunked-prefill`
|
|
|
|
|
|
- 其它参数同实验 2
|
|
|
|
|
|
|
|
|
|
|
|
结果:
|
|
|
|
|
|
|
|
|
|
|
|
- 服务未启动。
|
|
|
|
|
|
- 远端日志:`/root/work/logs/server_exp_sched4_nochunk_len4096_flashinfer.log`
|
|
|
|
|
|
- 本地归档:`worklogs/remote_results/2026-07-14-scheduler/server_exp_sched4_nochunk_len4096_flashinfer.log`
|
|
|
|
|
|
|
|
|
|
|
|
关键日志:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
TypeError: 'NoneType' object is not callable
|
|
|
|
|
|
...
|
|
|
|
|
|
self._decode_wrapper = BatchDecodeWithPagedKVCacheWrapper(...)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
解释:
|
|
|
|
|
|
|
|
|
|
|
|
`vllm/attention/backends/flashinfer.py` 会导入:
|
|
|
|
|
|
|
|
|
|
|
|
- `flashinfer.BatchDecodeWithPagedKVCacheWrapper`
|
|
|
|
|
|
- `flashinfer.decode.CUDAGraphBatchDecodeWithPagedKVCacheWrapper`
|
|
|
|
|
|
- `flashinfer.prefill.BatchPrefillWithPagedKVCacheWrapper`
|
|
|
|
|
|
- `ixformer.contrib.vllm_flash_attn.flash_attn_varlen_func`
|
|
|
|
|
|
|
|
|
|
|
|
当前环境中至少有关键 FlashInfer wrapper 没导入成功,导致 wrapper 为 `None`,在 profiling 阶段调用时报错。
|
|
|
|
|
|
|
|
|
|
|
|
### 实验结论
|
|
|
|
|
|
|
|
|
|
|
|
本轮没有进入 c1/c2/c4 decode 曲线压测,因为 multi-step 服务在启动阶段就失败。
|
|
|
|
|
|
|
|
|
|
|
|
结论不是“调度一定无效”,而是:
|
|
|
|
|
|
|
|
|
|
|
|
1. 当前 xFormers backend 下,multi-step 调度不可用。
|
|
|
|
|
|
2. 当前 FlashInfer backend 依赖不完整或与 CoreX 环境不兼容,不能直接替代 xFormers。
|
|
|
|
|
|
3. 自动 FlashAttention 也不可用;此前日志已显示 `vllm_flash_attn` 包缺失,因此自动回落到 xFormers。
|
|
|
|
|
|
|
|
|
|
|
|
因此,调度优化如果要继续推进,前置任务是 attention backend 适配:
|
|
|
|
|
|
|
|
|
|
|
|
- 路线 A:补齐/修复 CoreX 环境里的 FlashAttention 或 FlashInfer backend。
|
|
|
|
|
|
- 路线 B:改造 xFormers backend 或 MultiStepModelRunner,使其支持当前 xFormers 路径。
|
|
|
|
|
|
- 路线 C:绕过 multi-step,直接 profile xFormers decode、paged attention、MoE 与 TP 通信开销。
|
|
|
|
|
|
|
|
|
|
|
|
### 对下一步方向的影响
|
|
|
|
|
|
|
|
|
|
|
|
短期内,继续调 `--num-scheduler-steps` 没意义;它被 backend 卡住了。
|
|
|
|
|
|
|
|
|
|
|
|
下一步建议改为两条线并行:
|
|
|
|
|
|
|
|
|
|
|
|
1. **backend 可用性线**
|
|
|
|
|
|
- 检查 `flashinfer` 和 `ixformer.contrib.vllm_flash_attn` 在服务器上的实际导入错误。
|
|
|
|
|
|
- 确认官方镜像/包中是否本应包含 `vllm_flash_attn`。
|
|
|
|
|
|
- 如果能补齐依赖,再重跑 multi-step decode 曲线。
|
|
|
|
|
|
|
|
|
|
|
|
2. **代码 profiling 线**
|
|
|
|
|
|
- 直接在当前可用 xFormers 路径插桩。
|
|
|
|
|
|
- 重点记录每 token decode 中 attention、MoE、sampler、TP 同步的耗时。
|
|
|
|
|
|
- 当前平均 GPU 利用率低,profiling 比继续盲调参数更有价值。
|
2026-07-14 19:18:46 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-14:解除强制 eager / custom all-reduce 禁用验证
|
|
|
|
|
|
|
|
|
|
|
|
### 修改内容
|
|
|
|
|
|
|
|
|
|
|
|
本轮先处理 `vllm/engine/arg_utils.py` 中两个会掩盖真实性能路径的硬编码:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
enforce_eager=True
|
|
|
|
|
|
disable_custom_all_reduce=True
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
改为尊重 CLI / dataclass 参数:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
enforce_eager=self.enforce_eager
|
|
|
|
|
|
disable_custom_all_reduce=self.disable_custom_all_reduce
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
同时更新 `qwen3_6_scripts/patch_xformers_sdpa_seq.py`,让后续重新执行 patchops 时也会保留该行为。服务器实际运行路径已确认:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
PYTHONPATH=/usr/local/corex/lib/python3/dist-packages:/usr/local/corex/lib64/python3/dist-packages
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
因此运行时同时 patch 了:
|
|
|
|
|
|
|
|
|
|
|
|
- `/usr/local/corex/lib/python3/dist-packages/vllm/engine/arg_utils.py`
|
|
|
|
|
|
- `/usr/local/corex/lib64/python3/dist-packages/vllm/engine/arg_utils.py`
|
|
|
|
|
|
|
|
|
|
|
|
远端运行时备份:
|
|
|
|
|
|
|
|
|
|
|
|
- `arg_utils.py.bak_20260714_eager_allreduce`
|
|
|
|
|
|
|
|
|
|
|
|
### 实验 1:CUDA Graph + custom all-reduce 同时开启
|
|
|
|
|
|
|
|
|
|
|
|
启动命令不再带 `--enforce-eager`,也不带 `--disable-custom-all-reduce`。
|
|
|
|
|
|
|
|
|
|
|
|
日志确认配置已生效:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
disable_custom_all_reduce=False
|
|
|
|
|
|
enforce_eager=False
|
|
|
|
|
|
use_async_output_proc=True
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
结果:服务未能完成启动,长时间卡在 CUDA Graph capture 阶段。
|
|
|
|
|
|
|
|
|
|
|
|
关键日志:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
Capturing the model for CUDA graphs.
|
|
|
|
|
|
[W CUDAGraph.cpp:145] Warning: Waiting for pending NCCL work to finish before starting graph capture.
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
判断:当前 Iluvatar BI-V100 + CoreX + xFormers + TP=4 路径下,CUDA Graph capture 不可直接启用。它没有快速报错,而是卡在 graph capture / NCCL pending work 阶段,风险比普通参数不兼容更高。短期不建议继续沿 CUDA Graph 方向盲试。
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-eager-allreduce/server_exp_graph_allreduce_seq2_b8192.log`
|
|
|
|
|
|
|
|
|
|
|
|
### 实验 2:仅开启 custom all-reduce,继续 eager
|
|
|
|
|
|
|
|
|
|
|
|
启动命令保留 `--enforce-eager`,但不再带 `--disable-custom-all-reduce`。
|
|
|
|
|
|
|
|
|
|
|
|
日志确认:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
enforce_eager=True
|
|
|
|
|
|
disable_custom_all_reduce=False
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
服务可以正常启动并通过 `/health`。
|
|
|
|
|
|
|
|
|
|
|
|
decode microbench 结果:
|
|
|
|
|
|
|
|
|
|
|
|
| 配置 | 成功率 | TTFT P90 | Output TPS P10/req | Aggregate Output TPS | 对比旧结果 |
|
|
|
|
|
|
| --- | ---: | ---: | ---: | ---: | --- |
|
|
|
|
|
|
| short c1, 256 tok, custom AR on | 100% | 3.47s | 8.47 | 7.94 | 旧 full-parser c1 为 P10 8.74 / aggregate 8.34,略降 |
|
|
|
|
|
|
| short c2, 128 tok, custom AR on | 100% | 1.46s | 4.22 | 8.07 | 旧 parser-off c2 aggregate 7.23,略升但口径不完全相同 |
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-eager-allreduce/decode_eager_custom_ar_short_c1_t256_r3.json`
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-eager-allreduce/decode_eager_custom_ar_short_c2_t128_r4.json`
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-eager-allreduce/server_exp_eager_custom_ar_seq2_b8192_retry.log`
|
|
|
|
|
|
|
|
|
|
|
|
### 结论
|
|
|
|
|
|
|
|
|
|
|
|
1. 之前的硬编码确实屏蔽了真实配置,本轮已经解除,并确认修改落在实际运行的 CoreX site-packages 路径里。
|
|
|
|
|
|
2. CUDA Graph 当前不兼容或存在严重启动卡死问题,不适合作为短期主优化方向。
|
|
|
|
|
|
3. custom all-reduce 可以启动和推理,但收益有限:c2 聚合吞吐有小幅提升,c1 无提升。
|
|
|
|
|
|
4. 当前 decode 吞吐仍在 8 tok/s 左右,距离 Output TPS P10 >= 20 仍有明显差距,瓶颈不只是 all-reduce 开关。
|
|
|
|
|
|
|
|
|
|
|
|
### 下一步 profiling 方向
|
|
|
|
|
|
|
|
|
|
|
|
优先进入代码级 profiling,而不是继续调 CLI 开关:
|
|
|
|
|
|
|
|
|
|
|
|
1. 在 `ModelRunner.execute_model` 统计模型 forward、logits、sample 的阶段耗时。
|
|
|
|
|
|
2. 在 Qwen MoE 层统计 attention、MoE expert、MoE gate、TP all-reduce 的耗时占比。
|
|
|
|
|
|
3. 在 fused MoE 路径统计 topk、expert kernel、activation、sum 的耗时。
|
|
|
|
|
|
4. 用 `ENGINEX_PROFILE_DECODE=1` 这类环境变量控制插桩,只在短压测时开启,避免污染正式结果。
|
|
|
|
|
|
|
|
|
|
|
|
初步判断:custom all-reduce 不是第一大瓶颈;更可能的主战场是 xFormers decode attention、MoE 小 batch kernel、以及 TP 下大量小 kernel / 同步造成的低 GPU 利用率。
|
2026-07-14 20:44:05 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-14:代码级 profiling 与第一轮 MoE 优化
|
|
|
|
|
|
|
|
|
|
|
|
### profiling 插桩
|
|
|
|
|
|
|
|
|
|
|
|
新增环境变量控制的 profiling:
|
|
|
|
|
|
|
|
|
|
|
|
- `ENGINEX_PROFILE_DECODE=1`:开启 profiling。
|
|
|
|
|
|
- `ENGINEX_PROFILE_EVERY=N`:每 N 次 model forward 打印一次累计统计。
|
|
|
|
|
|
- `ENGINEX_PROFILE_SYNC=1`:每段计时前后 `torch.cuda.synchronize()`,用于定位 GPU 时间。
|
|
|
|
|
|
|
|
|
|
|
|
插桩位置:
|
|
|
|
|
|
|
|
|
|
|
|
- `vllm/worker/model_runner.py`
|
|
|
|
|
|
- `attn_state.begin_forward`
|
|
|
|
|
|
- `model_forward`
|
|
|
|
|
|
- `compute_logits`
|
|
|
|
|
|
- `sample`
|
|
|
|
|
|
- `qwen3_6_scripts/qwen3_5.py`
|
|
|
|
|
|
- `full_attention`: qkv projection / norm+rope / paged attention / gate+o_proj
|
|
|
|
|
|
- `linear_attention`: GatedDeltaNet 整层
|
|
|
|
|
|
- `MoE`: gate / routing topk / routed experts / shared expert / TP all-reduce
|
|
|
|
|
|
- `DecoderLayer`: norm / attention / MLP
|
|
|
|
|
|
|
|
|
|
|
|
注意:profiling 强制同步会显著拖慢请求,因此 profiling 结果只用于定位瓶颈,不作为真实性能分数。
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-code-profile/server_profile_mode_eager_custom_ar_seq2_b8192.log`
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-code-profile/profile_mode_eager_custom_ar_short_c1_t24_r1.json`
|
|
|
|
|
|
|
|
|
|
|
|
### 关键 profiling 结果
|
|
|
|
|
|
|
|
|
|
|
|
短请求 `c1, max_tokens=24`,修正标签后,rank0 在 step=24 的主要累计耗时:
|
|
|
|
|
|
|
|
|
|
|
|
| 模块 | 总耗时 | 平均单层/次 | 次数 | 判断 |
|
|
|
|
|
|
| --- | ---: | ---: | ---: | --- |
|
|
|
|
|
|
| `prefill.moe.routed_prefill_experts` | 5239.90ms | 65.50ms | 80 | 首 token 慢的最大来源 |
|
|
|
|
|
|
| `prefill.layer.linear_attention` | 2944.76ms | 49.08ms | 60 | prefill 第二大来源 |
|
|
|
|
|
|
| `decode.layer.mlp` | 1456.57ms | 1.66ms | 880 | decode 最大来源 |
|
|
|
|
|
|
| `decode.layer.linear_attention` | 942.63ms | 1.43ms | 660 | decode 第二大来源 |
|
|
|
|
|
|
| `decode.moe.routed_total` | 785.65ms | 0.89ms | 880 | MLP 中 routed expert 为主 |
|
|
|
|
|
|
| `decode.moe.routed_decode_experts` | 540.24ms | 0.61ms | 880 | 单 token MoE expert 计算 |
|
|
|
|
|
|
| `decode.layer.full_attention` | 322.06ms | 1.46ms | 220 | full attention 不是第一瓶颈 |
|
|
|
|
|
|
| `decode.moe.tp_all_reduce` | 265.92ms | 0.30ms | 880 | 通信有成本,但不是最大项 |
|
|
|
|
|
|
|
|
|
|
|
|
`ModelRunner` 粗粒度:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
decode.model_forward avg ~= 145.6ms
|
|
|
|
|
|
decode.compute_logits avg ~= 1.17ms
|
|
|
|
|
|
decode.sample avg ~= 1.01ms
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
结论:decode 慢主要发生在模型 forward 内部;logits 和 sampler 不是主瓶颈。
|
|
|
|
|
|
|
|
|
|
|
|
### 为什么 GPU 算力打不满
|
|
|
|
|
|
|
|
|
|
|
|
当前路径的 GPU 利用率低,不是因为单个大矩阵乘算不过来,而是因为每 token 被拆成大量小工作:
|
|
|
|
|
|
|
|
|
|
|
|
1. **MoE 没有真正 fused kernel**
|
|
|
|
|
|
- 注释里已经说明 BI-V100 上缺少 `vllm_moe_topk_softmax / vllm_invoke_fused_moe_kernel`。
|
|
|
|
|
|
- 当前 routed expert 是纯 PyTorch 实现。
|
|
|
|
|
|
- prefill 通用路径按 expert 做 Python 循环,长 prompt 下会产生大量小 GEMM 和 CPU/GPU 同步点。
|
|
|
|
|
|
|
|
|
|
|
|
2. **decode batch 太小**
|
|
|
|
|
|
- `max_num_seqs=2` 时每步只有 1-2 token。
|
|
|
|
|
|
- 小 batch 下矩阵乘规模小,kernel launch、Python 调度、TP 同步成本占比很高。
|
|
|
|
|
|
|
|
|
|
|
|
3. **模型结构有大量 GatedDeltaNet linear_attention 层**
|
|
|
|
|
|
- profiling 显示 linear_attention 在 prefill 和 decode 都是大头之一。
|
|
|
|
|
|
- 这部分不是标准 paged attention,不能靠换 xFormers attention backend 直接解决。
|
|
|
|
|
|
|
|
|
|
|
|
4. **TP all-reduce 不是第一瓶颈,但放大了小 kernel 问题**
|
|
|
|
|
|
- decode MoE all-reduce 单次约 0.30ms。
|
|
|
|
|
|
- 单次看不大,但每层一次、每 token 多次累积,且会让 rank 间等待更明显。
|
|
|
|
|
|
|
|
|
|
|
|
### 第一轮针对性优化:MoE tiny-batch fast path
|
|
|
|
|
|
|
|
|
|
|
|
发现:原代码只有 `T == 1` 的 MoE decode fast path。一旦并发 decode `T == 2`,会落入通用 `routed_prefill_experts` 路径:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
unique_eids = topk_ids.view(-1).unique().tolist()
|
|
|
|
|
|
for eid in unique_eids:
|
|
|
|
|
|
...
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这条路径适合大 prefill,但不适合 `max_num_seqs=2` 的小批量 decode。
|
|
|
|
|
|
|
|
|
|
|
|
本轮新增 `T <= 2` fast path:
|
|
|
|
|
|
|
|
|
|
|
|
- 将 `T * top_k` 个选中 expert 展平。
|
|
|
|
|
|
- 用两次 batched `torch.bmm` 计算 gate/up 和 down。
|
|
|
|
|
|
- 避免 Python per-expert loop。
|
|
|
|
|
|
- 保留 `T == 1` 原 fast path 不变。
|
|
|
|
|
|
|
|
|
|
|
|
### 优化结果
|
|
|
|
|
|
|
|
|
|
|
|
对比同口径 `short c2, max_tokens=128, requests=4`:
|
|
|
|
|
|
|
|
|
|
|
|
| 版本 | 成功率 | TTFT P90 | per-request Output TPS P10 | Aggregate Output TPS |
|
|
|
|
|
|
| --- | ---: | ---: | ---: | ---: |
|
|
|
|
|
|
| 优化前 custom all-reduce on | 100% | 1.46s | 4.22 | 8.07 |
|
|
|
|
|
|
| tiny-batch MoE fast path | 100% | 4.42s | 5.95 | 10.60 |
|
|
|
|
|
|
|
|
|
|
|
|
decode 聚合吞吐提升约 **31%**。TTFT 变差可能来自冷缓存/加载后首次请求抖动,后续需要用多轮 warmup 后再复测。
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-code-profile/decode_tiny_batch_moe_short_c2_t128_r4.json`
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-code-profile/server_tiny_batch_moe_eager_custom_ar_seq2_b8192.log`
|
|
|
|
|
|
|
|
|
|
|
|
### 下一步优化方向
|
|
|
|
|
|
|
|
|
|
|
|
优先级从高到低:
|
|
|
|
|
|
|
|
|
|
|
|
1. **MoE prefill 路径**
|
|
|
|
|
|
- 当前 `prefill.moe.routed_prefill_experts` 是 TTFT 最大来源。
|
|
|
|
|
|
- 需要把 Python per-expert loop 替换成更批量化的 grouped GEMM / batched GEMM。
|
|
|
|
|
|
- 官方负载长上下文输入占比极高,提升 prefill 会直接改善 TTFT 和 weighted throughput。
|
|
|
|
|
|
|
|
|
|
|
|
2. **GatedDeltaNet linear_attention**
|
|
|
|
|
|
- decode 和 prefill 都是大头。
|
|
|
|
|
|
- 需要进一步拆分 projection、conv/update、state update、out_proj,确认是 recurrent update 还是投影占主。
|
|
|
|
|
|
|
|
|
|
|
|
3. **更稳的并发策略**
|
|
|
|
|
|
- tiny-batch MoE 已证明 `T=2` 能受益。
|
|
|
|
|
|
- 后续可测试 `max_num_seqs=3/4`,但受 100K 上下文 KV cache 与 TTFT 影响,需要小心。
|
|
|
|
|
|
|
|
|
|
|
|
4. **TP all-reduce 合并**
|
|
|
|
|
|
- 当前 MoE 每层 routed+shared 后做一次 all-reduce。
|
|
|
|
|
|
- 如果后续能将若干小通信或 residual 路径合并,可能进一步改善 decode 抖动,但优先级低于 MoE/linear_attention 计算本体。
|
2026-07-14 21:49:55 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-14:MoE decode fast path v2(tokenwise)
|
|
|
|
|
|
|
|
|
|
|
|
### 背景
|
|
|
|
|
|
|
|
|
|
|
|
上一轮 `T <= 2` tiny-batch fast path 使用 batched `torch.bmm`,将 `T * top_k` 个 expert 选择展平后批量计算。它已经将 c2 aggregate Output TPS 从 8.07 提升到 10.60。
|
|
|
|
|
|
|
|
|
|
|
|
继续分析后发现,对于极小 batch(`T=2`):
|
|
|
|
|
|
|
|
|
|
|
|
- 第一段 gate/up projection 用 `torch.bmm` 实际是 `T * top_k` 个 `1 x H` 小矩阵乘。
|
|
|
|
|
|
- 原 `T == 1` 路径用的是 `F.linear(hidden, w13_sel.reshape(-1, H))`,会形成一个更大的 GEMM,通常更适合 GPU。
|
|
|
|
|
|
|
|
|
|
|
|
因此新增 v2:`tokenwise` tiny-batch 实现。
|
|
|
|
|
|
|
|
|
|
|
|
### 实现
|
|
|
|
|
|
|
|
|
|
|
|
新增环境变量:
|
|
|
|
|
|
|
|
|
|
|
|
- `ENGINEX_MOE_TINY_IMPL=tokenwise|bmm`
|
|
|
|
|
|
- `ENGINEX_MOE_TINY_MAX=4`
|
|
|
|
|
|
|
|
|
|
|
|
默认:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
ENGINEX_MOE_TINY_IMPL=tokenwise
|
|
|
|
|
|
ENGINEX_MOE_TINY_MAX=4
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
核心逻辑:
|
|
|
|
|
|
|
|
|
|
|
|
- 对 `T <= 4` 的小批量 decode,逐 token 走已经验证过的 `T == 1` 大 `F.linear` 路径。
|
|
|
|
|
|
- 每个 token 内仍然把 top-k expert 的 `w13` 拼成一个大权重,减少第一段 projection 的小 GEMM。
|
|
|
|
|
|
- 保留上一轮 batched bmm 实现,可通过 `ENGINEX_MOE_TINY_IMPL=bmm` 回退做 A/B 测试。
|
|
|
|
|
|
|
|
|
|
|
|
代码位置:
|
|
|
|
|
|
|
|
|
|
|
|
- `qwen3_6_scripts/qwen3_5.py`
|
|
|
|
|
|
- `Qwen3_5MoeSparseBlock._pure_pytorch_experts`
|
|
|
|
|
|
|
|
|
|
|
|
### 结果
|
|
|
|
|
|
|
|
|
|
|
|
同口径 `short c2, max_tokens=128, requests=4`:
|
|
|
|
|
|
|
|
|
|
|
|
| 版本 | 成功率 | TTFT P90 | per-request Output TPS P10 | Aggregate Output TPS |
|
|
|
|
|
|
| --- | ---: | ---: | ---: | ---: |
|
|
|
|
|
|
| custom all-reduce on,优化前 | 100% | 1.46s | 4.22 | 8.07 |
|
|
|
|
|
|
| tiny-batch bmm | 100% | 4.42s | 5.95 | 10.60 |
|
|
|
|
|
|
| tiny-batch tokenwise v2 | 100% | 4.49s | 6.43 | 11.28 |
|
|
|
|
|
|
|
|
|
|
|
|
相对上一版 bmm,aggregate Output TPS 又提升约 **6.4%**;相对优化前提升约 **39.8%**。
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-code-profile/decode_moe_tokenwise_short_c2_t128_r4.json`
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-14-code-profile/server_moe_tokenwise_eager_custom_ar_seq2_b8192.log`
|
|
|
|
|
|
|
|
|
|
|
|
### 结论
|
|
|
|
|
|
|
|
|
|
|
|
这个结果说明:对当前 BI-V100 + PyTorch fallback MoE 路径,**将小 GEMM 尽量合并为较大的 per-token GEMM** 比把所有 token/expert 都塞进 batched bmm 更好。瓶颈确实集中在 decode MoE routed expert 的小矩阵计算与调度开销。
|
|
|
|
|
|
|
|
|
|
|
|
下一步可继续沿两个方向走:
|
|
|
|
|
|
|
|
|
|
|
|
1. 测试 `max_num_seqs=4`,利用 `T <= 4` tokenwise fast path,看 aggregate Output TPS 是否继续上涨。
|
|
|
|
|
|
2. 继续优化 shared expert / linear_attention,因为 routed expert 已经明显改善,decode 下一个大头会逐渐转向 `linear_attention` 和 shared expert。
|
2026-07-15 00:49:24 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-15:平台提交构建/启动配置修复
|
|
|
|
|
|
|
|
|
|
|
|
### 背景
|
|
|
|
|
|
|
|
|
|
|
|
官方平台仍提示当前提交的 Docker 镜像构建失败。对比一个已能进入 benchmark-agent 阶段的成功案例仓库 `fangfangssj/enginex-vllm-bi100-qwen36` 后,确认我们的根目录结构没有问题,根目录已有 `Dockerfile` 和 `computility-run.yaml`。
|
|
|
|
|
|
|
|
|
|
|
|
成功案例日志中的 `BENCHMARK_DOCKER_IMAGE_NOT_READY` 只是镜像正在下载,随后已经创建 benchmark-agent 容器;因此该案例至少通过了代码仓库识别、镜像构建和镜像拉取阶段。
|
|
|
|
|
|
|
|
|
|
|
|
### 对齐内容
|
|
|
|
|
|
|
|
|
|
|
|
本次将当前仓库的平台提交相关文件对齐成功案例:
|
|
|
|
|
|
|
|
|
|
|
|
- `computility-run.yaml`
|
|
|
|
|
|
- 上下文从 `100000` 调整为 `245000`,满足官方说明中 235K+ 输入窗口需求。
|
|
|
|
|
|
- `max_num_batched_tokens` 从 `8192` 调整为 `16384`。
|
|
|
|
|
|
- 增加 `--chat-template /workspace/chat_template_multi_system.jinja`,兼容多/乱序 system 消息。
|
|
|
|
|
|
- 增加 `PYTHONPATH`、`LD_LIBRARY_PATH`、`PATH` 和五个 native 优化开关环境变量。
|
|
|
|
|
|
- `qwen3_6_scripts/patch_ops.sh`
|
|
|
|
|
|
- 增加 CoreX/OpenMPI 环境变量。
|
|
|
|
|
|
- 增加 `libcusolver.so` 软链,避免 `torch.linalg.solve_triangular` 在容器内找不到库。
|
|
|
|
|
|
- 增加 multi-system chat template 部署。
|
|
|
|
|
|
- 增加 patch 后验证输出。
|
|
|
|
|
|
- 新增/更新成功案例依赖文件:
|
|
|
|
|
|
- `qwen3_6_scripts/chat_template_multi_system.jinja`
|
|
|
|
|
|
- `qwen3_6_scripts/xformers.py`
|
|
|
|
|
|
- `qwen3_6_scripts/paged_attn.py`
|
|
|
|
|
|
- `qwen3_6_scripts/patch_xformers_sdpa_seq.py`
|
|
|
|
|
|
- `qwen3_6_scripts/protocol.py`
|
|
|
|
|
|
- `qwen3_6_scripts/qwen3_5.py`
|
|
|
|
|
|
|
|
|
|
|
|
### 影响
|
|
|
|
|
|
|
|
|
|
|
|
这次优先目标是恢复平台可构建、可启动、可进入 benchmark-agent 验证流程。为降低平台构建风险,直接采用成功案例中已验证的一组补丁文件;此前本地 MoE tiny-batch 实验代码可能被成功案例的 native MoE/GDN 优化实现覆盖,后续如果平台构建通过,再基于这套可提交基线继续做性能优化分支。
|
2026-07-15 01:28:06 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-15:MoE tiny-batch 矩阵实验
|
|
|
|
|
|
|
|
|
|
|
|
### 实验过程
|
|
|
|
|
|
|
|
|
|
|
|
远端实验目录:
|
|
|
|
|
|
|
|
|
|
|
|
- `/root/work/logs/moe_tiny_matrix_20260715_codex_05`
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-15-moe-tiny-matrix/`
|
|
|
|
|
|
|
|
|
|
|
|
前两次启动没有形成有效结果:
|
|
|
|
|
|
|
|
|
|
|
|
- `codex_03`:未加 `--enforce-eager`,服务加载后卡在 CUDA Graph capture,`/health` 一直拒绝连接。
|
|
|
|
|
|
- `codex_04`:已加 `--enforce-eager`,但旧的 `codex_03` 服务仍占用显存,导致新服务加载 MoE 权重时 OOM。
|
|
|
|
|
|
|
|
|
|
|
|
有效实验为 `codex_05`,先清理旧进程,再用 `--enforce-eager` 重跑 5 组矩阵。
|
|
|
|
|
|
|
|
|
|
|
|
固定压测口径:
|
|
|
|
|
|
|
|
|
|
|
|
- `prompt_mode=short`
|
|
|
|
|
|
- `concurrency=2`
|
|
|
|
|
|
- `requests=4`
|
|
|
|
|
|
- `max_tokens=128`
|
|
|
|
|
|
- `max_num_seqs=2`
|
|
|
|
|
|
- `max_model_len=100000`
|
|
|
|
|
|
- `tp=4`
|
|
|
|
|
|
- `enable_prefix_caching`
|
|
|
|
|
|
- `enable_chunked_prefill`
|
|
|
|
|
|
|
|
|
|
|
|
### 结果
|
|
|
|
|
|
|
|
|
|
|
|
| case | success | TTFT P90 | Output TPS P10 | Output TPS P50 | Aggregate Output TPS | Avg GPU util | Max GPU util |
|
|
|
|
|
|
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
|
|
|
|
| `bmm_max16` | 100.00% | 1.401 | 6.245 | 6.381 | 11.958 | 53.2 | 100.0 |
|
|
|
|
|
|
| `bmm_max8` | 100.00% | 1.424 | 6.900 | 6.957 | 12.907 | 54.7 | 100.0 |
|
|
|
|
|
|
| `tokenwise_max16` | 100.00% | 1.312 | 6.292 | 6.387 | 12.003 | 54.8 | 100.0 |
|
|
|
|
|
|
| `tokenwise_max4` | 100.00% | 1.671 | 6.377 | 6.416 | 11.868 | 51.4 | 100.0 |
|
|
|
|
|
|
| `tokenwise_max8` | 100.00% | 1.589 | 6.347 | 6.357 | 11.788 | 54.6 | 100.0 |
|
|
|
|
|
|
|
|
|
|
|
|
### 结论
|
|
|
|
|
|
|
|
|
|
|
|
本轮最佳结果是:
|
|
|
|
|
|
|
|
|
|
|
|
- `ENGINEX_MOE_TINY_IMPL=bmm`
|
|
|
|
|
|
- `ENGINEX_MOE_TINY_MAX=8`
|
|
|
|
|
|
- Aggregate Output TPS = `12.907`
|
|
|
|
|
|
- Output TPS P10 = `6.900`
|
|
|
|
|
|
|
|
|
|
|
|
相对上一轮 `tokenwise_max4` 的有效结果 `11.28`,本轮 `bmm_max8` 聚合吞吐约提升 `14.4%`。但要注意两轮测试的启动环境不同:本轮强制 `--enforce-eager`,且经过多次重启,不能把这个提升完全归因于 `bmm` 实现。
|
|
|
|
|
|
|
|
|
|
|
|
更重要的是,这个矩阵**没有真正验证 `T > TINY_MAX` 后按块拆分是否有收益**。原因是本轮压测固定 `max_num_seqs=2`,decode 阶段 `T` 基本是 1-2,因此 `TINY_MAX=4/8/16` 都会命中同一 tiny fast path。也就是说:
|
|
|
|
|
|
|
|
|
|
|
|
- `tokenwise_max4/8/16` 之间的差异主要是重启和运行噪声。
|
|
|
|
|
|
- `bmm_max8/16` 之间的差异也主要是噪声。
|
|
|
|
|
|
- 本轮更有价值的对比是 `tokenwise` 与 `bmm` 两种 tiny implementation。
|
|
|
|
|
|
|
|
|
|
|
|
从本轮结果看,在 `T≈2` 的短输出 decode 场景里,`bmm` 路径不差于 `tokenwise`,并且本次最佳是 `bmm_max8`。下一步如果要验证“`T` 大于阈值后分块计算”的思路,需要把 `max_num_seqs` 提到 8 或 16,或构造能让单步 decode 聚合更多 token 的压测,否则 `TINY_MAX` 参数不会真正生效。
|
2026-07-15 01:54:56 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-15:Docker build CRLF 根因修复
|
|
|
|
|
|
|
|
|
|
|
|
### 现象
|
|
|
|
|
|
|
|
|
|
|
|
平台持续提示 Docker 镜像构建失败。将当前 Git HEAD 通过 `git archive` 打包到远端 Linux 后执行:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd /root/work/submission_head/qwen3_6_scripts
|
|
|
|
|
|
./patch_ops.sh
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
首次复现到错误:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
./patch_ops.sh: line 11: $'\r': command not found
|
|
|
|
|
|
./patch_ops.sh: line 217: syntax error: unexpected end of file
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 根因
|
|
|
|
|
|
|
|
|
|
|
|
本地 Git 配置 `core.autocrlf=true`。在没有 `.gitattributes` 约束时,`git archive` 导出的 `patch_ops.sh` 变成 CRLF 行尾。Linux/Docker 中执行 `RUN cd ./qwen3_6_scripts && ./patch_ops.sh` 会直接失败。
|
|
|
|
|
|
|
|
|
|
|
|
### 修复
|
|
|
|
|
|
|
|
|
|
|
|
新增 `.gitattributes`,强制构建相关文本文件以 LF 行尾导出:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
Dockerfile text eol=lf
|
|
|
|
|
|
*.sh text eol=lf
|
|
|
|
|
|
*.py text eol=lf
|
|
|
|
|
|
*.yaml text eol=lf
|
|
|
|
|
|
*.yml text eol=lf
|
|
|
|
|
|
*.jinja text eol=lf
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
重新 `git archive HEAD` 后,本地确认 `patch_ops.sh` 行尾为 LF。再传到远端执行,`patch_ops.sh` 已完整跑通,末尾验证:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
[ok] MOE_NATIVE
|
|
|
|
|
|
[ok] _native_experts_sorted
|
|
|
|
|
|
[ok] CHUNK_PARALLEL
|
|
|
|
|
|
[ok] LOOP1_NATIVE
|
|
|
|
|
|
[ok] RMSNORM_NATIVE
|
|
|
|
|
|
[ok] PREFIX_FLASH
|
|
|
|
|
|
[ok] _forward_prefix_flash
|
|
|
|
|
|
[ok] libcusolver.so link
|
|
|
|
|
|
[ok] /workspace/chat_template_multi_system.jinja
|
|
|
|
|
|
[ok] torch.linalg.solve_triangular callable
|
|
|
|
|
|
=> all five optimizations deployed and enabled
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
对应远端日志:
|
|
|
|
|
|
|
|
|
|
|
|
- `/root/work/logs/patch_head_lf_20260715.log`
|
2026-07-15 03:23:39 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-15:官方相似负载基线与启动稳定性
|
|
|
|
|
|
|
|
|
|
|
|
### 配置
|
|
|
|
|
|
|
|
|
|
|
|
本轮使用官方相似合成数据集:
|
|
|
|
|
|
|
|
|
|
|
|
- 远端数据集:`/root/work/logs/synthetic_cumulative_8_s3_cap40k.jsonl`
|
|
|
|
|
|
- 请求数:8
|
|
|
|
|
|
- 并发:1
|
|
|
|
|
|
- `max_tokens=256`
|
|
|
|
|
|
- `--max-model-len 100000`
|
|
|
|
|
|
- `--max-num-seqs 1`
|
|
|
|
|
|
- `--max-num-batched-tokens 16384`
|
|
|
|
|
|
- `--enable-chunked-prefill`
|
|
|
|
|
|
- `--enable-prefix-caching`
|
|
|
|
|
|
- `--chat-template /workspace/chat_template_multi_system.jinja`
|
|
|
|
|
|
- `--enforce-eager`
|
|
|
|
|
|
- Native 优化环境:`MOE_NATIVE=1 CHUNK_PARALLEL=1 PREFIX_FLASH=1 RMSNORM_NATIVE=1 LOOP1_NATIVE=1`
|
|
|
|
|
|
|
|
|
|
|
|
本地归档结果:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-15-official-like-baseline/eager_c1_r8_t256_cap40k.json`
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-15-official-like-baseline/patch_head_lf_20260715.log`
|
|
|
|
|
|
|
|
|
|
|
|
### 结果
|
|
|
|
|
|
|
|
|
|
|
|
| 指标 | 当前值 | 目标 | 结论 |
|
|
|
|
|
|
| --- | ---: | ---: | --- |
|
|
|
|
|
|
| Success Rate | 100.00% | >= 99% | 通过 |
|
|
|
|
|
|
| Cache Hit Rate | 74.17% | >= 50% | 通过 |
|
|
|
|
|
|
| TTFT P90 | 9.803s | <= 5s | 未通过 |
|
|
|
|
|
|
| Output TPS P10 | 5.825 | >= 20 | 未通过 |
|
|
|
|
|
|
| Weighted Token Throughput | 883.109 | >= 8000 | 未通过 |
|
|
|
|
|
|
| Aggregate Output TPS | 5.789 | - | 参考 |
|
|
|
|
|
|
| Aggregate Uncached Input TPS | 178.330 | - | 参考 |
|
|
|
|
|
|
| Aggregate Cache TPS | 512.015 | - | 参考 |
|
|
|
|
|
|
|
|
|
|
|
|
### 观察
|
|
|
|
|
|
|
|
|
|
|
|
官方相似负载下,当前瓶颈不再是模板兼容或缓存命中率,而是长上下文 prefill/缓存读取吞吐和 decode routed expert 吞吐都偏低。8 条请求中,短输出 decode 约 5.8-8.5 tok/s;TTFT P90 被第二条长请求拉到 9.8s。
|
|
|
|
|
|
|
|
|
|
|
|
不加 `--enforce-eager` 的 CUDA Graph 路径在 BI-V100 当前环境中启动 OOM,典型日志为:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
torch.cuda.OutOfMemoryError: Tried to allocate 256MiB. GPU 1 ...
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
因此提交平台配置先加入 `--enforce-eager`,目标是保证镜像启动和评测稳定。后续如果要重新打开 CUDA Graph,需要先解决图捕获阶段额外显存开销,否则平台会直接启动失败。
|
|
|
|
|
|
|
|
|
|
|
|
### 异常处理
|
|
|
|
|
|
|
|
|
|
|
|
尝试 `--max-num-batched-tokens 32768` 后服务启动 OOM,并留下 GPU1-3 约 30GB 显存残留。`ixsmi` 进程表为空,但 `ps` 发现上一次 worker 残留为 `multiprocessing.spawn` 子进程。清理残留进程后,显存恢复到每卡约 257MiB。
|
|
|
|
|
|
|
|
|
|
|
|
结论:后续每次 OOM 后必须先确认:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
ps -ef | grep -E 'multiprocessing.spawn|resource_tracker|vllm.entrypoints|api_server'
|
|
|
|
|
|
ixsmi
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
若存在残留 worker,需清理后再继续实验,否则会把脏显存误判为参数不可行。
|
|
|
|
|
|
|
|
|
|
|
|
### 下一步
|
|
|
|
|
|
|
|
|
|
|
|
1. 以 `--enforce-eager + max_num_batched_tokens=16384` 作为稳定提交基线。
|
|
|
|
|
|
2. 在干净显存下重新验证 `max_num_batched_tokens=32768` 是否必然 OOM。
|
|
|
|
|
|
3. 对官方相似负载做参数矩阵:`max_num_batched_tokens`、`gpu_memory_utilization`、`max_model_len`、`max_num_seqs`。
|
|
|
|
|
|
4. 若参数调优收益不足,继续深入 `PREFIX_FLASH` 和 MoE routed expert 的 native/kernel 路径 profiling。
|
2026-07-15 03:55:20 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-15:245K 平台等价配置验证
|
|
|
|
|
|
|
|
|
|
|
|
### 重要启动细节
|
|
|
|
|
|
|
|
|
|
|
|
远端手工测试时,不能从仓库根目录启动:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd /root/data-disk-1/enginex-vllm-bi100-qwen36
|
|
|
|
|
|
python3 -m vllm.entrypoints.openai.api_server ...
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这样会优先导入仓库内未补丁的 `vllm/`,导致:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
KeyError: 'invalid tool call parser: qwen3_coder (chose from { hermes,internlm,llama3_json,mistral })'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
平台 Dockerfile 只复制 `qwen3_6_scripts` 并执行 `patch_ops.sh`,不会复制仓库根目录的 `vllm/`,因此平台会使用补丁后的 corex vLLM。远端手工测试需要从 `/root` 或 `/workspace` 等非仓库目录启动,以匹配平台行为。
|
|
|
|
|
|
|
|
|
|
|
|
### 245K 启动结果
|
|
|
|
|
|
|
|
|
|
|
|
从 `/root` 启动平台等价配置成功:
|
|
|
|
|
|
|
|
|
|
|
|
- `--max-model-len 245000`
|
|
|
|
|
|
- `--max-seq-len-to-capture 245000`
|
|
|
|
|
|
- `--enforce-eager`
|
|
|
|
|
|
- `--max-num-batched-tokens 16384`
|
|
|
|
|
|
- `--max-num-seqs 1`
|
|
|
|
|
|
- `--tool-call-parser qwen3_coder`
|
|
|
|
|
|
- `--reasoning-parser qwen3`
|
|
|
|
|
|
- `--chat-template /workspace/chat_template_multi_system.jinja`
|
|
|
|
|
|
|
|
|
|
|
|
启动后显存约为:
|
|
|
|
|
|
|
|
|
|
|
|
- GPU0: 28.4GB
|
|
|
|
|
|
- GPU1: 28.3GB
|
|
|
|
|
|
- GPU2: 28.3GB
|
|
|
|
|
|
- GPU3: 28.2GB
|
|
|
|
|
|
|
|
|
|
|
|
说明 `--enforce-eager` 后 245K 正确性配置可以启动,但剩余显存空间已经很紧。
|
|
|
|
|
|
|
|
|
|
|
|
### 245K 官方相似压测
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-15-official-like-baseline/eager_245k_c1_r8_t256_cap40k.json`
|
|
|
|
|
|
|
|
|
|
|
|
| 指标 | 100K 基线 | 245K 配置 | 目标 |
|
|
|
|
|
|
| --- | ---: | ---: | ---: |
|
|
|
|
|
|
| Success Rate | 100.00% | 100.00% | >= 99% |
|
|
|
|
|
|
| Cache Hit Rate | 74.17% | 74.17% | >= 50% |
|
|
|
|
|
|
| TTFT P90 | 9.803s | 24.886s | <= 5s |
|
|
|
|
|
|
| Output TPS P10 | 5.825 | 5.532 | >= 20 |
|
|
|
|
|
|
| Aggregate Output TPS | 5.789 | 4.631 | - |
|
|
|
|
|
|
| Uncached Input TPS | 178.330 | 139.628 | - |
|
|
|
|
|
|
| Cache TPS | 512.015 | 400.894 | - |
|
|
|
|
|
|
| Weighted Throughput | 883.109 | 693.102 | >= 8000 |
|
|
|
|
|
|
|
|
|
|
|
|
245K 配置满足上下文窗口正确性,但对当前小样本性能明显更差。后续优化策略:
|
|
|
|
|
|
|
|
|
|
|
|
1. 平台提交配置保留 245K,以覆盖官方 235K+ 输入上限。
|
|
|
|
|
|
2. 快速性能迭代优先用 100K/cap40K 官方相似数据,减少单轮成本。
|
|
|
|
|
|
3. 确定有效优化后,再回到 245K 配置做确认。
|
|
|
|
|
|
4. 当前离目标最大的差距仍是 prefill/cache 吞吐和 decode MoE 吞吐,而不是缓存命中率或请求成功率。
|
2026-07-15 04:19:56 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-15:MoE router top-k softmax 实验
|
|
|
|
|
|
|
|
|
|
|
|
### 改动
|
|
|
|
|
|
|
|
|
|
|
|
尝试把 MoE router 从:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
routing_weights = torch.softmax(router_logits.float(), dim=-1)
|
|
|
|
|
|
topk_weights, topk_ids = torch.topk(routing_weights, self.top_k, dim=-1)
|
|
|
|
|
|
topk_weights = topk_weights / topk_weights.sum(dim=-1, keepdim=True)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
改为:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
topk_logits, topk_ids = torch.topk(router_logits.float(), self.top_k, dim=-1)
|
|
|
|
|
|
topk_weights = torch.softmax(topk_logits, dim=-1)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
数学上这等价于 “full softmax -> topk -> renormalize”,但避免了完整 `(T, num_experts)` softmax。预期收益主要来自减少每层 decode 路由开销。
|
|
|
|
|
|
|
|
|
|
|
|
### 结果
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-15-official-like-baseline/eager_100k_router_topk_c1_r8_t256_cap40k.json`
|
|
|
|
|
|
|
|
|
|
|
|
| 指标 | 100K 基线 | router top-k softmax | 目标 |
|
|
|
|
|
|
| --- | ---: | ---: | ---: |
|
|
|
|
|
|
| Success Rate | 100.00% | 100.00% | >= 99% |
|
|
|
|
|
|
| Cache Hit Rate | 74.17% | 74.17% | >= 50% |
|
|
|
|
|
|
| TTFT P90 | 9.803s | 13.048s | <= 5s |
|
|
|
|
|
|
| Output TPS P10 | 5.825 | 5.664 | >= 20 |
|
|
|
|
|
|
| Aggregate Output TPS | 5.789 | 5.379 | - |
|
|
|
|
|
|
| Uncached Input TPS | 178.330 | 172.454 | - |
|
|
|
|
|
|
| Cache TPS | 512.015 | 495.142 | - |
|
|
|
|
|
|
| Weighted Throughput | 883.109 | 850.328 | >= 8000 |
|
|
|
|
|
|
|
|
|
|
|
|
### 结论
|
|
|
|
|
|
|
|
|
|
|
|
该改动没有带来收益,反而略降。可能原因:
|
|
|
|
|
|
|
|
|
|
|
|
- full softmax 相对 expert GEMM 和 TP all-reduce 占比很小;
|
|
|
|
|
|
- topk logits 后 softmax 改变了 kernel 调度形态,但没有减少主瓶颈;
|
|
|
|
|
|
- 小样本中输出 token 数不同,仍需看趋势而不是单条。
|
|
|
|
|
|
|
|
|
|
|
|
操作结论:不保留该代码改动,回退到 full-softmax router。下一步继续围绕 MoE expert GEMM、all-reduce、prefill/cache 路径做实验。
|
2026-07-15 04:43:46 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-15:关闭默认 PROF_TRACE
|
|
|
|
|
|
|
|
|
|
|
|
### 改动
|
|
|
|
|
|
|
|
|
|
|
|
`qwen3_5.py` 中 `_prof()` 原本默认开启:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
_PROF_TRACE = os.environ.get("PROF_TRACE", "1") != "0"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这会在 decode 阶段输出大量 `[PROF]` 行,并且每次 `flush=True`。改为默认关闭:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
_PROF_TRACE = os.environ.get("PROF_TRACE", "0") == "1"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
需要代码级 profiling 时再显式设置 `PROF_TRACE=1`。
|
|
|
|
|
|
|
|
|
|
|
|
### 结果
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-15-official-like-baseline/eager_100k_no_prof_c1_r8_t256_cap40k.json`
|
|
|
|
|
|
|
|
|
|
|
|
| 指标 | 100K 基线 | PROF_TRACE 默认关闭 | 目标 |
|
|
|
|
|
|
| --- | ---: | ---: | ---: |
|
|
|
|
|
|
| Success Rate | 100.00% | 100.00% | >= 99% |
|
|
|
|
|
|
| Cache Hit Rate | 74.17% | 74.17% | >= 50% |
|
|
|
|
|
|
| TTFT P90 | 9.803s | 12.739s | <= 5s |
|
|
|
|
|
|
| Output TPS P10 | 5.825 | 5.851 | >= 20 |
|
|
|
|
|
|
| Aggregate Output TPS | 5.789 | 5.674 | - |
|
|
|
|
|
|
| Uncached Input TPS | 178.330 | 169.148 | - |
|
|
|
|
|
|
| Cache TPS | 512.015 | 485.652 | - |
|
|
|
|
|
|
| Weighted Throughput | 883.109 | 840.712 | >= 8000 |
|
|
|
|
|
|
|
|
|
|
|
|
### 结论
|
|
|
|
|
|
|
|
|
|
|
|
关闭默认 profiling 没有带来明显性能收益,说明当前主要瓶颈不是日志 I/O。不过该改动仍应保留在生产提交中:
|
|
|
|
|
|
|
|
|
|
|
|
- 避免评测日志膨胀;
|
|
|
|
|
|
- 避免 profiling 输出影响长时间 benchmark 稳定性;
|
|
|
|
|
|
- profiling 仍可通过 `PROF_TRACE=1` 手动开启。
|
|
|
|
|
|
|
|
|
|
|
|
下一步继续看 MoE routed expert 和 TP all-reduce;服务日志中 decode 稳态仍约 5.8 tok/s,说明专家计算/通信路径仍是核心。
|
2026-07-15 10:27:33 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-15:max_num_batched_tokens=32768 实验
|
|
|
|
|
|
|
|
|
|
|
|
### 配置
|
|
|
|
|
|
|
|
|
|
|
|
在 no-prof 代码基础上,仅把:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
--max-num-batched-tokens 16384
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
改为:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
--max-num-batched-tokens 32768
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
其余保持 100K 快速基线一致。此前 32768 发生过 OOM,但这次在清理残留 worker 和显存后可正常启动,说明之前 OOM 不能作为参数不可行的证据。
|
|
|
|
|
|
|
|
|
|
|
|
### 结果
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-15-official-like-baseline/eager_100k_b32768_c1_r8_t256_cap40k.json`
|
|
|
|
|
|
|
|
|
|
|
|
| 指标 | 100K 基线 | no-prof 16384 | no-prof 32768 | 目标 |
|
|
|
|
|
|
| --- | ---: | ---: | ---: | ---: |
|
|
|
|
|
|
| Success Rate | 100.00% | 100.00% | 100.00% | >= 99% |
|
|
|
|
|
|
| Cache Hit Rate | 74.17% | 74.17% | 74.17% | >= 50% |
|
|
|
|
|
|
| TTFT P90 | 9.803s | 12.739s | 13.005s | <= 5s |
|
|
|
|
|
|
| Output TPS P10 | 5.825 | 5.851 | 5.895 | >= 20 |
|
|
|
|
|
|
| Aggregate Output TPS | 5.789 | 5.674 | 5.556 | - |
|
|
|
|
|
|
| Uncached Input TPS | 178.330 | 169.148 | 173.430 | - |
|
|
|
|
|
|
| Cache TPS | 512.015 | 485.652 | 497.947 | - |
|
|
|
|
|
|
| Weighted Throughput | 883.109 | 840.712 | 857.595 | >= 8000 |
|
|
|
|
|
|
|
|
|
|
|
|
### 结论
|
|
|
|
|
|
|
|
|
|
|
|
32768 可以启动,但没有带来总体收益;Output TPS P10 略升,TTFT 继续变差,综合吞吐仍低于原 100K 基线。单纯扩大 chunk 不是主优化方向。下一步尝试 8192,验证更小 chunk 是否能改善 TTFT。
|
2026-07-15 10:44:24 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-15:max_num_batched_tokens=8192 实验
|
|
|
|
|
|
|
|
|
|
|
|
### 流程改进
|
|
|
|
|
|
|
|
|
|
|
|
新增可复用远端实验脚本:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_scripts/run_batched_tokens_experiment.sh`
|
|
|
|
|
|
|
|
|
|
|
|
脚本负责:
|
|
|
|
|
|
|
|
|
|
|
|
- 停止旧 `api_server`;
|
|
|
|
|
|
- 检查启动前 GPU 显存;
|
|
|
|
|
|
- 按指定 `max_num_batched_tokens` 启动 100K/no-prof 服务;
|
|
|
|
|
|
- 等待 `/health`;
|
|
|
|
|
|
- 运行 8 请求官方相似压测;
|
|
|
|
|
|
- 写入 `driver.log`、`server.log` 和结果 JSON。
|
|
|
|
|
|
|
|
|
|
|
|
这能减少超长 SSH 命令导致的本地 PowerShell/SSH 子进程异常,也让后续实验日志格式统一。
|
|
|
|
|
|
|
|
|
|
|
|
### 结果
|
|
|
|
|
|
|
|
|
|
|
|
本地归档:
|
|
|
|
|
|
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-15-official-like-baseline/eager_100k_b8192_c1_r8_t256_cap40k.json`
|
|
|
|
|
|
- `worklogs/remote_results/2026-07-15-official-like-baseline/driver_b8192.log`
|
|
|
|
|
|
|
|
|
|
|
|
| 指标 | 100K 基线 | no-prof 8192 | no-prof 16384 | no-prof 32768 | 目标 |
|
|
|
|
|
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
|
|
|
|
| Success Rate | 100.00% | 100.00% | 100.00% | 100.00% | >= 99% |
|
|
|
|
|
|
| Cache Hit Rate | 74.17% | 74.17% | 74.17% | 74.17% | >= 50% |
|
|
|
|
|
|
| TTFT P90 | 9.803s | 13.996s | 12.739s | 13.005s | <= 5s |
|
|
|
|
|
|
| Output TPS P10 | 5.825 | 5.840 | 5.851 | 5.895 | >= 20 |
|
|
|
|
|
|
| Aggregate Output TPS | 5.789 | 5.513 | 5.674 | 5.556 | - |
|
|
|
|
|
|
| Uncached Input TPS | 178.330 | 168.408 | 169.148 | 173.430 | - |
|
|
|
|
|
|
| Cache TPS | 512.015 | 483.527 | 485.652 | 497.947 | - |
|
|
|
|
|
|
| Weighted Throughput | 883.109 | 834.751 | 840.712 | 857.595 | >= 8000 |
|
|
|
|
|
|
|
|
|
|
|
|
### 结论
|
|
|
|
|
|
|
|
|
|
|
|
8192 没有改善 TTFT,反而是本轮 chunk 参数中最弱。8192/16384/32768 三组的 Output TPS P10 都约 5.8-5.9,差别很小,说明当前目标瓶颈不是 chunk 大小,而是 decode 每 token 的层内计算/通信周期,以及长上下文 prefill/cache 路径的底层吞吐。
|
|
|
|
|
|
|
|
|
|
|
|
下一轮方向:
|
|
|
|
|
|
|
|
|
|
|
|
1. 对 decode 稳态做低开销 profiling,只记录少量聚合计时,避免 `PROF_TRACE` 每层每 token 打日志。
|
|
|
|
|
|
2. 拆分 `MLP` 时间中 routed expert、shared expert、TP all-reduce 的占比。
|
|
|
|
|
|
3. 若 all-reduce 占比高,测试 `--disable-custom-all-reduce` 保持关闭/打开的差异和 TP 通信路径。
|
|
|
|
|
|
4. 若 routed expert 占比高,继续优化 `_native_experts_sorted` 中逐 expert 循环和 `index_add_`。
|