CANN graph-autofusion 自动融合精度一致性指南:二进制不一致的根源、保证边界与实操应对

发布时间:2026/9/18 20:03:20
CANN graph-autofusion 自动融合精度一致性指南:二进制不一致的根源、保证边界与实操应对 CANN graph-autofusion 自动融合精度一致性指南二进制不一致的根源、保证边界与实操应对【免费下载链接】graph-autofusionGraph-autofusion 是一个面向昇腾Ascend芯片的轻量级、解耦式组件集合旨在通过自动融合技术加速模型执行。 目前已开源 SuperKernel 组件和 Autofuse 组件未来将持续开放更多自动融合相关模块。项目地址: https://gitcode.com/cann/graph-autofusion导读本文基于 CANN graph-autofusion 开源仓库的《关于自动融合模块精度一致性的说明》系统阐述自动融合Autofuse与 Eager 手写算子之间二进制精度差异的根本来源、自动融合自身提供的精度保证以及希望在继续使用自动融合的前提下与 Eager 严格对齐时的实操处理路径。读者阅读本文后将理解为何融合代码与手写 kernel 在末位上必然存在差异自动融合为何能做到精度不差于 Eager并掌握 PyTorch Inductor 与 TensorFlowGE 后端两个场景下关闭/规避特定融合以对齐 Eager 的具体开关与注意事项。一、结论先行不保证二进制一致但保证精度不差于 Eager自动融合模块无法保证与 Eager 模式下手写算子的输出在二进制bitwise上完全一致但通过保守的精度策略可以保证精度不差于 Eager 模式。这两句话分别界定了能承诺什么与不能承诺什么也是本文全部内容展开的两条主线承诺项是否保证与 Eager 手写算子二进制完全一致❌ 不保证计算类算子纯搬运类算子理论上可一致精度不差于 Eager累积舍入误差上界 ≤ Eager✅ 保证基于保守的精度策略自动融合自身的输出确定性可复现性✅ 保证编译期输入与运行期输入不变时多次运行输出二进制一致二、精度差异的根本来源从源码不同到数值不同2.1 代码生成路径不同——一切差异的起点Eager 模式每个算子由人工预先编写编译为固定的二进制 kernel自动融合通过 codegen 技术在运行时JIT生成算子代码。即使不做任何融合同一语义的算子自动融合生成的代码与手写 kernel 也不是同一份源码。源码不同是一切数值差异的起点。在 graph-autofusion 仓库中这条 JIT 代码生成路径的核心实现位于 autofuse/codegen 目录如 codegen_kernel.cpp、codegen_tiling.cpp自动融合在编译期动态产出融合 kernel 的代码与 Tiling 策略这与人工固化在算子库中的二进制实现本质上是两条独立的生产链。2.2 源码差异在四个层面演变为末位差异同一算子的两份不同源码至少会在以下四个层面引入可观察的末位差异1. 升精度策略不同为保持 fp16 下的数值稳定性算子内部常把中间计算提升到 fp32。是否升、在哪一步升、何时降回手写实现与 codegen 实现的选择不一定一致。2. 指令选择不同同一语义可以用不同指令实现。例如Tensor * Scalar先 broadcast scalar再走标准乘法指令直接使用muls类的标量乘指令。两者数学上等价但在硬件上走的是不同的乘法单元舍入行为不完全一致。3. 切块策略与 Reduction 累加顺序不同浮点加法不满足结合律(ab)c与a(bc)的结果在最低位通常不同。reduce、matmul、layernorm等计算的归约顺序由切块策略和并行度决定而切块策略本身在自动融合与手写算子之间从硬件资源约束上不可能一致。展开来说在相同大小切块下一个融合 kernel 内部要承担多个算子的中间计算单步 UBUnified Buffer占用会高于单算子 kernel。而手写单算子为了降低搬运开销往往会把切块尽量做大用满 UB——这种切块尺寸在融合场景下可能放不下。4. 算法实现不同同一数学函数可以用不同算法逼近。例如rsqrt、div常采用多轮迭代逼近实现迭代轮次、初值选取不同末位误差就不同。2.3 小结这是原理层面限制而非工程 bug源码不同 → 在升精度、指令、累加序、算法等任一维度产生差异 → 最终输出在二进制位上不一致。这是原理层面的限制不是可通过工程对齐消除的 bug。三、可以保证二进制一致的场景纯搬运类算子精度差异来源于计算。对纯搬运类算子不做任何数学运算只重排数据broadcastconcat/splittranspose/reshape/slice这类算子理论上可以做到二进制一致。但这类场景的融合收益通常很小。自动融合的收益主要来自多个计算算子融合消除中间结果的读写多个计算 单个搬运的融合计算与访存 overlap。纯搬运融合在真实业务中占比低因此能保证一致的场景与值得融合的场景交集很窄。值得注意的是从仓库的精度提升实现看improve_precision.cpp 中内置的默认黑名单kBlackList1恰好包含Data、Load、Scalar、Store、Output、Broadcast、Transpose、Concat、Gather、Slice等节点类型——这些纯搬运/数据类节点不参与计算精度提升也从实现层面印证了搬运类算子不做数学运算、不引入数值差异的设计判断。四、自动融合提供的三项保证虽然与手写算子之间不保证二进制一致自动融合在精度与可复现性两个方向上提供以下保证4.1 融合区域内部强制 fp32 累积自动融合在生成的融合 kernel 内部默认将所有中间计算统一提升到 fp32只在融合块的输入/输出边界保留原 dtype。以损失部分计算性能的代价换取更高的中间精度。仓库中的实现证据非常直观ImprovePrecisionForAscGraphimprove_precision.cpp作为 PreProcess::Run 的第一步被调用其核心动作包括在Load/Gather之后插入升精度Castfp16/bf16 → fp32见ProcessLoadGatherNodes将计算类节点的输出 dtype 从 fp16/bf16 统一改写为 fp32FromDtypeToOtherDtype在Store落盘前插入降回原 dtype 的CastProcessStoreNodes保证融合块边界 dtype 不变。对应的 ST 测试 test_improve_precision.cpp 验证了典型场景一条load → abs → add → mul → sub → store的 fp16 链路经过ImprovePrecisionForAscGraph处理后abs0、add0、mul0、sub0的输出 dtype 全部变为DT_FLOATfp32且 Load 之后至少插入一个升精度 Cast。同时该 pass 提供黑名单机制--autofuse_enhance_precision_blacklist可配置跳过硬编码黑名单之外的指定 AscIR 算子类型取值为算子类型字符串多个用英文逗号分隔或配置为all默认值为空。需要注意Sum、Mean、Prod不支持低精度类型即使加入黑名单也仍会提升精度CheckNodeDtypeimprove_precision.cpp会校验加入黑名单的节点在低精度下仍被 InferDtype 支持否则直接报错拒绝配置。更多环境变量细节参见 环境变量参考。4.2 精度不差于 Eager 模式Eager 模式下每个算子独立执行算子与算子的中间结果必须按 tensor 原 dtype如 fp16/bf16落盘再由下一个算子读入。这意味着Eager 模式在每个算子边界都发生一次 fp16/bf16 截断自动融合把一串算子合并为一个 kernel只在融合块边界截断块内全程 fp32。因此自动融合的累积舍入误差上界 ≤ Eager 模式下对应算子链的累积误差。即精度不低于 Eager 模式。这一点与 4.1 的源码实现完全吻合升精度 Cast 插在Load之后、降精度 Cast 插在Store之前正是块内 fp32、边界原 dtype的工程落地。4.3 自身的确定性可复现性本节讨论的是自动融合自身的输出确定性不涉及与 Eager 模式的对比。自动融合的确定性可以分两层来看编译确定性相同的编译期输入 → 相同的 kernel 二进制。编译期输入包括模型结构、shape、dtype、自动融合的版本与编译选项。执行确定性相同的 kernel 二进制 相同的运行期输入 → 相同的输出。运行期输入包括张量数值、芯片型号等。两层叠加即可得到端到端的保证编译期输入与运行期输入都不变时多次运行的输出在二进制上完全一致。也就是说前文列举的差异来源升精度策略、指令选择、切块/累加顺序、算法实现在自动融合自身的两次编译运行之间是完全一致的——它们只在自动融合 vs 手写算子之间产生分歧。自动融合本身不引入额外的不确定性。仓库中的配套测试计划自动融合精度测试报告 中的任务 A正是针对这一论点设计同一进程内连续执行 1000 次输出 MD5 集合大小应为 1清空 JIT 缓存冷启动后 kernel hash 与输出 MD5 均应保持不变。五、PyTorch Inductor 的相关说明业界共识PyTorch Inductor 社区不保证精度的二进制一致其逻辑与自动融合一致。以下是文档摘录的三个权威来源5.1 PyTorch 文档《Numerical accuracy》涉及浮点结合律与 bit-exact原文PyTorch is not guaranteed to produce bitwise identical results for floating point computations that are mathematically identical.floating point addition and multiplication are not associative, so the order of the operations affects the results.5.2 Edward Yang《Ways to use torch.compile》2024作者为 PyTorch 核心开发者文章在 Improve training efficiency on a small-medium scale 一节中说明torch.compile与 eager 的数值关系Unfortunately, the compiler does not guarantee exact bitwise equivalence with eager code; we reserve the right to do things like select different matrix multiply algorithms with different numerics or eliminate unnecessary downcast/upcasts when fusing half precision compute together.5.3 PyTorch 文档《torch.compile Troubleshooting》Accuracy Debugging 一节中关于下游编译器数值表现的说明the reason we need this is downstream compilers will codegen code whether its Triton code or the C backend, the numerics from those downstream compilers can be different in subtle ways yet have dramatic impact on your training stability.这组引用说明一个行业级事实编译器不保证与 eager 二进制一致是 PyTorch 官方及核心开发者公开声明的设计立场与 graph-autofusion 的精度一致性结论同源同逻辑。六、对照表Eager vs 自动融合维度Eager 手写算子自动融合源代码人工固定JIT 生成二进制一致性基准不保证计算类/ 可一致纯搬运类中间计算精度受限于算子间 dtype常为 fp16融合块内部 fp32累积误差上界基准≤ Eager 基准性能基准显著优于基准七、使用建议希望继续使用自动融合时的处理路径若用户关注的是数值正确性模型精度、收敛性、下游业务指标自动融合在精度上无劣势、在性能上有显著收益可直接启用无需额外处理。若用户有与 Eager 严格对齐的诉求又希望继续使用自动融合的性能收益是十分有挑战的可参考以下策略组合使用。7.1 PyTorch-Inductor 场景状态和使用建议与 Eager 模式严格对齐涉及 Inductor 融合前的图处理与融合阶段两部分。Inductor 对多数相关流程未提供原生关闭开关需通过 patch 或修改源码实现。融合前处理Inductor 在自动融合前会执行以下可能引入数值差异的流程Inplace 算子函数化例如relu_→relu函数化后的 Kernel 实现与 Eager 可能存在差异。Decompose将复杂算子分解为基础算子以扩大融合范围与单算子实现相比算法有差异。FX 图 Pass主要为 pattern 替换类融合 Pass涉及 Kernel 替换。前反向切图基于自动重计算的切图策略重计算的融合 Kernel 与 Eager 模式可能存在差异。建议措施Inplace 算子函数化为融合前提、非功能项无法关闭。实践中建议仅实现 Inplace 版本通过自动函数化减少 Kernel 差异带来的数值差异。DecomposeInductor 原生不提供关闭选项需通过 patch 或修改源码实现。FX 图 PassInductor 不提供全部关闭的选项且每个版本生效的 Pass 可能不同需根据执行时实际情况依次关闭。前反向切图Inductor 使用最大流最小割算法切图会引入重计算可通过配置custom_partitioner_fn替换为无重计算的切图策略。自动融合Inductor 融合阶段因重新生成执行 Kernel 引入数值差异涉及的融合类型如下融合类型影响二进制精度数值差异来源用户可配置Reduction 类是累加顺序不同需 patch 或修改源码Pointwise 类是类型提升规则、指令映射、迭代算法差异需 patch 或修改源码MM/FA 模板类是算法实现差异需 patch 或修改源码使用须知如追求与 Eager 严格对齐需关闭上述所有融合类型。Inductor 未提供相应配置项需通过 patch 或修改源码实现。建议显式开启TORCHINDUCTOR_EMULATE_PRECISION_CASTS避免 Lowering 过程中的 Cast 消除优化引入数值误差。PyTorch 场景的其他调测环境变量如TORCH_COMPILE_DEBUG、TORCHINDUCTOR_FORCE_DISABLE_CACHES可参见 环境变量参考。7.2 TensorFlow 场景状态和使用建议通用优化TensorFlow 场景默认使用 GE 作为图编译器后端。GE 自带的融合 pass 与硬件无关优化 pass例如常量折叠、等价公式变换都会导致 kernel 源代码变化使二进制精度一致无法保障。建议措施将图编译优化等级设为 O0仅保留功能类优化。O0 等级下仍会执行功能相关与静态 shape 相关优化。可打开 DUMP 图开关观察 GE 中实际生效的 pass对照前文 2.2 节判断对精度的影响若有影响可通过手工调整脚本规避相应优化难度较高。自动融合自动融合目前支持以下类别算子的融合默认策略与可控性如下融合类型默认状态影响二进制精度用户可配置elemwise含 broadcast开启是否reduce关闭是是concat关闭否是slice关闭否是gather关闭否是transpose关闭否是默认关闭的融合可通过环境变量显式开启以 concat 为例export AUTOFUSE_FLAGS--enable_autofusetrue;--autofuse_enable_passconcat多个扩展融合可用英文逗号组合例如同时开启 reduce 与 concatexport AUTOFUSE_FLAGS--enable_autofusetrue;--autofuse_enable_passreduce,concat仓库中的 TensorFlow 融合样例 af_tf_eleandreduce/README.md 完整演示了这一流程Reduce 融合默认不使能需要先设置--autofuse_enable_passreduce再运行脚本融合生效时可在 Profiling 的op_summary_*.csv中观察到autofuse_reduce_前缀的融合 Kernel其中包含 Abs 与 ReduceSum 计算且不再出现对应的独立Abs、ReduceSumKernel。使用须知elemwise 融合暂不支持关闭。该类融合既是二进制精度差异的主要来源也是融合收益的主要来源关闭后整体收益将显著降低。如确有关闭需求可提交 issue 反馈。从配置实现看auto_fuse_config.h 等源码中--autofuse_enable_pass目前仅支持扩展融合类型基础 elemwise 融合由--enable_autofuse整体控制两者在开关粒度上不对称印证了基础融合不可单独关闭的约束。默认关闭的融合属于实验特性开启后可能出现功能异常或性能劣化建议在目标网络上实测后再决定是否保留。八、附配套测试计划与回归体系仓库为上述四类核心论点提供了可执行的验证框架详见 自动融合精度测试报告其任务拆解与本文论点的对应关系为任务 S共享基础设施提供bit_equal、ulp_diff、error_stats、fp64_reference等数值比较工具覆盖 fp16 / bf16 / fp32 及 inf/nan 场景是其余任务的公共底座任务 A验证 §4.3 自动融合自身的二进制确定性1000 次运行 hash 唯一任务 C1/C2/C3分别验证 §2.2 中的指令选择差异、Reduction 累加顺序差异、rsqrt 等迭代算法差异任务 D验证 §3 纯搬运类算子的二进制一致性transpose / reshape / slice / concat / split / broadcast 组合子图任务 B/E验证 §4.2累积误差不差于 Eager的定量结论以及端到端训练指标对齐loss / grad norm / PPL 曲线。结合源码层的 test_improve_precision.cpp ST 用例读者可以完整构建文档结论 → 源码实现 → 自动化测试三层证据链既理解为什么也掌握怎么验证。总结自动融合与 Eager 手写算子的二进制不一致是原理层面的必然——代码生成路径不同、升精度策略不同、指令选择不同、累加顺序与算法实现不同任何一环都足以造成末位差异但自动融合通过块内强制 fp32 累积、只在融合块边界截断的保守精度策略保证精度不差于 Eager同时保持自身编译与执行的双重确定性。对于确有严格对齐诉求的场景PyTorch Inductor 侧需通过 patch 或修改源码关闭 Reduction / Pointwise / MM/FA 三类融合并显式开启TORCHINDUCTOR_EMULATE_PRECISION_CASTSTensorFlow 侧则需将 GE 图编译优化等级设为 O0、按需通过AUTOFUSE_FLAGS控制扩展融合elemwise 基础融合不可关闭。在绝大多数关注模型精度、收敛性与下游业务指标的场景下直接启用自动融合即可获得无精度劣势的显著性能收益。【免费下载链接】graph-autofusionGraph-autofusion 是一个面向昇腾Ascend芯片的轻量级、解耦式组件集合旨在通过自动融合技术加速模型执行。 目前已开源 SuperKernel 组件和 Autofuse 组件未来将持续开放更多自动融合相关模块。项目地址: https://gitcode.com/cann/graph-autofusion创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考