常量折叠Constant Folding特性分析【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge1 特性概述常量折叠是 GE 图编译器中一项核心的编译期优化。其核心思想是在图编译阶段识别出所有输入均为常量的算子节点在主机侧提前完成计算将计算结果写回为新的常量节点从而消除运行时不必要的计算开销。GE 的常量折叠不仅是简单的常量表达式求值而是形成了一套包含维度计算、空张量替换、潜在常量标记、延迟生效等机制的完整优化体系贯穿图预处理Prepare、优化阶段 1OptimizeStage1、优化阶段 2OptimizeStage2、自动融合前BeforeAutofuse等多个编译阶段。1.1 解决的问题在深度学习计算图中大量节点的输入在编译期即可确定如模型权重、超参数、Shape 相关操作这些节点如果留到运行时在设备上执行会带来两方面问题不必要的计算开销Shape、Rank、Size 等纯元信息操作完全可以在编译期完成阻碍后续优化未折叠的常量节点阻挡了死代码消除、公共子表达式消除等优化的开展1.2 功能范围常量折叠特性包含以下能力能力说明标准常量折叠将输入全为常量的算子替换为常量节点维度计算折叠对 Shape/Reshape/Transpose 等维度操作进行编译期求值维度调整折叠对 ExpandDims 等改变维度的算子进行原地优化并移除空张量替换将输出为空张量的算子替换为空常量节点潜在常量标记标记当前部分输入为非常量、但未来可能变为全常量的节点潜在常量生效在图遍历结束后统一将已标记的潜在常量节点执行折叠2 用户使用场景2.1 离线编译场景atc用户使用 atc 工具将 ONNX/PB 模型编译为 OM 文件时常量折叠默认在 O1 及以上优化等级启用。用户可通过配置参数控制开关atc --modelmodel.onnx --outputmodel --framework5 \ --ge.oo.levelO1 --ge.oo.constantFoldingtrue典型收益场景模型中包含大量用于动态 Shape 推断的辅助计算如 Shape→Gather→Concat→Reshape 链路这些在静态 Shape 场景下完全可以预计算常量折叠能将其全部消除。2.2 在线编译场景aclgrphBuildModel使用 ACL Graph Builder API 构建模型时通过aclgrphBuildInitialize或aclgrphBuildModel的配置参数控制// 全局级别配置 std::mapge::AscendString, ge::AscendString global_options { {ge::ir_option::OO_LEVEL, O1}, {ge::ir_option::OO_CONSTANT_FOLDING, true} }; aclgrphBuildInitialize(global_options); // 图级别配置优先级更高 std::mapge::AscendString, ge::AscendString build_options { {ge::ir_option::OO_CONSTANT_FOLDING, true} }; aclgrphBuildModel(graph, build_options, modelBufferData);2.3 调试场景当用户怀疑常量折叠导致结果异常时可以关闭该优化进行对比验证{ge::ir_option::OO_CONSTANT_FOLDING, false}关闭后Size、Shape、ShapeN、Rank 等算子不会被折叠删除运行时仍将在设备上执行。ge_deleted_op.cc中的GeDeletedOp机制会针对这些算子给出清晰的错误提示帮助用户定位问题。2.4 用户指定跳过折叠框架侧如 TensorFlow 的_grappler_do_not_remove属性或用户可通过设置节点属性_do_not_constant_folding来阻止特定节点被常量折叠。这为需要保留特定节点的场景提供了精细控制手段。3 对外接口3.1 配置参数参数键参数值配置入口说明ge.oo.constantFoldingtrue/falseaclgrphBuildInitialize, aclgrphBuildModel, atc控制常量折叠优化开关ge.oo.levelO1/O2/O3同上优化等级O1 及以上默认启用常量折叠配置参数定义位于inc/graph_metadef/external/ge_common/ge_api_types.h常量名OO_CONSTANT_FOLDING实际配置键为ge.oo.constantFolding。选项注册位于base/common/option_register.ccREG_OPTION(OO_CONSTANT_FOLDING) .LEVELS(OoLevel::kO1) .DEFAULT_VALUES({{OoLevel::kO1, true}, {OoLevel::kO3, true}}) .CHECKER(OoInfoUtils::IsSwitchOptValueValid) .VISIBILITY({OoEntryPoint::kSession, OoEntryPoint::kIrBuild, OoEntryPoint::kAtc}) .SHOW_NAME(OoEntryPoint::kAtc, oo_constant_folding, OoCategory::kModelTuning)选项在 O1 和 O3 优化等级下默认开启支持 Session、IR Build 和 ATC 三个入口配置。3.2 节点属性接口属性名作用设置方ATTR_NO_NEED_CONSTANT_FOLDING标记节点无需常量折叠GE 内部 PassATTR_NAME_DO_NOT_CONSTANT_FOLDING标记用户指定节点不做常量折叠框架 Parser / 用户ATTR_NAME_POTENTIAL_CONST标记节点为潜在常量GE 常量折叠 PassATTR_NAME_POTENTIAL_WEIGHT存储潜在常量的权重值GE 常量折叠 PassATTR_NAME_POTENTIAL_WEIGHT_INDICES存储潜在常量的输出索引GE 常量折叠 Pass_is_from_constant_folding标记常量节点由常量折叠产生GE 常量折叠 PassATTR_NAME_IS_INSERTED_BY_GE标记节点由 GE 内部插入GE 内部 Pass属性定义位于inc/graph_metadef/graph/debug/ge_attr_define.h。3.3 工具类接口GraphOptimizeUtility::ConstantFolding位于compiler/graph/manager/util/graph_optimize_utility.cc提供了单节点级别的常量折叠接口供其他模块如权重压缩判断WeightCompressJudge按需调用。该接口依次执行 ConstantFoldingPass → DimensionComputePass → ReplaceWithEmptyConstPass。4 具体实现4.1 整体架构常量折叠采用 Pass 链式执行模式通过 GE 的 Pass 管理框架驱动。核心实现集中在compiler/graph/passes/standard_optimize/constant_folding/目录下由 7 个 Pass 和配套的基础设施组成┌──────────────────────────┐ │ BaseNodePass │ │ (compiler/graph/passes │ │ /base_pass.h) │ └──────────┬───────────────┘ │ ┌──────────▼───────────────┐ │ FoldingPass │ │ (folding_pass.h/cc) │ │ 折叠基础操作 │ │ 创建Const节点、删除原节点 │ │ 重连数据边、保留控制边 │ └──────────┬───────────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ┌──────────▼──────┐ ┌─────▼──────────┐ ┌──▼──────────────────┐ │ PotentialFolding │ │DimensionAdjust │ │ ReplaceWithEmpty │ │ Pass │ │ Pass │ │ ConstPass │ │ (potential_ │ │(dimension_ │ │(replace_with_ │ │ folding_pass) │ │ adjust_pass) │ │ empty_const_pass) │ └────────┬────────┘ └────────────────┘ └─────────────────────┘ │ ┌──────────┼──────────┐ │ │ ┌──────▼──────┐ ┌───────────▼──────────┐ │ Constant │ │ DimensionCompute │ │ FoldingPass │ │ Pass │ │ (constant_ │ │ (dimension_compute_ │ │ folding_ │ │ pass) │ │ pass) │ └──────────────────────┘ └─────────────┘4.2 Pass 继承体系常量折叠的 Pass 体系采用三层继承结构逐层增加能力BaseNodePass→ 定义了节点级 Pass 的基本框架包括节点遍历、重 PassRePass、节点删除跟踪等机制。FoldingPass→ 继承 BaseNodePass实现了核心的折叠动作FoldingPass::Folding包括收集被折叠节点的下游锚点连接关系GetIndexAndPeerInDataAnchors处理 Switch/RefSwitch 类型的输入节点断开数据边、插入 Identity 节点保持控制依赖创建新的 Const 节点替换原节点输出AddConstNodeToGraph将新 Const 节点连接到原节点的所有下游消费者隔离并删除原节点及其变为孤立的输入常量节点传递流标签Stream Label属性以保持流规划的正确性PotentialFoldingPass→ 继承 FoldingPass引入潜在常量概念和计算调度机制由PotentialFoldingPass::Run统一编排。ConstantFoldingPass / DimensionComputePass / ReplaceWithEmptyConstPass→ 继承 PotentialFoldingPass各自实现不同的计算策略和判断逻辑。DimensionAdjustPass→ 直接继承 BaseNodePass处理维度调整类算子如 ExpandDims在主机侧完成计算后直接隔离删除不走完整的常量折叠流程。PotentialConstTakenEffectPass→ 直接继承 FoldingPass负责在图遍历结束后统一处理所有已标记的潜在常量节点。4.3 核心 Pass 详解4.3.1 ConstantFoldingPass — 标准常量折叠文件compiler/graph/passes/standard_optimize/constant_folding/constant_folding_pass.h/cc这是常量折叠的主入口 Pass处理所有输入均为常量的节点。其执行流程如下前置检查检查用户是否设置了_do_not_constant_folding属性若是则跳过检查节点是否标注了ATTR_NO_NEED_CONSTANT_FOLDING若是则跳过检查节点是否为潜在空常量所有输出 Shape 为空若是则交给 ReplaceWithEmptyConstPass 处理输入验证确认节点的所有输入均为常量节点通过OpDescUtils::GetConstInputNodeAndAnchor若存在非常量输入但该节点被标记为潜在常量则置need_fold_ false并返回 NOT_CHANGED本轮不折叠但保留标记从常量输入节点提取权重值通过OpDescUtils::GetWeightsFromNodes内存优先策略检查当配置了 MemoryPriority 策略时若输入常量节点的 Shape 较大8且被多个下游共享则跳过折叠。因为折叠会复制一份常量数据在内存受限场景下得不偿失计算执行两级回退策略第一级AICPU 算子内核ComputeWithHostCpuKernel尝试通过aicpu_ascend_kernel引擎获取算子的 Host CPU 实现通过OpKernelRegistry创建算子实例由HostCpuEngine执行这一级支持最广泛的算子类型运行时加载libconstant_folding_ops.so第二级GE 内置内核ComputeWithBuiltInKernel若 AICPU 不支持该算子回退到 GE 内置的 Host Kernel通过KernelFactory按算子类型查找注册的 Kernelfolding_pass::GetKernelByTypeGE 内置 Kernel 覆盖了约 40 种常见算子折叠替换计算成功后由FoldingPass::Folding完成图结构变换新建的 Const 节点会被标记_is_from_constant_foldingtrue用于后续流程识别性能统计分别记录 AICPU 内核和 GE 内置内核的折叠耗时和调用次数在GraphManager::OptimizeStage1_2中汇总输出性能追踪日志Pass 注册宏REG_PASS_OPTION(ConstantFoldingPass).SWITCH_OPT(ge::OO_CONSTANT_FOLDING);该 Pass 受ge.oo.constantFolding开关控制。4.3.2 DimensionComputePass — 维度计算折叠文件compiler/graph/passes/standard_optimize/constant_folding/dimension_compute_pass.h/cc专门处理维度相关的计算操作如 Shape、Reshape、Transpose 等。与 ConstantFoldingPass 的区别在于仅使用 GE 内置 Kernel通过folding_pass::GetKernelByType获取 Kernel 并计算不尝试 AICPU 内核支持标记但不折叠模式可通过构造参数need_fold控制是否只做计算标记在预处理阶段以need_foldfalse模式运行只标记潜在常量不实际折叠支持与 PotentialFoldingPass 的潜在常量机制配合在预处理阶段GraphPrepare::ComputeConstantShapeDimensionComputePass 以need_foldfalse模式运行目的是先利用维度计算确定 Shape 信息为后续的 InferShape 提供更准确的输入。Pass 注册宏REG_PASS_OPTION(DimensionComputePass).LEVELS(OoLevel::kO1).SWITCH_OPT(ge::OO_CONSTANT_FOLDING);4.3.3 DimensionAdjustPass — 维度调整折叠文件compiler/graph/passes/standard_optimize/constant_folding/dimension_adjust_pass.h/cc处理 ExpandDims 等通过常量参数调整维度的算子。工作流程获取节点原始类型查找对应的 GE 内置 Kernel检查是否为未知 Shape未知 Shape 的节点跳过调用 Kernel 的Compute(node)接口进行维度调整计算删除无用的常量轴参数输入节点通过IsolateAndDeleteNode将节点隔离删除并按{0}的 IO 映射重连数据边即只保留数据输入直连数据输出DimensionAdjustPass 不走完整的常量折叠替换流程不创建 Const 节点而是直接完成维度推断后将节点简化为直连。Pass 注册宏REG_PASS_OPTION(DimensionAdjustPass).LEVELS(OoLevel::kO1).SWITCH_OPT(ge::OO_CONSTANT_FOLDING);4.3.4 ReplaceWithEmptyConstPass — 空张量替换文件compiler/graph/passes/standard_optimize/constant_folding/replace_with_empty_const_pass.h/cc识别输出为空张量Shape 中包含维度 0如[0, 3, 224, 224]的算子将其替换为空常量节点。排除规则不会被替换的节点类型Const/ConstantOp/FileConstant 等常量类节点Data 类节点NetOutput 节点控制流算子Switch、Merge、Enter、NextIteration、Exit、LoopCond 等资源算子Stack、StackPop、StackPushHcom 集合通信算子无输出描述的节点GE 内部插入的节点ATTR_NAME_IS_INSERTED_BY_GE标记此 Pass 也支持need_fold参数控制是否只做标记。Pass 注册宏REG_PASS_OPTION(ReplaceWithEmptyConstPass).LEVELS(OoLevel::kO1).SWITCH_OPT(ge::OO_CONSTANT_FOLDING);4.3.5 PotentialConstTakenEffectPass — 潜在常量生效文件compiler/graph/passes/standard_optimize/constant_folding/potential_const_taken_effect_pass.h/cc这是一个特殊 Pass其Run方法为空操作直接返回 SUCCESS实际工作在OnFinishGraph回调中完成。设计意图在图遍历过程中某些节点的部分输入在当前轮次还不是常量例如来自 Shape 推断的中间结果DimensionComputePass 等会将其标记为潜在常量并缓存权重。当图遍历完成后所有 Pass 已执行完毕潜在常量的输入可能已在前序轮次被折叠为常量。此时 PotentialConstTakenEffectPass 在OnFinishGraph中统一扫描所有标记为潜在常量的节点执行延迟折叠。处理流程遍历图中所有节点找到带有ATTR_NAME_POTENTIAL_CONST标记的节点从属性中读取缓存的潜在权重ATTR_NAME_POTENTIAL_WEIGHT若权重存在调用FoldingPass::Folding执行折叠若权重缺失清除潜在常量相关属性并记录警告收集所有需要重 Pass 的节点传递给下一轮 Pass 执行Pass 注册宏REG_PASS_OPTION(PotentialConstTakenEffectPass).LEVELS(OoLevel::kO1).SWITCH_OPT(ge::OO_CONSTANT_FOLDING);4.4 潜在常量机制潜在常量Potential Const是 GE 常量折叠的核心创新之一用于解决当前输入不全为常量但后续可能变为常量的场景。相关属性和工具类组件位置说明ConstantUtilsinc/graph_metadef/graph/utils/constant_utils.h潜在常量标记/查询/清除工具类ATTR_NAME_POTENTIAL_CONSTinc/graph_metadef/graph/debug/ge_attr_define.h标记节点为潜在常量ATTR_NAME_POTENTIAL_WEIGHT同上缓存潜在常量的权重值ATTR_NAME_POTENTIAL_WEIGHT_INDICES同上标记哪些输出索引为潜在常量_source_pass_of_potential_constpotential_folding_pass.cc记录来源 Pass 名防止跨 Pass 误操作机制流程DimensionComputePass / DimensionComputePass │ │ (节点部分输入为非常量) ▼ ComputePotentialWeight() → 计算成功得到输出权重 → need_fold false (本轮不折叠) │ ▼ UpdatePotentialConstMark() → MarkPotentialConstWithPassName() → ConstantUtils::MarkPotentialConst() → 设置 _source_pass_of_potential_const │ ▼ (多轮 InferShape ConstantFolding 后 部分输入被其他 Pass 折叠为常量) │ ▼ PotentialConstTakenEffectPass::OnFinishGraph() → 读取 ATTR_NAME_POTENTIAL_WEIGHT → FoldingPass::Folding() 执行折叠PotentialFoldingPass中的IsCurPassSameWithSource方法确保只有最初标记潜在常量的 Pass 才能更新或清除该标记避免不同 Pass 之间的干扰。4.5 计算引擎常量折叠的计算引擎分为两层4.5.1 GE 内置 Host Kernel位于compiler/host_kernels/通过KernelFactory注册和创建。Kernel基类定义了三种 Compute 接口Compute(OpDescPtr, inputs, outputs)— 基于输入张量计算输出张量用于 ConstantFoldingPass 和 DimensionComputePassCompute(NodePtr, outputs)— 基于节点信息计算输出部分 Kernel 使用Compute(NodePtr)— 仅修改节点属性用于 DimensionAdjustPass已注册的 Host Kernel 按类别分布如下类别目录算子列表数组操作host_kernels/array_ops/Reshape, Squeeze, SqueezeV3, Unsqueeze, UnsqueezeV3, ExpandDims, Rank, Shape, ShapeN, Size, Identity, Empty, BroadcastArgs, BroadcastGradientArgs, GatherShapes逐元计算host_kernels/elewise_calculation_ops/Add, Sub, Mul, FloorDiv, FloorMod, Maximum, Greater, Rsqrt, Cast选择操作host_kernels/selection_ops/Slice, SliceD, StridedSlice, GatherV2, Range变换操作host_kernels/transformation_ops/Transpose, Permute, Transdata, FlattenV2拼接拆分host_kernels/split_combination_ops/ConcatV2, ConcatOffset, Pack, Unpack填充操作host_kernels/pad_ops/Fill规约操作host_kernels/reduce_ops/ReduceProd数据流host_kernels/data_flow_ops/DynamicStitch自定义host_kernels/custom_ops/ReFormat, SsdPriorBox每个 Kernel 通过REGISTER_COMPUTE_NODE_KERNEL宏注册到KernelFactory运行时按算子类型查找。4.5.2 AICPU Host CPU 引擎作为 GE 内置 Kernel 的补充通过 AICPU 引擎执行算子。位于runtime/v2/engine/aicpu/和runtime/v1/hybrid/node_executor/host_cpu/。加载路径为libconstant_folding_ops.so由 OPPOperator Package提供包含更广泛的算子实现。compiler/engines/cpu_engine/cpu_engine/constant_folding_stub/constant_folding_ops_stub.cpp是编译期的桩库无实际实现运行时由真正的 OPP 库替换。HostCpuEngine负责加载libconstant_folding_ops.so动态库通过OpKernelRegistry创建算子实例调用RunHostCpuKernel执行计算4.6 编译流程集成常量折叠在 GE 图编译的多个阶段被调用形成多轮迭代优化GraphPrepare::ComputeConstantShape (预处理阶段) │ ├── ReplaceWithEmptyConstPass (need_foldfalse, 仅标记) ├── DimensionComputePass (need_foldfalse, 仅标记) ├── ConstantClipPass ├── ConstantFoldingPass └── InferValueRangePass GraphManager::OptimizeStage1_2 (优化阶段1) │ ├── ReplaceWithEmptyConstPass ├── DimensionComputePass ├── ConstantClipPass ├── ConstantFoldingPass └── DimensionAdjustPass GraphManager::OptimizeStage2 (优化阶段2, 合并子图后) │ ├── ConstantFoldingPass ├── ReshapeRemovePass ├── CondRemovePass ├── AssignRemovePass └── DimensionAdjustPass GraphOptimizerBeforeAutofuse (自动融合前) │ └── ConstantFoldingPass多阶段调用的设计考虑预处理阶段以标记模式运行 DimensionComputePass先完成维度推断再执行标准折叠优化阶段1是常量折叠的主战场包含所有 Pass 的完整执行优化阶段2对合并子图后的图再做一次常量折叠消除子图合并引入的新常量计算自动融合前额外执行一次常量折叠确保融合 Pass 面对的是最优化的图结构4.7 与其他优化的协作常量折叠与多个其他优化 Pass 存在协作关系ConstantClipPass在常量折叠前处理权重裁剪插入的 Min/Max 节点会被后续的 ConstantFoldingPass 再次折叠DeadCodeElimination常量折叠产生的孤立常量节点会被死代码消除清除CommonSubexpressionElimination共享同一常量输入的场景下常量折叠后公共子表达式消除可以进一步去重WeightCompressJudge权重压缩判断在执行前调用GraphOptimizeUtility::ConstantFolding先对权重相关节点做常量折叠DeleteNoConstFoldingFusionPassFE 量化 Pass在融合阶段删除AscendWeightQuant算子上的ATTR_NO_NEED_CONSTANT_FOLDING属性使其可以被常量折叠处理DecomposeLargeConstPass大常量拆分 Pass 会继承_is_from_constant_folding属性到拆分后的新常量节点AscIrLowerer在 IR Lowering 阶段对来自常量折叠的控制边做特殊处理允许移除以获得更多融合机会CtrlEdgeTransferPass常量折叠后形成的控制边关系需要由控制边转移 Pass 清理【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考