Skip to content

KDA 性能工程方法:从 Profile、噪声控制到可信 PR

KDA(Kernel Development Agents)如果只理解成“让 Agent 自动写 CUDA/Triton kernel”,其实低估了它的目标。

对于 CosyVoice、Qwen3-Omni、dots.tts 一类多阶段生成系统,更有价值的形态是:

让 Agent 完成一轮可验证的性能工程闭环:Profile → 归因 → 提出可证伪假设 → 干预实验 → 正确性验证 → E2E 复现 → PR。

这套方法比单纯追求某个 kernel 的 microbenchmark 更重要,因为多阶段 serving 中最常见的问题并不是“算得慢”,而是 CPU dispatch、stage queue、shape churn、batch formation、数据通路或同步把 GPU 饿住。


1. 第一原则:先测占比,再决定优化对象

Section titled “1. 第一原则:先测占比,再决定优化对象”

多阶段系统应先拆 runtime:

T_e2e
├── T_preprocess
├── T_AR / Thinker
├── T_DiT / Talker
├── T_vocoder / Code2Wav
├── T_queue
├── T_transport
└── T_host_dispatch / sync

Agent 在动代码前至少应该回答:

  1. 当前 critical path 是哪个 stage?
  2. stage 内是 GPU compute 慢,还是 host 没把 GPU 喂饱?
  3. queue-to-terminal 时间有多少是真 compute,有多少是排队?
  4. kernel launch gap、CPU thread saturation、CUDA Graph replay hit rate 是否异常?
  5. 优化目标是 TTFT、TTFA、ITL、RTF、吞吐还是尾延迟?

例如看到 GPU utilization 很低时,不应该自动得出“kernel 需要优化”。可能的因果链反而是:

CPU dispatch 饱和
↓
kernel launch 间隔变大
↓
GPU 出现 bubble
↓
GPU utilization 低

这时去改 GEMM kernel 往往没有意义。


2. Profile 不是截图,而是因果归因的起点

Section titled “2. Profile 不是截图,而是因果归因的起点”

一个好的 profile 应该同时覆盖:

  • GPU kernel / CUDA API;
  • stage queue 与 admission;
  • scheduler thread;
  • CPU preprocessing;
  • transport / relay;
  • batch size 与 shape 分布;
  • graph capture / replay 命中;
  • request-level TTFT / TTFA / ITL / E2E。

真正有用的问题通常是:

“为什么这个时间花在这里?”

而不是:

“哪个函数在 profiler 里颜色最深?”

Qwen3-Omni 的公开优化工作已经展示了很多这种归因方式:有的瓶颈最终落在 host dispatch,有的落在 Thinker→Talker 的小消息通路,有的落在 Code2Wav 的同步 D2H,而不是单一大 kernel。


3. A/A 先于 A/B:先知道噪声底有多大

Section titled “3. A/A 先于 A/B:先知道噪声底有多大”

多阶段 serving 的 benchmark 很容易受以下因素影响:

  • GPU boost / thermal state;
  • CPU 抢占;
  • batch composition;
  • request arrival timing;
  • allocator / cache warmup;
  • colocated process 干扰;
  • CUDA Graph shape 命中差异。

因此在宣称“+5%”之前,应该先做 A/A:

A/A
↓
同一版本、同一配置重复测
↓
估计 noise floor
A/B
↓
只有 effect size 明显超过 noise floor 才 claim

建议至少采用:

  • counter-balanced 顺序:AB / BA / BA / AB;
  • fresh server 或严格一致的 warmup;
  • 相同数据集、并发度、sampling contract;
  • 记录每轮而不是只报平均值;
  • 对高噪声 cell 明确标记“not promotion-capable”。

如果 A/A 自己能抖 8%,那么一个 +3% 的 A/B 不应该被包装成性能收益。


这是性能工程中最容易被忽略的原则。

一个优化可能在局部完全成立:

某 stage replay +4%
某 vocoder batch +3×
某 kernel 数量 -160

但端到端可能几乎不变。

原因就是 Amdahl 定律与 pipeline critical path:

Se2e=1(1−p)+p/SlocalS_{e2e} = \frac{1}{(1-p)+p/S_{local}}

因此报告应至少分成两层:

回答:

  • 优化机制是否真的发生?
  • kernel 数量是否减少?
  • graph replay 是否命中?
  • stage latency / throughput 是否改善?

回答:

  • request throughput 是否改善?
  • TTFT / TTFA / ITL 是否改善?
  • p95 / p99 RTF 是否改善?
  • 是否把压力推给了下游 stage?

公开的 Qwen3-Omni predictor GQA 工作就是一个很好的例子:局部 replay 有稳定的几个百分点提升,同时明确报告 E2E 基本落在噪声带内。这样的“E2E-neutral”结果同样有价值,因为它证明了机制成立,但当前不在系统 critical path。


5. CUDA Graph 优化的关键不是“开 graph”,而是 shape contract

Section titled “5. CUDA Graph 优化的关键不是“开 graph”,而是 shape contract”

很多 serving 系统已经支持 CUDA Graph,但真实 replay hit rate 仍然很低。

原因通常不是 graph 本身慢,而是:

真实请求 shape
↓
不断变化的 batch / chunk / sequence length
↓
和 captured keys 对不上
↓
频繁 eager fallback

所以应该先测:

  • 每种 batch size 的出现频率;
  • 每种 sequence/chunk shape 的出现频率;
  • capture key coverage;
  • replay hit / miss reason;
  • eager fallback 的 host overhead。

优化方向往往是:

