Kernel fusion 为什么快?
什么时候反而变慢?
先弄清楚:每个概念是什么
- Kernel fusion
- 把几个 kernel 合成一个,一次 launch 做完几步计算。
- 中间结果
- 上一个 kernel 的输出,下一个 kernel 的输入。不融合时要写回 HBM 再读出来,融合后留在 register 或 shared memory 里。
- Memory traffic
- 一个 kernel 读写 HBM 的总 byte 数。fusion 省的是它,FLOPs 不变。
- Epilogue
- 接在 matmul 后面的小运算,比如 bias 和 activation,结果写回 HBM 之前顺手做掉。
看图:中间结果还过不过 HBM
30 秒回答
Fusion merges several kernels into one, so intermediate results stay in registers or shared memory instead of going out to HBM and back, with fewer launches. The FLOPs don't change; the memory traffic does. So it helps most on memory-bound chains: element-wise ops, reductions, or a bias and activation fused into a matmul's epilogue. Three element-wise kernels make six passes over memory; fused, they make two. It can backfire when the fused kernel uses so many registers or shared memory that occupancy drops, or when you pull a big GEMM out of a tuned library.
为什么快:手算一遍
按图里的三步算,x 是 1 GB 的 tensor(示意)。融合前每个 kernel 读 1 GB、写 1 GB,三个一共 6 GB,融合后一共 2 GB。这三步都是 memory bound,时间跟着 memory traffic 走,所以上限是快 3 倍。按 A100 的 2,039 GB/s 算,下界从约 2.9 ms 降到约 1.0 ms。
FlashVector 的 Case 2 把 target attention 的 6 个 kernel 融成 1 个,996.5 µs 降到 279.8 µs,约 3.6 倍,结果 bit-identical。经典的例子是 FlashAttention,把 softmax 和两个 matmul 融进一个 kernel。
适合融什么,什么时候反而慢
| 情况 | 结果 | 原因 |
|---|---|---|
| 一串 element-wise,或后面接一个 reduction | 快 | 都是 memory bound,中间结果不再过 HBM |
| matmul 后面接 bias、activation | 快 | 当 epilogue 做 |
| 融合后 register 或 shared memory 用得太多 | 可能变慢 | occupancy 下降,等 memory 的时间藏不住 |
| 把大 GEMM 硬融进自己写的 kernel | 变慢 | 失去厂商库的高效实现 |
必答点
- 中间结果不写回 HBM,launch 次数也少
- FLOPs 不变,变的是 memory traffic
- 适合 memory bound 的 element-wise 和 reduction,或 matmul 的 epilogue
- 变慢的两种情况:register 或 shared memory 用多了,occupancy 下降;大 GEMM 硬融进来,失去厂商库的实现
- 做法:torch.compile、手写 Triton 或 CUDA、CUTLASS epilogue
- FlashVector:6 个 kernel 融成 1 个,快约 3.6 倍,bit-identical
追问
怎么判断哪一串适合融合?
先 profile。挨在一起、都是 memory bound 的小 kernel 最值得融(判断见 Nsight profiling)。蚂蚁 AI Infra 二面 2026-04-11 问过,帖子是转述。
融合后的输出还和原来一样吗?
不改计算顺序时应该逐元素相等,FlashVector 对这类 deterministic rewrite 就是这样要求的(见 kernel 正确性)。
compiler 为什么有时候融不了?
遇到 opaque op,compiler 看不到里面。FlashVector 的 embedding bag 就是这样(见 compiler 的边界)。
fusion 和 CUDA Graph 有什么区别?
fusion 同时减少 memory traffic 和 launch 次数。CUDA Graph 只去掉 launch overhead,每个 kernel 照旧读写 HBM。
kernel 快了 3.6 倍,线上就快 3.6 倍吗?
不会。Case 2 合计 GPU kernel time 降 33.4%,forward latency 降 30.1%(见 局部和端到端)。
来源FlashVector §5.2(Case 2)· Unity JD JOBREQ-2615890 · ml-八股 08 第 4 题 · 面经和 JD 出处见 面试题与 JD 1.3、1.4、2.1、3.3 节 · A100 的 bandwidth 见 matmul 与 roofline