Skip to content

算子优化 ​

状态:入门理解 · 更新于 2026-09-14

算子优化的目标不是让某个 Kernel 的指令数量最少,而是在固定输入、精度和正确性约束下,缩短端到端关键路径。一个可复用的流程是:先建立基线,再判断瓶颈,最后针对瓶颈改数据流和并行映射。

先把问题量化 ​

优化前固定以下变量:输入 shape、dtype、布局、Batch、硬件型号、驱动与框架版本。至少记录:

  • 延迟的中位数和 P95,而不是一次最快值;
  • 吞吐、显存占用和启动次数;
  • 与参考实现的最大误差、相对误差和边界样例;
  • 端到端时间中该算子所占比例。

如果算子只占总耗时的很小部分,单独加速它未必能带来用户可见收益。

先判断瓶颈 ​

类型典型表现优先方向
Memory-bound带宽接近上限,计算单元未充分利用合并访问、减少读写、复用数据、融合
Compute-bound计算吞吐接近上限,访存不是主因Tensor Core、指令映射、Tile、精度
Launch-boundKernel 很短但调用很多,间隙明显融合、批处理、减少 Python/框架调度
Synchronization-bound等待和屏障占比高减少同步、改变流水线和通信安排

Roofline 模型可以用算术强度(FLOPs/Byte)初步判断 Compute-bound 与 Memory-bound,但它是上界模型,不能替代真实 Profile。

常用优化手段 ​

访存与布局 ​

让相邻线程访问相邻地址,减少非合并事务;对可复用数据使用 Shared Memory 或寄存器 Tile;避免不必要的 dtype 转换和中间张量。布局转换本身有成本,只有在后续多个算子能够复用时才值得做。

Tiling 与并行映射 ​

以 GEMM 为例,可以把矩阵切成 Block Tile,再进一步做 Warp/Thread Tile,让加载的数据在计算阶段被多次复用。Tile 太小会降低复用,太大则可能增加寄存器和 Shared Memory 压力,需要按 shape 选择。

融合与流水线 ​

把逐元素操作融合进前后主算子,可以减少中间结果的 Global Memory 往返。异步拷贝、双缓冲和计算/访存流水线有助于隐藏延迟,但会增加边界处理与资源占用,必须用 Profile 验证。

选择合适的实现层 ​

优先比较成熟库(cuBLAS、cuDNN、CUTLASS)和框架已有实现;规则稳定、需要快速迭代时可用 Triton;需要精细控制硬件映射时再写 CUDA C++。自定义 Kernel 的维护、编译和多架构兼容成本也应计入方案。

一个安全的优化闭环 ​

  1. 用参考实现生成多 shape、多 dtype 的正确性样本。
  2. 预热后测基线,固定 stream、同步方式和重复次数。
  3. 用 Nsight Systems 判断是否被调用间隙、拷贝或通信限制。
  4. 用 Nsight Compute 找到访存、指令、Occupancy 或分支问题。
  5. 只改一个因素,记录配置、结果和误差。
  6. 在目标 shape 集合、边界输入和不同 GPU 上做回归。

常见反效果 ​

  • 为提高 Occupancy 盲目减少寄存器,导致重复加载或指令增加;
  • 融合过多操作,寄存器溢出或并行度下降;
  • 只比较单个 shape,线上 shape 分布变化后反而变慢;
  • 用不同精度或不同输入规模制造“性能提升”;
  • 只测 Kernel,不测框架调度、数据搬运和后处理。

信息来源 ​

延伸阅读 ​

更新日志 ​

  • 2026-09-14:建立从基线、瓶颈判断到回归验证的优化闭环。

学习、验证、修订,持续构建 AI Infra 知识地图。