跳转到主要内容

从 Profiling 到 Simulation:推理性能分析的证据链方法

· 约 5 分钟阅读

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_modelmodel_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/KVattention 与 KV read 是否主导ISL、prefix hit、KV cache 策略如何影响 TTFT/TPM
MoE A2A/Commdispatch/combine 和通信是否上升EP、NVLink 域、global batch 是否可承载
Copy/KV loadKV load/copy 是否吃掉收益KV load 与 compute 是否 overlap
Launch/Runtime小 kernel 和 launch 是否成为瓶颈CUDA Graph、chunk size、microbatch 是否值得实验
Sync/Waitqueue、同步和等待是否主导调度、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.973ms
  • GPUModelRunner._model_forward 平均从 344.106ms 降到 249.571ms
  • GraphModule.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 cacheon / off验证开关收益
hit ratio0% / 50% / 90%+验证收益是否随 hit 增长
chunk sizesmall / default / large验证 compute 下降是否被 launch/调度抵消
workload单压 prefill / 并发 prefill / mixed prefill+decode验证单压收益是否能转化成吞吐收益
trace instrumentationbaseline / 加 request+chunk+KV range验证能否解释到 chunk 级别

个人技术提升

这周真正沉淀下来的不是某个具体数字,而是三个分析习惯:

  1. 不把 profiling 当成最终答案。它是事实层,还需要分类和假设。
  2. 不把 simulation 当成线上事实。它是验证层,必须写清假设和边界。
  3. 不直接给“优化建议”。先给证据等级、缺失字段和可证伪实验。

这让我从“提供数据”往“提供分析判断和下一步验证路径”前进了一步。

后续补完

  • 把 GB300/B300 的 10ms vs 20ms SLO case 加成第二个样例。
  • 把 trace taxonomy v0 整理成可复用表格。
  • 把 A/B/C/D 证据等级补进每个 case。
  • 决定是否从 draft 转为正式发布。
修改历史2 次提交