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.ccConv BatchNorm 融合。将 BN 的参数融合到 Conv 的权重和偏置中删除 BN 节点fuse_constant_optimizer.ccConv Add/Mul 融合。将 Add 或 Mul 的常量值融合到 Conv 的 bias 中fuse_parallel_node_optimizer.cc并行节点融合。融合可以并行执行的无依赖节点fuse_shape_optimizer.ccShape 相关算子融合。将 Shape → Gather → Slice → Concat → Add/Div 等多个算子融合成一个PPL.ShapeOperation自定义算子identity_node_optimizer.ccIdentity 节点删除。Identity 算子恒等映射在推理时无实际作用直接删除并重新连接输入输出skip_dropout_optimizer.ccDropout 删除。推理时 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月已支持batchnormrelu融合2. Conv Add/Mul 融合实现位置FuseConstantOptimizer融合方式将Conv后面的Add或Mul常量节点融合到Conv的bias中3. Conv ReLU/ReLU6 融合官方记录x86后端所有卷积算法支持 ReLU/ReLU6 融合融合方式卷积计算后直接执行激活函数避免中间结果回写内存4. Conv DepthwiseConv 融合官方记录x86后端支持ConvDepthwiseConv融合使用AVX512/FMA指令集优化5. Conv Concat 融合修复记录2022年12月修复了ConvConcat融合时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 就地计算减少内存分配四、各后端支持汇总融合类型x86CUDA说明Conv BN✅✅CUDA支持batchnormrelu融合Conv ReLU✅✅x86所有卷积算法支持Conv Add✅✅FuseConstantOptimizer实现Conv DepthwiseConv✅❌AVX512/FMA指令集优化Shape融合✅✅PPL.ShapeOperation自定义算子Identity融合✅✅2022年12月加入QuickGelu❌✅Onnxruntime后端支持算子融合在大多数情况下确实只是修改节点信息而不生成新算子类型但在某些情况下也会生成全新的算子类型。两种融合模式模式1修改节点信息最常见这种模式不生成新算子类型只是修改现有节点的属性或删除冗余节点。典型例子Conv BN 融合cpp// 融合前 Conv节点weightW, biasb BN节点gammaγ, betaβ, meanμ, varσ² // 融合后 Conv节点weightW, biasb ← 只修改了属性 BN节点被删除发生了什么没有创建新节点类型Conv节点的类型仍然是Conv只是更新了它的权重和偏置删除了BN节点模式2生成新算子类型少见但有这种模式会创建全新的节点类型通常用于融合模式无法用现有算子表达后端有专门优化的融合Kernel典型例子Conv Add Relu 融合有专门优化时// 融合前 Conv → Add → Relu // 融合后如果有专门优化的Kernel FusedConvAddRelu ← 新类型为什么需要新类型Conv Add Relu 融合后数据流从Conv→Add→Relu变成了一个端到端的计算如果后端有专门的手工汇编优化版本如CUDA的cudnnConvolutionBiasActivationForward就需要一个新的算子类型来调用它为什么大多数融合不生成新算子1. 数学等价性很多融合如ConvBN可以完全用修改参数来表达不需要改变计算结构。2. Kernel复用修改属性后现有的Conv Kernel仍然可以正确执行只是参数不同。3. 避免组合爆炸如果为每种组合都创建新算子ConvBN ConvBNRelu ConvAdd ConvAddRelu ConvBNAdd ConvBNAddRelu ... 组合数 2^n 爆炸增长4. 保持图结构简单算子类型越少图越简单后续优化和调试越容易。什么时候需要生成新算子场景原因例子后端有专门优化利用硬件特性如CUDA的Fused ConvConvReluFused数据布局改变融合后需要特殊的内存排布NC4HW4Conv无法用原算子表达融合后的计算模式与原算子不同DepthwiseConvBN性能关键路径为特定融合模式手写汇编优化ConvAddReluInt8
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2452826.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!