OpenPPL之二,优化器里面的算子融合

算子融合的执行时机

完整的时间线

复制代码
模型加载阶段(一次)          运行时阶段(多次推理)
    ↓                              ↓
┌─────────────────────┐      ┌─────────────┐
│ 1. 解析ONNX模型      │      │ 输入数据     │
│ 2. 构建IR Graph     │      │     ↓       │
│ 3. 图优化(融合)    │ ←──  │ 执行融合算子 │
│ 4. 图分割(多硬件)  │      │     ↓       │
│ 5. 编译成执行计划    │      │ 输出结果     │
└─────────────────────┘      └─────────────┘

关键点

  • 融合发生在模型加载时,只执行一次

  • 运行时直接执行融合后的算子,不再重新融合

  • 因此,融合的误差是固定的,不会随输入变化

OpenPPL 的算子融合功能主要通过以下优化器实现:

文件 功能说明
act_reorder_for_conv_optimizer.cc 激活函数重排序优化。将卷积后的激活函数(如ReLU)提前或调整顺序,以便与卷积核融合或减少中间内存开销
constant_node_optimizer.cc 常量折叠。将输入全为常量的节点在编译时预计算,替换为常量节点
engine_graph_partitioner.cc 引擎图分割器。根据硬件后端(如x86、CUDA)将计算图分割成多个子图,每个子图由对应引擎执行
fuse_bn_optimizer.cc Conv + BatchNorm 融合。将 BN 的参数融合到 Conv 的权重和偏置中,删除 BN 节点
fuse_constant_optimizer.cc Conv + Add/Mul 融合。将 Add 或 Mul 的常量值融合到 Conv 的 bias 中
fuse_parallel_node_optimizer.cc 并行节点融合。融合可以并行执行的无依赖节点
fuse_shape_optimizer.cc Shape 相关算子融合。将 Shape → Gather → Slice → Concat → Add/Div 等多个算子融合成一个 PPL.ShapeOperation 自定义算子
identity_node_optimizer.cc Identity 节点删除。Identity 算子(恒等映射)在推理时无实际作用,直接删除并重新连接输入输出
skip_dropout_optimizer.cc Dropout 删除。推理时 Dropout 无效,直接删除

这9个优化器可以分为几类:

优化器 实现方式 说明
fuse_bn_optimizer.cc 只改参数 将BN参数融合到Conv的weight/bias中,Conv节点类型不变
fuse_constant_optimizer.cc 只改参数 将Add/Mul的常量融合到Conv的bias中,Conv节点类型不变
constant_node_optimizer.cc 只改参数 常量折叠,预计算结果替换原节点,不产生新算子类型
skip_dropout_optimizer.cc 只改参数 删除Dropout节点,不产生新算子
identity_node_optimizer.cc 只改参数 删除Identity节点,不产生新算子
act_reorder_for_conv_optimizer.cc 只改参数 调整激活函数顺序,不产生新算子
engine_graph_partitioner.cc 非融合 图分割,不是算子融合
fuse_parallel_node_optimizer.cc 非融合 并行节点融合,实际是调度优化
fuse_shape_optimizer.cc 生成新算子 将Shape→Gather→Slice→Concat→Add/Div等多个算子融合成一个新的 自定义算子

在**fuse_shape_**optimizer.cc的****Optimize里

复制代码
node->SetType(ir::Node::Type{"pmx", "Shape", 1});

实际看到的是pmx.Shape算子

一、卷积相关融合(最核心)

1. Conv + BN 融合

  • 实现位置FuseBNOptimizer

  • 融合方式:将BatchNorm的参数融合到Conv的权重和偏置中

  • 官方记录 :2021年12月已支持 batchnorm+relu 融合

2. Conv + Add/Mul 融合

  • 实现位置FuseConstantOptimizer

  • 融合方式:将Conv后面的Add或Mul常量节点融合到Conv的bias中

3. Conv + ReLU/ReLU6 融合

  • 官方记录:x86后端所有卷积算法支持 ReLU/ReLU6 融合

  • 融合方式:卷积计算后直接执行激活函数,避免中间结果回写内存

4. Conv + DepthwiseConv 融合

  • 官方记录 :x86后端支持 Conv+DepthwiseConv 融合,使用AVX512/FMA指令集优化

5. Conv + Concat 融合

  • 修复记录:2022年12月修复了Conv+Concat融合时channel不能整除的计算问题

二、Shape相关融合(动态模型专用)

PPL.ShapeOperation 自定义算子

OpenPPL设计了专门的自定义算子 PPL.ShapeOperation 来处理Shape相关计算,支持融合以下算子 :

融合类型 具体算子
提取类 Gather, Slice
拼接类 Concat
数值计算类 Add, Div, Mul, Sub

