海光 DCU 性能测试实录:冷启动如何把 0.325% 伪装成接近三倍
在一组海光 DCU gfx936 推理 A/B 中,第一次看到结果时,几乎没有理由不兴奋。
我们只替换了一条 BF16 MLP gate/up GEMM 的 vendor solution。四次新服务按照 B-A-B-A 顺序运行,结果是:
baseline1: 4.183840 tok/s candidate1: 15.041781 tok/s baseline2: 5.503544 tok/s candidate2: 15.138390 tok/s两个 baseline 都慢,两个 candidate 都快。
这不是一次偶然尖峰,而且所有运行都生成了 1809 个 token,10/10 文本完全一致。
如果只看常见的 A/B 原则,这组结果似乎已经很有说服力:
- baseline 重复了;
- candidate 重复了;
- 顺序交错;
- 输出一致;
- failed 为 0。
但它仍然是错的。
一个明显不符合物理预算的结果
这条改动只影响 Prefill 中一个固定大 GEMM shape。它不会让 Decode TPOT 大幅下降,也不可能把完整服务从 4-5 tok/s 直接推到 15 tok/s。
当局部改动与端点收益相差几十倍时,第一反应不应该是“中了大奖”,而应该是:
测量窗口里混进了什么?
于是我们不再只看整组吞吐,而是拆开每个请求的时间线。
问题很快出现:每个 baseline 服务都在同一个靠后请求上进入了数分钟级冷路径;candidate 也有冷路径,但持续时间短得多。
这个异常足以主导整组墙钟时间。
为什么 B-A-B-A 没能消除污染
B-A-B-A 能降低随机波动,但它有一个隐含前提:
每次 B 和 A 的系统状态分布相同这次并不满足。
四次运行都使用“新启动的服务”,而冷路径是进程级状态。每个 baseline 都重新经历了相似的首次路径,因此污染不是随机噪声,而是和配置、服务生命周期或缓存状态相关的系统性偏差。
交错顺序只能处理时间趋势,处理不了“每个新进程第一次执行某个 shape 时都会发生什么”。
换句话说,B-A-B-A 重复了污染,而不是消除了污染。
先看单请求,真实信号其实很小
排除那个数分钟异常请求后,对其他请求做归因,baseline 的 TTFT 只比 candidate 慢约:
38 ms/request换算成整组 output throughput,物理上大约对应 +0.29%,而不是接近三倍。
这个数字和改动范围更匹配:
- 只改 Prefill GEMM;
- TPOT 不应显著变化;
- 每个请求只获得几十毫秒 TTFT 改善;
- 完整请求持续时间远大于这几十毫秒。
于是实验需要重做。
新协议:每个进程先完整跑一遍
修正后的流程不是增加几个短 warmup,而是:
每个新服务 -> 完整运行固定 10 请求一次 -> 丢弃第一次完整结果 -> 再运行相同 10 请求 -> 只记录第二次顺序仍然保持:
baseline1 -> candidate1 -> baseline2 -> candidate2这样做的目的,是让每个进程都经历同样的 shape、编译和缓存建立过程,再进入测量窗口。
稳态结果终于回到物理范围
第二轮测量结果如下:
| 运行 | output tok/s | tokens | mean TTFT | p99 TPOT | failed |
|---|---|---|---|---|---|
| baseline1 | 16.417439 | 1809 | 3150.450 ms | 43.792 ms | 0 |
| candidate1 | 16.478697 | 1809 | 3107.261 ms | 43.715 ms | 0 |
| baseline2 | 16.413027 | 1809 | 3148.763 ms | 43.745 ms | 0 |
| candidate2 | 16.458452 | 1809 | 3106.818 ms | 43.826 ms | 0 |
四种交叉比较的吞吐变化为:
candidate1 vs baseline1: +0.3731% candidate1 vs baseline2: +0.4001% candidate2 vs baseline1: +0.2498% candidate2 vs baseline2: +0.2768%均值结果:
candidate mean vs baseline mean = +0.3250%两个 baseline 之间只差 0.027%,说明修正后的参考已经稳定。
所有测量仍然保持:
- 1809 个输出 token;
- 10/10 文本一致;
- failed=0;
- TPOT 基本不变。
这和“只优化 Prefill GEMM”的预期完全一致。
最危险的不是冷启动,而是合理的假证据链
如果第一次结果只有一组 4.18 对 15.04,很容易被识别为异常。
真正危险的是它看起来拥有完整证据:
B1 慢 A1 快 B2 慢 A2 快 文本一致 无失败这套证据会让人放松警惕。
但只要四个进程都没有经过同样的完整 warmup,B-A-B-A 仍然可能比较的是四种不同的进程状态。
如何识别类似问题
1. 先做物理预算
如果改动只省几十毫秒 TTFT,却观察到几倍吞吐,优先怀疑测量。
2. 拆每个请求
整组吞吐会隐藏单个数分钟异常。逐请求 TTFT、TPOT 和持续时间更容易定位污染源。
3. Warmup 必须覆盖真实 shape
一个短 prompt 或一个小 shape 的 warmup,不能保证后面的长 shape 已完成编译和缓存建立。
4. 新进程和同进程要分开理解
进程间 A/B 测的是“启动后完整生命周期”;进程内第二轮更接近稳态 kernel 效果。两种口径都可以有价值,但不能混在一起。
5. 输出一致只能证明语义,没有证明计时正确
1809 token 和 10/10 文本一致排除了生成漂移,却无法排除 I/O、编译、缓存和 runtime 冷路径。
这为什么是海光 DCU 论坛值得讨论的问题
海光 DCU 大模型服务通常同时包含 DTK kernel、Triton/Inductor 编译、CUDAGraph、模型加载和多种 shape cache。本文没有把那次冷路径唯一归因于其中某一项,但它证明了进程状态足以压过真实 kernel 收益。
因此,论坛中的 DCU benchmark 最好同时公开:
- 是否为新进程;
- warmup 是否覆盖完整真实请求;
- 计时是否包含首次编译和图捕获;
- 是否按请求记录 TTFT/TPOT;
- baseline 与优化版本是否经历相同生命周期。
这些信息能让不同海光 DCU 结果真正可比较,而不只是各自给出一个最快数字。
结语
同一条改动,第一次看起来把吞吐从 4-5 tok/s 提升到 15 tok/s;修正进程内 warmup 后,真实稳态收益是 +0.3250%。
两组数字都来自真实运行,但回答的是不同问题:
- 第一组主要测到了冷路径差异;
- 第二组才接近目标 GEMM 的稳态服务收益。
这次实验留下的最重要结论不是“0.325% 太小”,而是:
A/B 的价值不在字母顺序,而在 A 和 B 是否处于相同系统状态。
在 DCU 大模型推理中,编译、图捕获、权重加载和 shape cache 都可能成为隐形变量。没有完整、对称的 warmup,再漂亮的 B-A-B-A 也可能只是把同一个误差重复了四次。