01
60 秒回答模板
我会把验证分成三步:先保证测量公平,再证明性能改善来自算子本身,最后证明对真实链路有价值。公平 benchmark 的基本原则是同机同卡同驱动,固定 GPU 型号、SM 架构、时钟或功耗策略、CUDA 版本、依赖库、输入 shape、batch、dtype、layout、随机种子、warmup 次数、CUDA stream 和同步点。CUDA 是异步执行,不能只用 CPU wall time 包一段代码就下结论,应该用 CUDA Event 或 profiler 记录 kernel 时间,并在统计前做充分 warmup,排除首次 JIT、内存分配、cache 冷启动和数据拷贝。第二步排除硬件和精度差异:如果新旧版本跑在不同 GPU 或一个用 FP16、一个用 FP32,那不能直接说算子优化提速;要么统一硬件和 dtype,要么按理论峰值、带宽、Tensor Core 支持和误差容忍做归一化说明。第三步用 profiler 解释为什么快:计算密集看 achieved FLOPS、Tensor Core 利用率、指令吞吐、occupancy、寄存器压力;访存密集看 DRAM/L2 带宽、coalescing、load/store efficiency、shared memory bank conflict、cache hit 和访存字节;小算子看 launch 数、同步和调度开销。最后还要做 correctness:和参考实现对比最大误差、相对误差、边界 shape、NaN/Inf、不同 dtype 和随机输入;再放进真实 LLM 推理链路看 TTFT、tokens/s、p50/p95/p99、显存峰值和吞吐。只有 microbenchmark、profiler 证据、精度回归和端到端收益一致,才能说性能提升真实有效。
考点 同条件对比
难度 真实面经题
回答目标 让候选人展示一套可复现、可归因、可上线的 CUDA 性能验证方法,能排除硬件和精度干扰,并用 profiler 与端到端指标证明优化真实有效。
02
深入解析
01 先定义公平基线
公平 benchmark 要让新旧算子除了实现本身外尽量一致:同一台机器、同一块 GPU、同一驱动和 CUDA 运行时、同一依赖库、同一输入 shape、batch、stride、dtype、layout 和输出语义。若硬件或精度不同,最多能做平台对比,不能直接归因为算子优化。
02 计时要尊重 CUDA 异步
CUDA kernel launch 是异步的,CPU 侧时间容易只测到 launch 或混入其他工作。常用做法是 warmup 后用 CUDA Event 在同一 stream 上计时,必要时显式同步,再重复多轮取 median、p95 和置信区间。首次编译、内存分配、数据拷贝和 host-device 同步要单独统计或排除。
03 排除硬件和精度红利
不同 GPU 架构的 Tensor Core、显存带宽、L2、SM 数量和频率都不同,同一 GPU 上 FP32、TF32、FP16、BF16、INT8 的理论吞吐也差很多。要证明优化有效,最好在同硬件同 dtype 下对比;如果必须跨硬件或跨精度,要明确这是部署配置收益,而不是 kernel 实现本身收益。
04 区分计算密集和访存密集
可以用 roofline 思路估算 arithmetic intensity,即 FLOPs 与访存字节之比,再结合 NCU 指标判断瓶颈。计算密集关注 achieved FLOPS、Tensor Core 使用、指令吞吐和 SM 利用率;访存密集关注 DRAM/L2 带宽、访存合并、cache 命中、shared memory 冲突和不必要的中间写回。
05 用 profiler 解释提速来源
一个可信结论应能回答为什么快了:减少 global memory 读写、改善 coalescing、提升 Tensor Core 利用率、降低寄存器 spilling、减少分支发散、提高 occupancy、融合小 kernel、减少同步或 launch。只给一个加速倍数而没有 profiler 证据,面试官通常会继续追问。
06 精度和正确性不能省
优化算子必须和参考实现对齐输出,覆盖典型 shape、极端 shape、非连续内存、不同 batch、不同 seq length、NaN/Inf、溢出和下溢。要报告最大绝对误差、相对误差、均方误差或任务允许的容差,并说明低精度路径是否改变了数值语义。
07 端到端收益才算落地
microbenchmark 快不代表线上快。LLM 推理要看 prefill/decode 分阶段耗时、TTFT、单 token 延迟、tokens/s、吞吐、p95/p99、显存峰值、并发承载和调度等待。如果算子只占端到端 2%,即使 kernel 快 2 倍,整体收益也可能很小。
03
易错点
- 新旧版本跑在不同 GPU 上,直接把总耗时差说成算子优化收益。
- 一个版本用 FP16 或 Tensor Core,另一个用 FP32,却不单独说明精度和理论算力差异。
- 用 CPU wall time 包异步 CUDA 调用,没有同步或 CUDA Event,导致计时口径错误。
- 只报最好一次耗时,不做 warmup、多轮统计、p95 或波动分析。
- 没有做正确性和误差回归,速度提升可能来自少算、错算或溢出处理缺失。
- 只看 microbenchmark,不看端到端 LLM 推理中的 TTFT、吞吐、p99 和显存。
- 说不清优化命中了计算、访存、launch、同步还是 layout 哪个瓶颈。
04
面试官追问
为什么不能只用 Python time 或 std::chrono 计时?
因为 CUDA kernel launch 通常是异步的,CPU 计时可能只测到提交开销,也可能把后续同步、数据拷贝或其他 kernel 混进去。更可信的是在同一 CUDA stream 上用 CUDA Event 记录 kernel 起止,或直接用 Nsight Systems、Nsight Compute 看时间线和硬件指标。
如果优化版本用了 FP16,原版本是 FP32,怎么比较?
这不是单纯算子实现对比,而是精度配置和硬件能力共同变化。需要先同 dtype 对比实现收益,再单独报告低精度带来的部署收益,并给出误差容忍、任务指标回归和不同精度理论峰值差异。否则提速可能主要来自 Tensor Core 或更少访存。
如何判断算子是 memory-bound 还是 compute-bound?
先估算 FLOPs 和访存字节得到 arithmetic intensity,再看 profiler 中 achieved FLOPS 与带宽接近哪个硬件上限。若带宽接近峰值、FLOPS 不高,多半是访存瓶颈;若 Tensor Core 或 FP 单元利用率高、带宽未满,多半是计算瓶颈。还要结合 stall reason、cache hit 和 occupancy 判断。
microbenchmark 提升明显但端到端没提升,可能是什么原因?
可能该算子端到端占比太低,收益被调度、排队、数据拷贝、同步、其他 kernel 或显存瓶颈抵消;也可能真实 shape 分布和 microbenchmark 不一致,或者融合后破坏了上游下游 layout。需要回到完整 trace 看关键路径,而不是只盯单 kernel。
Benchmark 结果波动很大怎么办?
要固定 GPU 频率或功耗策略,避免温度降频,隔离其他进程,增加 warmup 和重复次数,使用 median/p95 而不是单次最小值,检查输入随机性和动态 shape,并确认没有隐式同步、懒加载、内存池扩容或系统噪声混入。