☰
TorchAir 冗余算子消除(remove_noop_ops):图模式下自动裁剪无意义计算的实战指南
2026/10/14 2:04:23 网站建设 项目流程
  • 人工智能
  • 模型编译
  • Ascend

【免费下载链接】torchair

TorchAir 支持用户基于PyTorch框架和torch_npu插件在昇腾NPU上使用图模式进行推理。

项目地址:https://gitcode.com/Ascend/torchair
点击查看免费下载

图模式(Graph Mode)编译过程中,模型中常残留一些不影响计算结果的"空转"算子,例如对整个张量做完整切片、重复次数全为 1 的 repeat、前后类型一致的 dtype 转换等。TorchAir 通过experimental_config.remove_noop_ops集成了 PyTorch 的冗余算子消除能力,在 FX 图优化阶段自动识别并剔除这类冗余节点,从而减少不必要的计算与调度开销。本文将围绕该功能的适用算子场景、使用方法、源码实现链路与测试验证展开,帮助你在昇腾 NPU 图模式推理场景中正确开启、验证并规避消除失效的边界情况。

功能简介

TorchAir 的冗余算子消除功能针对图模式(Ascend IR / GE 图)编译场景设计,能够在 FX 图生成后、送入图编译器之前,自动识别并消除计算图中不影响程序逻辑或数据计算的冗余操作。这类操作不改变任何数据内容,属于典型的"空操作",保留在图中只会增加算子调度、内存分配与 Kernel 执行的开销。

典型冗余操作示例如下:

  • 无实际意义的张量视图操作,例如b = tensor_a[:](对整张量做完整切片);
  • 参数无效的特殊算子,例如重复次数为 1 的repeat操作。

该功能与 TorchAir 其他图优化策略(如 pattern_fusion_pass 图算子融合)相互独立、可叠加使用,用户可通过开启/关闭组合进行优化对比,从而为模型选择最佳编译方案。

支持的冗余算子消除场景

本功能依赖 PyTorch 2.2.0 或更高版本(对应 TorchAir 在 npu_fx_compiler.py 中对torch._inductor.fx_passes.post_grad.remove_noop_ops的按版本导入逻辑),不同 PyTorch 版本支持的优化场景可能存在差异。以 PyTorch 2.5.1 为例,支持优化的算子包括但不限于下表(各算子的详细语义请参见 PyTorch 源码):

| 算子名 | 冗余操作场景示例 | | -- | -- | | aten.slice | 对整个张量进行完整切片操作,如tensor_a[:]。 | | aten.slice_scatter | 对整个张量进行完整切片操作,如tensor_a.slice_scatter(tensor_b)。 | | aten.repeat | 张量在所有待重复维度上重复的次数为 1,如tensor_a.repeat(1)。 | | aten.constant_pad_nd | 张量在所有待扩充维度上扩充的数量为 0,如torch.nn.functional.pad(tensor_a, pad=[0, 0, 0, 0], value=3.5)。 | | torch.ops.prims.convert_element_type | 张量数据类型转换时,转换前后类型一致。 | | torch.ops.prims.device_put | 张量设备类型转换时,转换前后一致。 | | aten.ceil、aten.floor、aten.round、aten.trunc | 张量数据类型为整型(取整类算子对整型数据无实际效果)。 | | aten.pow | 张量指数运算时幂为 1。 | | aten.cat | 张量拼接时,参与拼接的张量只有自身,如torch.cat([tensor_a])。 | | aten.view.default、aten.view.dtype | — | | aten.copy、aten.alias、aten.clone | — |

从仓库测试用例可以印证上述场景:在 tests/st/torchair_fx_pattern_st.py 中,分别针对x[:]完整切片、x.slice_scatter(y)完整切片写入、x.repeat(1, 1)、func.pad(x, pad=[0, 0, 0, 0], value=3.5)、torch.ops.prims.convert_element_type(x, torch.float32)、torch.ceil(x)、torch.pow(x, 1)、torch.cat([x], dim=0)、x.clone()编写了独立的消除验证用例,均断言优化后图中对应算子节点不再存在。同样的场景在 ACL Graph 后端也有对应覆盖(见 tests/st/torchair_aclgraph_st.py)。

不进行冗余消除的场景

冗余消除并非无条件执行。当算子的输入/输出 Shape 不一致,或优化后在输入/输出之间引入了新的别名关系时,不会进行冗余消除操作。这是因为消除这类节点会改变数据流语义(Shape 变化)或破坏张量别名约束,可能导致后续算子行为异常。

