已关闭
[RFC]: 删除 NPU Sanitizer 中不可达的 Autograd Dispatch Key 兼容 Patch #3669
No_neck创建于 8月5日关闭于 8月7日
8月5日 添加了label:rfc
8月5日 添加了label:bot-triaged
TorchNPU-Bot
8月5日 评论:
8月5日 评论:
检测到当前 issue 已关联 PR,自动添加标签:bot-triaged


8月7日 关闭了 issue
8月7日 添加了label:resolved
检测到当前 issue 已关联 PR,自动添加标签:bot-triaged


1. 概述
1.1 简介
本文提议删除
torch_npu/npu/_stream_check.py中一段 NPU Sanitizer 私有的 Autograd Dispatch Key 兼容逻辑。该逻辑在 2024 年首次引入 NPU Sanitizer 时加入,通过五个公开算子名称组成的白名单,调用 PyTorch 私有接口修改当前线程的 Dispatch TLS 状态。待删除代码包括:
self.npu_adjust_autograd白名单;enable_autograd()方法;aten_api = func.__name__.split(".")[0]临时变量;self.enable_autograd(aten_api)调用;test/npu/test_stream_check.py中仅验证上述旧实现的测试和字段断言。本提议不删除 NPU Sanitizer,不改变其跨 Stream 数据竞争、Event 同步、内存生命周期和
record_stream检测能力。变更的目标仅是消除当前 master / PyTorch 2.13 组合中真实算子路径无法命中的历史兼容 Patch。1.2 背景与动机
NPU Sanitizer 是运行时检查工具。Stream Check 模式通过
TorchDispatchMode拦截 ATen 算子,收集算子输入、输出对应的内存地址以及当前 NPU Stream,并结合 Event、Stream、内存分配释放和record_stream回调,检查潜在的跨 Stream 未同步访问。历史兼容代码如下:
self.npu_adjust_autograd = [ "adaptive_avg_pool2d", "batch_norm", "log_softmax", "nll_loss", "to" ] def enable_autograd(self, aten_api): if aten_api in self.npu_adjust_autograd: torch._C._dispatch_tls_set_dispatch_key_excluded( torch._C.DispatchKey.AutogradFunctionality, False, )该调用中
False的准确含义不是“排除 Autograd”,而是将AutogradFunctionality从当前线程 TLS 的excluded集合中移除。对 NPU Tensor 而言,其主要效果是允许AutogradPrivateUse1再次参与算子分发。这段代码存在以下维护问题:
torch._C._dispatch_tls_set_dispatch_key_excluded私有接口,跨 PyTorch 版本不保证稳定。TorchDispatchMode的是内部 ATen 名称,名称无法精确匹配。1.3 历史追溯
该兼容逻辑由以下提交引入:
a6e615ee59b3018457cb34e2f0d433c90eae1b4dEnable NPU Sanitizer从引入至本次分析时,相关白名单和
enable_autograd()代码未发生实质修改。PyTorch 官方 CUDA Sanitizer 中也不存在相同的算子白名单或 TLS setter 调用,因此该逻辑属于 torch_npu 下游私有兼容 Patch,而不是需要持续与 PyTorch CUDA Sanitizer 同步的通用实现。1.4 目标
record_stream官方测试无回退。1.5 非目标
_kernel_check.py。Tensor.is_cuda等其他兼容逻辑。record_stream的判定规则。2. 用例分析
2.1 Sanitizer 的用户入口和调用链
用户可以显式调用
enable_npu_sanitizer()启用 Sanitizer;当进程环境中存在TORCH_NPU_SANITIZER时,torch_npu 的可选功能初始化也会导入 Sanitizer 模块并调用该入口。Stream Check 模式下,核心调用链如下:历史兼容 Patch 位于
__torch_dispatch__()的输入解析之前:2.2 类和函数职责
NPUSanitizerNPUSanitizer.enable()NPUSanitizer.enable_stream_check()NPUSanitizerDispatchModeTorchDispatchMode拦截 NPU ATen 算子__torch_dispatch__()enable_autograd()__torch_dispatch__()时AutogradFunctionalitycheck_errors()2.3 五类历史白名单用例
PyTorch 2.13 环境中,公开 API 与实际进入 DispatchMode 的内部 ATen 名称不一致:
enable_autograd()adaptive_avg_pool2d_adaptive_avg_pool2d、_adaptive_avg_pool2d_backwardbatch_normnative_batch_norm、native_batch_norm_backwardlog_softmax_log_softmax、_log_softmax_backward_datanll_lossnll_loss_forward、nll_loss_backwardto_to_copy必须区分“进入函数”和“执行真分支”:
enable_autograd(),因此覆盖了待删函数和调用点。enable_autograd()传入五个历史名称时,均观察到before_excluded=True、after_excluded=False,证明旧代码本身具有 TLS 副作用,只是该副作用在当前真实路径不可达。2.4 用户可见行为要求
删除后,用户启用 NPU Sanitizer 执行上述算子时,应满足:
requires_grad和grad_fn;backward()成功;running_mean、running_var等状态一致;3. 方案设计
3.1 总体方案
采用“完整删除不可达兼容逻辑,不增加替代分支”的方案:
NPUSanitizerDispatchMode.__init__()删除npu_adjust_autograd白名单。enable_autograd()方法。__torch_dispatch__()删除aten_api临时变量和方法调用。test/npu/test_stream_check.py删除仅验证旧白名单、旧方法和旧字段的测试内容。3.2 删除前后代码
删除前:
class NPUSanitizerDispatchMode(TorchDispatchMode): def __init__(self, event_handler: csan.EventHandler): super().__init__() self.event_handler = event_handler self.args_handler = None self.npu_adjust_autograd = [ "adaptive_avg_pool2d", "batch_norm", "log_softmax", "nll_loss", "to" ] def enable_autograd(self, aten_api): if aten_api in self.npu_adjust_autograd: torch._C._dispatch_tls_set_dispatch_key_excluded( torch._C.DispatchKey.AutogradFunctionality, False, ) def __torch_dispatch__(self, func, types, args=(), kwargs=None): # ... self.args_handler = NPUArgumentHandler() aten_api = func.__name__.split(".")[0] self.enable_autograd(aten_api) self.parse_inputs(func._schema, args, kwargs, is_factory=is_factory) outputs = func(*args, **kwargs) # ...删除后:
class NPUSanitizerDispatchMode(TorchDispatchMode): def __init__(self, event_handler: csan.EventHandler): super().__init__() self.event_handler = event_handler self.args_handler = None def __torch_dispatch__(self, func, types, args=(), kwargs=None): # ... self.args_handler = NPUArgumentHandler() self.parse_inputs(func._schema, args, kwargs, is_factory=is_factory) outputs = func(*args, **kwargs) # ...对应最小差异:
class NPUSanitizerDispatchMode(TorchDispatchMode): def __init__(self, event_handler: csan.EventHandler): super().__init__() self.event_handler = event_handler self.args_handler = None - self.npu_adjust_autograd = [ - "adaptive_avg_pool2d", "batch_norm", - "log_softmax", "nll_loss", "to" - ] - - def enable_autograd(self, aten_api): - if aten_api in self.npu_adjust_autograd: - torch._C._dispatch_tls_set_dispatch_key_excluded( - torch._C.DispatchKey.AutogradFunctionality, - False, - ) def __torch_dispatch__(self, func, types, args=(), kwargs=None): # ... self.args_handler = NPUArgumentHandler() - aten_api = func.__name__.split(".")[0] - self.enable_autograd(aten_api) self.parse_inputs(func._schema, args, kwargs, is_factory=is_factory)3.3 方案选型
3.4 技术依据
3.4.1 TLS setter 的语义
PyTorch Dispatcher 的当前线程状态包含 included 和 excluded DispatchKeySet。算子最终可参与选择的 Key 可简化理解为:
历史代码将
AutogradFunctionality的 excluded 状态设置为False,即从排除集合中移除该功能键。该操作会改变当前线程后续 Dispatch 行为,因此只有在存在明确的实际故障和版本边界时才应保留。3.4.2 当前真实路径不可达
历史实现用
func.__name__.split(".")[0]得到内部算子名,再与公开 API 名称白名单精确比较。在 PyTorch 2.13 中,五类用例进入 Mode 的名称为_adaptive_avg_pool2d、native_batch_norm、_log_softmax、nll_loss_forward、_to_copy等,均不等于白名单项。因此当前行为实际等价于:
def enable_autograd(aten_api): return None删除该调用不会改变当前真实路径的 TLS 状态。
3.4.3 现有 Autograd 能力
当前 PyTorch / torch_npu 组合已通过正常 Dispatcher、代码生成的 Autograd wrapper、
AutogradPrivateUse1注册及对应 fallback 处理 NPU Autograd。五类算子在删除兼容代码后仍能建立计算图并完成反向传播,消融结果与该机制一致。3.5 功能、性能和 DFX 设计
3.5.1 功能兼容性
enable_npu_sanitizer()的调用方式。3.5.2 性能
变更删除了每个普通 Dispatch 中的一次
func.__name__字符串切分、一次 Python 方法调用和一次五项列表成员判断。预期不会产生性能回退;本 RFC 不宣称可观的整体性能收益。3.5.3 可靠性
3.5.4 可维护性
3.5.5 可测试性
3.5.6 安全与隐私
本变更不新增网络访问、文件写入、遥测、日志字段或用户数据处理,不涉及新增安全和隐私风险。
3.6 影响文件
RFC 设计范围仅包含:
torch_npu/npu/_stream_check.pyenable_autograd()、临时变量和调用点test/npu/test_stream_check.py异常日志格式、其他 import 清理、代码风格调整和其他 Sanitizer 模块均不属于本 RFC,若确有必要应另行提交,避免扩大 Patch 削减 PR 的审查范围。
3.7 接口与资料设计
4. 测试设计
4.1 验证环境
2.13.0a0+gitfad742419cd51d0eb647f9bdcc3d1624de20a9481ab8bc22.13.0+git19cd51d两个 wheel 从同一提交构建,分别安装到独立虚拟环境,并使用相同 PyTorch、设备和随机种子执行用例。
4.2 直接分支探针
目的:证明历史代码本身可以修改 TLS,而当前真实路径只是无法命中白名单,避免将“不可达”误写为“setter 失效”。
步骤:
AutogradFunctionality的 excluded 状态设置为True。enable_autograd()直接传入五个历史公开名称。结果:五项均为
before_excluded=True、after_excluded=False,符合预期。4.3 五类真实算子消融测试
adaptive_avg_pool2dgrad_fn、输入梯度batch_normlog_softmaxgrad_fn、输入梯度nll_losstograd_fn、梯度回传每个输出、梯度和状态 Tensor 检查:
总比较结果:
额外一致性结果:
state_change_count全部为 0;4.4 torch_npu 官方 Sanitizer 单元测试
test/npu/test_stream_check.pytest/npu/test_sanitizer.pytest/npu/test_sanitizer_record_stream.pyrecord_stream检测能力test/test_npu_sanitizer.pytest/test_sanitizer_pluggable_allocator.py基线共执行 38 个官方测试,删除版本共执行 37 个官方测试。数量差 1 是删除了直接调用旧
enable_autograd()的实现绑定型测试,其余官方 Sanitizer 测试均通过。4.5 PyTorch 社区参考用例
PyTorch 社区的
test/test_cuda_sanitizer.py用于参考上游 Sanitizer 的测试组织和行为关注点。当前实验使用 CPU PyTorch 构建,torch.cuda.is_available() == False,该文件运行 0 个测试并记录为SKIP_CUDA_UNAVAILABLE。因此,该项只作为社区实现参考,不作为本次 NPU Patch 删除成功的证据。删除结论以 NPU 实机消融和 torch_npu 官方 Sanitizer 测试为准。
4.6 验收标准
只有同时满足以下条件才允许合入:
enable_autograd()和调用点。git diff --check、代码检查和相关 CI 通过。4.7 测试产物
ablation/test_dispatch_key_reduction_213.pyablation/run_master_213_focused_ablation.shablation/remove_autograd_workaround.patchablation/update_stream_check_tests_after_removal.patchablation/npu_sanitizer_dispatch_key_reduction_report_pt213_master.mdresults/master_213_focused_20260730_013025/results/master_213_official_sanitizer_tests_20260730/summary_final.tsv/home/w50063847/sanitizer_master_213_focused_run3/构建产物校验值:
5. 缺点和风险
5.1 回滚方案
若合入后发现与本删除直接相关的回归:
AutogradFunctionality被排除,而不是其他 Dispatcher、kernel 或 Sanitizer 问题。6. 现有技术
6.1 PyTorch
TorchDispatchModePyTorch 使用
TorchDispatchMode在 Python 层拦截 ATen 算子。Mode 内部再次调用func(*args, **kwargs)时,Dispatcher 会结合 Tensor Dispatch Keys 与当前线程 TLS included/excluded 状态选择后续 kernel。本 RFC 不改变该机制,仅删除 torch_npu 在 Mode 中额外修改 Autograd TLS 的下游逻辑。
6.2 PyTorch CUDA Sanitizer
PyTorch 官方 CUDA Sanitizer 同样在 Python 层解析算子输入和输出,并结合 Stream、Event 与内存生命周期信息进行检查。官方实现未维护本 RFC 所述五项 Autograd 白名单,也未在 Sanitizer DispatchMode 中调用
_dispatch_tls_set_dispatch_key_excluded(AutogradFunctionality, False)。这说明历史逻辑不是 Sanitizer 通用设计要求;是否保留应由 NPU 当前真实故障和验证结果决定。
6.3 torch_npu 当前实现
torch_npu 通过 PrivateUse1 / AutogradPrivateUse1 机制接入 PyTorch Dispatcher 和 Autograd。当前 master / PyTorch 2.13 组合中,五类历史算子无需 Sanitizer 内部手工修改 TLS 即可完成前向和反向。
7. 未解决问题
待社区讨论/决策的开放问题,如硬件适配范围、参数默认值等(需在RFC通过前解决)。
附录
欢迎加入社区,感谢您对社区的贡献 🎉!