一、算子融合概述:到底什么是算子融合?
各位同学好,我是老张。今天咱们开始这门课的第一章——算子融合。说实话,这个主题我讲了快十年,每次都有新感悟。你想想看,深度学习模型越来越大,从几层到几百层,计算图也越来越复杂。那怎么让这些模型跑得更快?算子融合就是其中一个非常关键的手段。
我个人习惯把算子融合比作「做饭」。你炒一盘菜,如果每放一种调料就关一次火、洗一次锅,那效率得多低?算子融合就是把这些「关火洗锅」的步骤省掉,让整个流程一气呵成。
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%,剩下的时间都在等数据搬运。
算子融合能解决这个问题,原因有三:
- 减少中间数据读写:融合前,每个算子都要把结果写回显存,下一个算子再读进来。融合后,中间数据直接留在片上缓存或寄存器里。
- 减少 kernel launch 开销:每个算子对应一个 GPU kernel。启动 kernel 是有开销的,包括上下文切换、参数传递等。融合后 kernel 数量减少,这部分开销自然就降下来了。
- 提升计算密度:融合后的算子通常有更高的计算密度(计算量/访存量),更容易打满计算单元。
我的经验:曾经优化一个 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 算子融合的知识体系
为了让大家对算子融合有个全局认识,我画了一张图。这张图涵盖了算子融合的核心概念、收益、挑战以及后续章节会讲到的技术方向。
这张图把算子融合的核心内容分成了三个维度:概念(什么是融合)、动机(为什么需要融合)、权衡(收益与挑战)。下面还列出了后续章节会深入讲解的技术方向:融合策略设计、硬件适配优化、编译器实现。大家先有个整体印象,后面我们会一个一个啃下来。
1.6 小结
好了,第一章的内容就到这里。总结一下:
- 算子融合就是把多个连续算子合并成一个,减少中间数据搬运和 kernel 启动开销。
- 核心收益是降低延迟、提升吞吐、减少显存占用。
- 挑战在于数学等价性、硬件限制、内存压力和调试难度。
- 融合不是银弹,需要结合具体硬件和模型做 benchmark。