典型例证是 clone 算子的"选择性消除":在 tests/st/torchair_fx_pattern_st.py 中,当x.clone()的结果被直接作为图输出(test_enable_remove_noop_ops_clone_skipped)时,clone 节点被保留;而当 clone 结果参与后续add_(就地加)等运算(test_enable_remove_noop_ops_clone_inplace)时,clone 节点才被消除。这正是"不引入新别名关系"约束的实际体现——直接输出的 clone 承载了独立存储语义,不可轻易移除。

使用方法

该功能通过 torchair.get_npu_backend 的compiler_config配置开启,配置项位于config.experimental_config下。示例代码如下(仅供参考,不支持直接拷贝运行):

import torch_npu import torchair config = torchair.CompilerConfig() # 冗余算子消除配置 config.experimental_config.remove_noop_ops = True npu_backend = torchair.get_npu_backend(compiler_config=config) opt_model = torch.compile(model, backend=npu_backend)

表 1参数说明

| 参数名 | 说明 | | -- | -- | | remove_noop_ops | 图模式中是否开启冗余算子消除,布尔类型。False:关闭。True(默认值):开启。 |

参数默认值与解析实现

从源码看,该参数默认即为开启状态。experimental_config.py 中定义:

self.remove_noop_ops = OptionValue(True, [True, False])

OptionValue(True, [True, False])表示默认值为True,合法取值集合为{True, False}。当配置非法取值时,TorchAir 的配置校验机制会给出提示,帮助用户规避拼写或取值错误。此外,该配置项会随as_dict()输出进入编译选项字典(local_experiment_option["remove_noop_ops"] = self.remove_noop_ops.value,见 experimental_config.py),作为图编译选项下发给后端。

在 npugraph_ex 后端(ACL Graph 图模式)中,对应的配置入口为torch.compile(model, backend="npugraph_ex", options={"remove_noop_ops": True}),默认值同样为开启(True),相关解析见 npugraphex_config.py 与 npugraph_ex/npugraph_ex/npu_fx_compiler.py。

底层实现:FX 图优化阶段的调用链路

冗余算子消除并非在 GE 图侧实现,而是在Python 层 FX 图优化阶段提前完成。其核心调用链路位于 npu_fx_compiler.py:

  1. 按版本导入优化函数:文件头部尝试from torch._inductor.fx_passes.post_grad import remove_noop_ops,若当前 PyTorch 版本低于 2.2.0(或环境中缺少该模块),则置为None并降级处理:

    try: from torch._inductor.fx_passes.post_grad import remove_noop_ops except ImportError: remove_noop_ops = None
  2. 图优化入口调度:在_optimize_fx中,当config.experimental_config.remove_noop_ops为 True 时,通过observer.apply_gm_pass(_optimize_noop_ops, "optimize_noop_ops")注册该 Pass。该机制会在 Pass 执行前后自动记录图状态,并与 debug_save 图调试信息保存 等调试能力联动:

    if config.experimental_config.remove_noop_ops: observer.apply_gm_pass(_optimize_noop_ops, "optimize_noop_ops")
  3. 冗余消除执行与版本降级告警:_optimize_noop_ops检查导入结果,若为None则打印告警日志并跳过(避免低版本 PyTorch 直接报错崩溃),否则对 FX GraphModule 的graph执行原生消除逻辑:

    if remove_noop_ops is None: logger.warning( "Skip removing noop ops; check if your PyTorch version is above 2.2.0 and ensure" " the module torch._inductor.fx_passes.post_grad.remove_noop_ops exists" ) else: remove_noop_ops(graph_module.graph)

值得说明的是,该 Pass 在_optimize_fx的 Pass 序列中位于自定义 pre-pass(post_grad_custom_pre_pass)之后、recover_view_inplace_pattern与pattern_fusion_pass之前,因此它处理的是已经过用户自定义优化、尚未做视图就地化恢复的 FX 图,消除结果会继续参与后续融合与视图优化流程。

日志验证:确认冗余算子已被消除

设置成功后,参考 TorchAir Python层日志打印 开启 Debug 日志(from torchair import logger; logger.setLevel(logging.DEBUG)),即可在日志中看到消除前后的图对比。消除冗余算子后的日志类似如下:

