一、算子融合概述:到底什么是算子融合?

各位同学好,我是老张。今天咱们开始这门课的第一章——算子融合。说实话,这个主题我讲了快十年,每次都有新感悟。你想想看,深度学习模型越来越大,从几层到几百层,计算图也越来越复杂。那怎么让这些模型跑得更快?算子融合就是其中一个非常关键的手段。

我个人习惯把算子融合比作「做饭」。你炒一盘菜,如果每放一种调料就关一次火、洗一次锅,那效率得多低?算子融合就是把这些「关火洗锅」的步骤省掉,让整个流程一气呵成。

1.1 什么是算子融合?

说白了,算子融合就是把计算图中多个连续的算子(操作)合并成一个算子。比如你有一个 Conv2d,后面跟着一个 BatchNorm,再跟着一个 ReLU。传统做法是三个算子依次执行,每次都要读写中间结果。融合之后,这三个操作变成一个算子,中间数据直接在寄存器或缓存里传递,不用来回写显存。

核心定义:算子融合是将多个连续的计算操作合并为一个复合操作,减少中间数据的存储和传输开销。

举个例子,假设你有这么一段计算图:

// 融合前的计算图
input -> Conv2d -> BatchNorm -> ReLU -> output

// 融合后的计算图
input -> FusedConvBNReLU -> output

嗯,这里要注意:融合不是简单的「拼在一起」,而是要在数学上保证等价。我在项目中遇到过有人把不同精度的算子硬凑在一起,结果精度掉了两个点,排查了半天才发现是融合逻辑写错了。

1.2 为什么需要算子融合?

这个问题我问过很多刚入行的同学。答案五花八门,但核心原因其实就两个:访存瓶颈和计算效率。

你想想看,现代 GPU 算力有多强?A100 的 FP16 算力接近 312 TFLOPS。但显存带宽呢?只有 2 TB/s 左右。这意味着什么?意味着计算单元经常在「等数据」。我做过一个 profiling,有些模型的计算单元利用率不到 30%,剩下的时间都在等数据搬运。

算子融合能解决这个问题,原因有三:

  1. 减少中间数据读写:融合前,每个算子都要把结果写回显存,下一个算子再读进来。融合后,中间数据直接留在片上缓存或寄存器里。
  2. 减少 kernel launch 开销:每个算子对应一个 GPU kernel。启动 kernel 是有开销的,包括上下文切换、参数传递等。融合后 kernel 数量减少,这部分开销自然就降下来了。
  3. 提升计算密度:融合后的算子通常有更高的计算密度(计算量/访存量),更容易打满计算单元。

我的经验:曾经优化一个 BERT 模型,光是把 LayerNorm 和后面的 Add 融合,推理速度就提升了 15%。别小看这点提升,在生产环境里,15% 意味着能省下好几台服务器。

1.3 融合的收益到底有多大?

咱们用数据说话。下表是我在某个实际项目中测出来的数据,模型是 ResNet-50,batch size=64,GPU 是 V100:

优化阶段 延迟 (ms) 吞吐 (images/s) 显存占用 (MB)
未优化 12.5 5120 2456
Conv+BN+ReLU 融合 9.8 6530 1892
进一步融合 Add+ReLU 8.6 7440 1623
全图融合优化 7.2 8888 1345

看到了吗?从 12.5ms 降到 7.2ms,提升了 42%。显存占用也降了将近一半。这就是算子融合的威力。

1.4 融合的挑战与陷阱

收益这么大,那是不是所有算子都能融合?当然不是。我曾经踩过一个坑,把两个形状不兼容的算子强行融合,结果跑出来的结果全是错的。嗯,这里给大家总结几个常见的挑战:

  • 数学等价性:融合后的结果必须和融合前完全一致。比如 Conv 和 BN 融合时,BN 的均值和方差是在训练时统计的,推理时是固定的。如果搞混了训练和推理模式,精度就会出问题。
  • 硬件限制:不是所有硬件都支持任意融合。比如某些老款 GPU 的指令集不支持某些复合操作,强行融合反而会触发软件模拟,性能更差。
  • 内存压力:融合后的算子可能需要更多的寄存器或共享内存。如果超出硬件限制,编译器会自动 spill 到显存,得不偿失。
  • 调试难度:融合后的算子是个黑盒,出了问题很难定位。我建议在开发阶段保留一个未融合的版本做对照。

避坑指南:我曾经把一个 Conv+BN+ReLU 融合后的 kernel 直接部署到生产环境,结果发现某些输入尺寸下性能反而下降了。后来一查,是因为融合后的 kernel 占用了太多寄存器,导致 occupancy 下降。所以,融合不是万能的,一定要做充分的 benchmark。

1.5 算子融合的知识体系

为了让大家对算子融合有个全局认识,我画了一张图。这张图涵盖了算子融合的核心概念、收益、挑战以及后续章节会讲到的技术方向。

算子融合知识体系 算子融合 什么是算子融合 合并多个连续算子 为什么需要融合 解决访存瓶颈 收益与挑战 性能提升 vs 实现风险 水平融合 相同算子批量处理 垂直融合 不同算子流水处理 跨算子融合 跨层/跨子图融合 减少访存 中间数据片上传递 减少 Kernel 启动 降低调度开销 提升计算密度 打满计算单元 收益:延迟降低 实测提升 20-50% 挑战:数学等价 精度必须严格对齐 挑战:硬件限制 寄存器/指令集约束 融合策略设计 模式匹配 / 动态规划 硬件适配优化 GPU / NPU / CPU 差异 编译器实现 TVM / MLIR / XLA 后续章节将围绕这三个方向深入展开,结合实战案例讲解具体实现

这张图把算子融合的核心内容分成了三个维度:概念(什么是融合)、动机(为什么需要融合)、权衡(收益与挑战)。下面还列出了后续章节会深入讲解的技术方向:融合策略设计、硬件适配优化、编译器实现。大家先有个整体印象,后面我们会一个一个啃下来。

1.6 小结

好了,第一章的内容就到这里。总结一下:

  • 算子融合就是把多个连续算子合并成一个,减少中间数据搬运和 kernel 启动开销。
  • 核心收益是降低延迟、提升吞吐、减少显存占用。
  • 挑战在于数学等价性、硬件限制、内存压力和调试难度。
  • 融合不是银弹,需要结合具体硬件和模型做 benchmark。