第1章:融合策略分类——水平融合、垂直融合与混合融合
各位同学,今天我们来聊聊算子融合策略的分类。说实话,这个分类是我在实际项目中反复琢磨出来的。刚开始做存算一体芯片时,我也踩过不少坑——比如把不该合并的算子硬凑在一起,结果性能反而下降了。后来慢慢总结,才理清了这三种融合策略的适用场景。
1.1 为什么需要分类?
你想想看,一个神经网络模型里可能有几十上百个算子。如果一股脑全融合,芯片的布线资源会爆炸;如果完全不融合,数据搬运的功耗又扛不住。所以,我们需要一套分类方法,来指导「哪些算子该合,哪些该分」。
我个人习惯把融合策略分成三大类:
- 水平融合:同一层内的算子合并
- 垂直融合:跨层的算子合并
- 混合融合:前两者的组合拳
下面我们逐一展开。
1.2 水平融合:同层算子合并
水平融合,说白了就是把同一计算层里的多个小算子拼成一个。比如卷积层里,有卷积、偏置、激活函数这三个操作。传统做法是分三步走,每步都要读写一次中间结果。水平融合后,这三个操作在同一个计算单元里一气呵成。
核心思想:减少中间数据的搬运次数。
我在项目中遇到过这样一个案例:一个3×3卷积后面跟着ReLU激活。如果不融合,卷积输出要写回SRAM,ReLU再读出来。融合后,卷积结果直接喂给ReLU,省掉了这趟「内存旅行」。别看一次读写不多,模型跑起来成千上万次,累积的功耗节省相当可观。
我的经验:水平融合最适合那些「计算密集+数据复用率高」的场景。比如卷积+BN+ReLU这种经典组合,几乎可以无脑融合。
水平融合的典型模式:
// 伪代码示例:水平融合前后对比
// 不融合
conv_out = conv(input, weight) // 写回SRAM
bias_out = bias_add(conv_out, bias) // 读SRAM,再写回
relu_out = relu(bias_out) // 读SRAM,再写回
// 水平融合后
fused_out = conv_bias_relu(input, weight, bias) // 一次搞定,无中间写回
1.3 垂直融合:跨层算子合并
垂直融合就更有意思了。它跨越不同的网络层,把前后层的算子合并到一起。比如卷积层和池化层,或者全连接层和Softmax层。
为什么会需要垂直融合?嗯,这里有个关键点:很多网络结构里,前一层输出和后一层输入之间存在「数据依赖」。如果能把这种依赖关系在硬件层面直接打通,就能省掉层与层之间的数据搬运。
注意:垂直融合不是万能的。如果前后层的计算模式差异太大(比如卷积层后面接了一个稀疏矩阵运算),硬融合反而会让硬件控制逻辑变得极其复杂,得不偿失。
我曾经犯过一个错误:把卷积层和后面的全连接层强行融合。结果呢?卷积是数据密集型,全连接是权重密集型,两者的数据流模式完全不同。融合后的控制逻辑占用了大量芯片面积,性能提升却微乎其微。嗯,从那以后,我对垂直融合就谨慎多了。
垂直融合的适用条件:
- 前后层的数据维度匹配(或可以简单变换)
- 计算模式相似(比如都是乘加运算)
- 中间结果不需要被其他分支复用
1.4 混合融合:组合拳
混合融合,就是把水平融合和垂直融合结合起来。说白了,先在同一层内做水平合并,再跨层做垂直合并。这是最灵活、也最考验架构师功力的策略。
我个人的做法是:先画一张数据流图,标出所有算子的计算量和数据搬运量。然后问自己三个问题:
- 哪些算子可以水平合并?(看数据复用率)
- 哪些层可以垂直合并?(看数据依赖关系)
- 合并后会不会引入新的瓶颈?(看计算单元利用率)
下面这张图展示了三种融合策略的对比:
1.5 三种策略的对比
为了让你更直观地理解,我整理了一个对比表:
| 维度 | 水平融合 | 垂直融合 | 混合融合 |
|---|---|---|---|
| 合并范围 | 同一层内 | 跨层 | 层内+跨层 |
| 主要收益 | 减少中间数据搬运 | 消除层间数据依赖 | 综合收益最大化 |
| 设计复杂度 | 低 | 中 | 高 |
| 适用场景 | Conv+BN+ReLU等 | Conv+Pooling等 | 复杂网络结构 |
| 风险点 | 控制逻辑简单 | 数据流不匹配 | 资源开销大 |
我的建议:刚开始做存算一体芯片设计时,先从水平融合入手。它风险低、收益明确。等团队积累了经验,再尝试垂直融合和混合融合。别一上来就想搞个大新闻,稳扎稳打才是正道。
1.6 避坑指南
最后,分享几个我踩过的坑:
- 不要盲目追求融合率:我曾经为了把融合率做到90%以上,把一些计算模式差异很大的算子硬凑在一起。结果芯片面积暴涨,功耗反而没降多少。融合不是越多越好,合适才是最好。
- 注意数据精度:融合后的算子,中间结果的数据类型要统一。我遇到过因为精度不匹配,导致融合后的计算结果和原始模型不一致的情况。排查起来相当痛苦。
- 留好调试接口:融合后的算子是一个整体,出了问题很难定位。我习惯在每个融合算子内部留几个观察点,方便调试时查看中间结果。
好了,这一章的内容就到这里。三种融合策略——水平、垂直、混合——各有各的脾气。理解了它们的区别和适用场景,你就能在设计芯片时做出更明智的选择。