真实 workload
↓
设计 bucket / chunk policy
↓
让 scheduler 输出靠近 captured shape
↓
提高 replay hit rate

而不是无限增加 captured graph 数量。

Qwen3-Omni Code2Wav 的 chunk-aligned dispatch 就是这一思想的典型实现:先把每次处理工作量规整成固定 chunk,再让 batch bucket × frame bucket 真正可复用 CUDA Graph。


6. Batching 的前提:请求必须能在同一个 shape contract 上相遇

Section titled “6. Batching 的前提:请求必须能在同一个 shape contract 上相遇”

“并发很高”并不自动意味着 batching 有效。

典型失败模式:

request A backlog = 10 frames
request B backlog = 17 frames
request C backlog = 31 frames
↓
shape 全不同
↓
几乎无法形成高效 batch

常见解决方式有两类:

适合允许变长输入的 stage:

相近长度请求
↓
padding waste gate
↓
pad 到同一 shape
↓
batched forward

CosyVoice3 HiFT batching 属于这一类。

适合 streaming window:

variable backlog
↓
每轮只消费固定 chunk
↓
uniform windows
↓
batched graph replay

Qwen3-Omni Code2Wav 属于这一类。

因此一个成熟 Agent 不应该只调 max_batch_size,而应该一起研究:

  • batch wait;
  • bucket size;
  • padding waste;
  • chunk size;
  • graph key;
  • admission policy。

7. compile 与 TensorRT:默认值不能只由最快结果决定

Section titled “7. compile 与 TensorRT:默认值不能只由最快结果决定”

一个后端即使 isolated benchmark 更快,也不一定适合作为默认路径。

决策至少应该考虑:

维度问题
性能isolated / E2E 提升多少?
稳定性动态 shape / fallback 是否可靠?
正确性输出、WER、音质是否保持?
环境是否需要额外 TensorRT / engine 构建依赖?
启动成本compile/capture/build 时间有多大?
可维护性模型升级后是否容易失效?

所以更合理的流程是:

eager / compile / TensorRT
↓
同一 paired benchmark contract
↓
correctness + quality gate
↓
再决定 default / opt-in

而不是“最快的那个直接默认开启”。


8. 正确性 gate 必须和性能 gate 同级

Section titled “8. 正确性 gate 必须和性能 gate 同级”

对生成模型,正确性不能只看“没有 crash”。

根据修改层级,应选择不同 gate:

  • token 序列;
  • output token count;
  • deterministic sampling parity;
  • cache 生命周期。
  • WER / CER;
  • 长音频是否截断;
  • chunk seam 是否丢词;
  • timestamp / segment ordering。
  • waveform / codec token;
  • WER 反向验证;
  • speaker / audio quality 指标;
  • chunk ordering 与 terminal semantics。

除了 TTFA,还必须测:

  • inter-chunk latency(ITL);
  • message count / ordering;
  • terminal flush;
  • abort / disconnect behavior。

有些 overlap 优化会降低线程阻塞,却把一个 chunk 延迟到下一个 production interval;如果只看 throughput,就会错过真实用户体验退化。


9. 复现实验要控制 host 环境,而不只是 GPU 型号

Section titled “9. 复现实验要控制 host 环境,而不只是 GPU 型号”

多阶段 serving 中 CPU 是正式性能资源。

因此“同样是 H100”远远不够,还应记录:

  • CPU affinity / isolation;
  • NUMA;
  • colocated process;
  • PyTorch / CUDA / driver;
  • model revision;
  • scheduler 配置;
  • graph/compile 开关;
  • warmup;
  • random seed / sampling mode。

如果目标问题本身就是 host dispatch 饱和,那么没有 CPU 隔离的 A/B 很容易被系统噪声污染。

对于需要做默认值决策的优化,最好要求至少一次独立环境复现,而不是只在开发者自己的机器上通过。


一个面向 Agent 的性能 harness 最重要的不是“能跑 benchmark”,而是把实验纪律固化下来。

建议至少包含:

Experiment Spec
├── base / candidate SHA
├── hardware + software manifest
├── workload / dataset
├── concurrency cells
├── warmup policy
├── A/A noise gate
├── paired A/B order
├── isolated metrics
├── E2E metrics
├── correctness / quality gate
└── artifact retention

Agent 的 judge 应该能拒绝:

  • 没有 baseline 的“性能提升”;
  • effect 小于 noise floor 的 claim;
  • 只报 isolated、不报 E2E 的系统优化;
  • 只报吞吐、不报 latency tail 的 serving 改动;
  • 性能提升但 WER / correctness 退化的方案;
  • benchmark contract 不一致的前后对比。

这样 Agent 才是在做 performance engineering,而不是 benchmark hill-climbing。


可以把整套流程压缩成:

1. Profile
↓
2. Attribute critical path
↓
3. Write falsifiable hypothesis
↓
4. Establish A/A noise floor
↓
5. Make the smallest intervention
↓
6. Run isolated mechanism test
↓
7. Run paired E2E A/B
↓
8. Run correctness / WER / quality gate
↓
9. Reproduce on isolated environment
↓
10. Merge only if evidence clears gate

这里最重要的是第 3 步:假设必须可证伪。

例如好的假设是:

“CUDA Graph replay 低是因为 scheduler 常产生未捕获的 batch size;补齐对应 bucket 后 replay hit rate 应显著上升,同时 host launch overhead 下降。”

而不是:

“试试 CUDA Graph,也许会更快。”


这套方法并不是抽象原则,在 SGLang-Omni 的公开工作中已经反复出现:

它们共同指向同一个方向:

Agentic Performance Engineering 的核心不是自动生成代码,而是自动生成可信证据。