[DEBUG] TORCHAIR(1675418,python3):2025-10-31 16:13:28.281.364 [npu_fx_compiler.py:297]1675418 After removing noop ops, graph is graph(): %arg0_1 : [num_users=0] = placeholder[target=arg0_1] %arg1_1 : [num_users=1] = placeholder[target=arg1_1] %arg2_1 : [num_users=1] = placeholder[target=arg2_1] %add_3 : [num_users=1] = call_functiontarget=torch.ops.aten.add.Tensor, kwargs = {}) return (add_3,)

从日志可见,原本可能存在的slice等冗余节点已被移除,%arg0_1的num_users降为 0(成为死输入),图中仅保留真正参与计算的aten.add.Tensor节点。若关闭该功能,同样的模型图中会保留这些冗余节点,二者对比即可量化该优化带来的图规模缩减。

典型效果:完整切片消除示例

以"对整个张量进行完整切片"为例,直观展示优化前后差异。当不开启冗余消除(remove_noop_ops = False)时,FX 图中保留aten.slice.Tensor节点:

graph(): %arg0_1 : [num_users=1] = placeholder[target=arg0_1] %arg1_1 : [num_users=1] = placeholder[target=arg1_1] %slice_1 : [num_users=1] = call_functiontarget=torch.ops.aten.slice.Tensor, kwargs = {}) %add : [num_users=1] = call_functiontarget=torch.ops.aten.add.Tensor, kwargs = {}) return (add,)

开启后,slice_1节点被消除,add直接消费%arg0_1:

graph(): %arg0_1 : [num_users=1] = placeholder[target=arg0_1] %arg1_1 : [num_users=1] = placeholder[target=arg1_1] %add : [num_users=1] = call_functiontarget=torch.ops.aten.add.Tensor, kwargs = {}) return (add,)

可见冗余aten.slice操作被完全消除,图中算子节点数量减少,编译产物更精简。

测试验证与质量保障

TorchAir 仓库为该功能提供了较为完整的自动化测试覆盖,可作为功能行为的事实依据:

  • tests/st/torchair_fx_pattern_st.py(Ascend IR 后端):包含test_disable_remove_noop_ops(关闭时 slice 节点保留)、以及 slice、slice_scatter、repeat、pad、convert_element_type、ceil、pow、cat、clone 等算子开启后的消除断言;所有用例均以@unittest.skipIf(torch.__version__ < '2.2.0', "")标记,进一步印证"依赖 PyTorch 2.2.0+"的版本约束;
  • tests/st/torchair_aclgraph_st.py(ACL Graph 后端):覆盖x[:]完整切片消除、zeros_like + copy_组合下的冗余消除与死代码消除联动、以及关闭开关时冗余节点保留的对照用例;
  • tests/st/torchair_debug_log_st.py:在开启 Debug 日志、图 dump(pbtxt)以及自定义 pre/post pass 组合场景下验证功能可正常参与编译流程;
  • 冒烟测试 tests/smoke/ge_test.py、tests/smoke/aclgraph_test.py 中则以remove_noop_ops = False关闭该优化,用于隔离验证其他图优化 Pass 的行为,从侧面说明该功能默认开启、且可在测试中按需关闭。

小结与建议

冗余算子消除是 TorchAir 图模式编译流水线中默认开启的一项基础优化,对包含大量视图、类型转换、拼接等"空转"算子的模型收益明显。实际使用建议如下:

  • 保持默认开启(True);仅当需要与其他优化策略做对比实验、或排查疑似由优化引发的异常时,才显式关闭;
  • 确认 PyTorch 版本不低于 2.2.0,否则功能自动降级跳过并打印 warning 日志;
  • 结合 Debug 日志或 graph_dump 图转储功能 对比消除前后的 FX 图,确认冗余节点确被裁剪;
  • 注意 Shape 不一致或引入新别名关系的场景不会触发消除,属预期行为而非功能异常。
  • 人工智能
  • 模型编译
  • Ascend

【免费下载链接】torchair

TorchAir 支持用户基于PyTorch框架和torch_npu插件在昇腾NPU上使用图模式进行推理。

项目地址:https://gitcode.com/Ascend/torchair
点击查看免费下载
上一篇:洛雪音乐音源完整指南:免费解锁全网音乐资源的终极方案
下一篇:终极指南:GoCD数据迁移后功能验证的完整测试计划与实用案例

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询