融合示例(Shufflenet网络):

  • 融合前:Shape → Gather → Div → Concat 等多个算子

  • 融合后:单个 PPL.ShapeOperation 算子,维护系数矩阵表示完整计算逻辑

  • 效果:融合共计20个Shape相关算子

三、其他算子融合

1. Identity 算子融合

  • 官方记录:支持 identity 算子的融合

2. QuickGelu 算子融合

  • 官方记录:Onnxruntime后端已支持 QuickGelu 算子融合

3. Add/Sub/Mul/Div 就地计算(Inplace)

  • 官方记录:x86后端支持 Add/Sub/Mul/Div 就地计算,减少内存分配

四、各后端支持汇总

融合类型 x86 CUDA 说明
Conv + BN CUDA支持batchnorm+relu融合
Conv + ReLU x86所有卷积算法支持
Conv + Add FuseConstantOptimizer实现
Conv + DepthwiseConv AVX512/FMA指令集优化
Shape融合 PPL.ShapeOperation自定义算子
Identity融合 2022年12月加入
QuickGelu Onnxruntime后端支持

算子融合在大多数情况下确实只是修改节点信息而不生成新算子类型,但在某些情况下也会生成全新的算子类型。

两种融合模式

模式1:修改节点信息(最常见)

这种模式不生成新算子类型,只是修改现有节点的属性或删除冗余节点。

典型例子:Conv + BN 融合

cpp

复制代码
// 融合前
Conv节点:weight=W, bias=b
BN节点:gamma=γ, beta=β, mean=μ, var=σ²

// 融合后
Conv节点:weight=W', bias=b'  ← 只修改了属性
BN节点:被删除

发生了什么

  • 没有创建新节点类型

  • Conv节点的类型仍然是 "Conv"

  • 只是更新了它的权重和偏置

  • 删除了BN节点


模式2:生成新算子类型(少见但有)

这种模式会创建全新的节点类型,通常用于:

  1. 融合模式无法用现有算子表达

  2. 后端有专门优化的融合Kernel

典型例子:Conv + Add + Relu 融合(有专门优化时)
复制代码
// 融合前
Conv → Add → Relu

// 融合后(如果有专门优化的Kernel)
FusedConvAddRelu  ← 新类型

为什么需要新类型

  • Conv + Add + Relu 融合后,数据流从 ConvAddRelu 变成了一个端到端的计算

  • 如果后端有专门的手工汇编优化版本(如CUDA的cudnnConvolutionBiasActivationForward),就需要一个新的算子类型来调用它

为什么大多数融合不生成新算子

1. 数学等价性

很多融合(如Conv+BN)可以完全用修改参数来表达,不需要改变计算结构。

2. Kernel复用

修改属性后,现有的Conv Kernel仍然可以正确执行,只是参数不同。

3. 避免组合爆炸

如果为每种组合都创建新算子:

复制代码
ConvBN
ConvBNRelu
ConvAdd
ConvAddRelu
ConvBNAdd
ConvBNAddRelu
... 组合数 = 2^n 爆炸增长

4. 保持图结构简单

算子类型越少,图越简单,后续优化和调试越容易。


什么时候需要生成新算子

场景 原因 例子
后端有专门优化 利用硬件特性(如CUDA的Fused Conv) ConvReluFused
数据布局改变 融合后需要特殊的内存排布 NC4HW4Conv
无法用原算子表达 融合后的计算模式与原算子不同 DepthwiseConvBN
性能关键路径 为特定融合模式手写汇编优化 ConvAddReluInt8
相关推荐
词元Max2 小时前
1.1 AI技术全景图:从传统ML到大模型
人工智能
一水鉴天2 小时前
智能代理体系 20260325(腾讯元宝)
人工智能·架构
新知图书2 小时前
LangGraph节点的并行化处理
人工智能·ai agent·智能体·langgraph
006_2 小时前
python 全球多语言情感分析-模型版
人工智能·自然语言处理
金融小师妹2 小时前
基于AI航运与能源数据模型的极端收缩分析:霍尔木兹海峡从2000万桶到130万桶的结构性断层
大数据·深度学习·svn·能源
DO_Community2 小时前
如何使用DigitalOcean Gradient 平台上的无服务器推理
人工智能·aigc·ai编程·ai推理
ws2019072 小时前
花城聚智:2026广州新能源汽车技术与热管理展为何成产业升级关键节点?
大数据·人工智能·科技·物联网·汽车
康康的AI博客2 小时前
向量数据库选型指南:AI 数据底座怎么选不踩坑
数据库·人工智能
森诺Alyson2 小时前
前沿技术借鉴研讨-2026.3.19(睡眠分期/Agents模拟临床会诊/多模态抑郁症检测)
论文阅读·经验分享·深度学习·论文笔记·论文讨论