从 Profiling 到 Simulation:推理性能分析的证据链方法
写作目标
这篇不是周报,也不是项目复盘,而是记录本周形成的一个技术能力:用 profiling 找到真实瓶颈,用 simulation 验证优化假设,最后把两者收敛成可执行的性能建议。
过去我容易把两类证据分开看:
- Profiling 告诉我线上到底慢在哪里,但它天然是一次运行、一个窗口、一个 workload 的事实。
- Simulation 告诉我如果 TPOT、P/D、cache hit、ISL/OSL 或 batch 形态变化,容量可能如何变化,但它天然是带假设的模型。
本周的提升是:不再把它们当成两个独立材料,而是把它们组织成一条证据链。
线上 profiling 事实
-> 归因分类
-> 优化假设
-> simulation 敏感性验证
-> 建议 / 风险 / 下一步实验
方法骨架
1. Profiling 负责回答“现实发生了什么”
Profiling 的价值不是给出一个巨大的 trace,而是把现实问题压缩成几个可解释的事实:
- 请求级结果:TTFT、TPOT、吞吐是否真的变化。
- Engine 语义层:
execute_model、model_forward、scheduler、queue wait 是否变轻。 - GPU kernel 层:GEMM/FFN/MoE、attention/KV、copy、launch、sync/wait 哪些分类在变化。
- 采样边界:wall window、event 数、profiling window 不能被误读成请求耗时。
也就是说,profiling 是事实层,但不是最终判断。
2. Simulation 负责回答“如果假设成立,会带来什么”
Simulation 的价值不是替代线上,而是把优化方向放进可计算的条件里:
- 如果 TPOT SLO 从 20ms 收紧到 10ms,哪些 high-batch 候选会被淘汰?
- 如果 prefix cache hit ratio 提升,prefill chip-time 会怎么变?
- 如果 P/D 形态不同,综合 TPM 是否还能直接比较?
- 如果真实 ISL/OSL 不同,当前压测和仿真差异是否会收敛?
也就是说,simulation 是假设验证层,但不是线上事实。
3. 证据链的关键是“中间层分类”
单看 profiling 容易停留在“某个 kernel 很慢”。单看 simulation 容易停留在“某个配置容量更高”。中间必须有一层稳定分类,把两者连起来:
| 分类 | Profiling 看什么 | Simulation 验证什么 |
|---|---|---|
| GEMM/FFN/MoE compute | 模型前向计算是否下降 | batch、TP/EP、量化、MoE shape 变化是否影响产能 |
| Attention/KV | attention 与 KV read 是否主导 | ISL、prefix hit、KV cache 策略如何影响 TTFT/TPM |
| MoE A2A/Comm | dispatch/combine 和通信是否上升 | EP、NVLink 域、global batch 是否可承载 |
| Copy/KV load | KV load/copy 是否吃掉收益 | KV load 与 compute 是否 overlap |
| Launch/Runtime | 小 kernel 和 launch 是否成为瓶颈 | CUDA Graph、chunk size、microbatch 是否值得实验 |
| Sync/Wait | queue、同步和等待是否主导 | 调度、admission、prefill/decode 混部策略是否需要调整 |
Prefix on/off 作为第一个样例
这个 case 很适合作为方法论样例,因为它同时具备请求级结果、engine span 和 GPU kernel 证据。
问题
打开 prefix KV cache 后,收益到底来自哪里?它只是某些 kernel 变短,还是已经转化成用户可感知的 TTFT 改善?
Profiling 证据
请求级结果:
- cache-off TTFT:
476ms - cache-on TTFT:
343ms - 改善:
133ms,约27.9%
Engine 语义层:
GPUModelRunner.execute_model平均从227.724ms降到148.973msGPUModelRunner._model_forward平均从344.106ms降到249.571msGraphModule.forward平均从8.079ms降到5.749ms
GPU kernel 层:
- GPU kernel 总 duration 从
6.842s降到2.764s - GEMM-like 从
1,795.302ms降到558.524ms - attention-like 从
571.032ms降到186.275ms
当前判断
最稳的结论不是“prefix cache 让所有东西都变快”,而是:
Prefix KV cache 减少了 prefill 阶段 prefix token 的完整重算,使每次模型执行 step 变轻,并且在这次单压 prefill 中已经转化成 TTFT 改善。
还不能回答的问题
这个 case 也暴露了下一轮实验必须补的字段:
- 每个 chunk 的 request id / sequence id / chunk id。
- 每个 chunk 的 token range。
- prefix hit token 数和 miss token 数。
- prefix KV lookup、load、write 的独立耗时。
- 同一窗口内完成了多少请求、输入 token、输出 token。
- 多次重复单压下,TTFT 改善是否稳定。
下一轮实验设计
建议把实验设计成矩阵,而不是只做一次 on/off:
| 变量 | 取值 | 目的 |
|---|---|---|
| prefix cache | on / off | 验证开关收益 |
| hit ratio | 0% / 50% / 90%+ | 验证收益是否随 hit 增长 |
| chunk size | small / default / large | 验证 compute 下降是否被 launch/调度抵消 |
| workload | 单压 prefill / 并发 prefill / mixed prefill+decode | 验证单压收益是否能转化成吞吐收益 |
| trace instrumentation | baseline / 加 request+chunk+KV range | 验证能否解释到 chunk 级别 |
个人技术提升
这周真正沉淀下来的不是某个具体数字,而是三个分析习惯:
- 不把 profiling 当成最终答案。它是事实层,还需要分类和假设。
- 不把 simulation 当成线上事实。它是验证层,必须写清假设和边界。
- 不直接给“优化建议”。先给证据等级、缺失字段和可证伪实验。
这让我从“提供数据”往“提供分析判断和下一步验证路径”前进了一步。
后续补完
- 把 GB300/B300 的 10ms vs 20ms SLO case 加成第二个样例。
- 把 trace taxonomy v0 整理成可复用表格。
- 把 A/B/C/D 证据等级补进每个 case。
- 决定是否从 draft 转为正式发布。
← 被以下页面引用(3)
- 推理框架对比 2026:从 Engine 到 Serving Stackai-systems · synthesis
- Chunked Prefill 深入分析:调度、Chunk Size 与 Attention 形状ai-systems · synthesis
- Prefill Trace:Worker 供给、DSA/MLA 与 Chunked Prefillai-systems · synthesis
修改历史2 次提交
- docs(wiki): publish July inference researchxiaocheng··
5d6504e - docs: add inference profiling notesxiaocheng··
6a94c5d