​作者​:昇腾实战派
​知识地图​:https://blog.csdn.net/Lumos_Lovegood/article/details/161601003

1. 背景

1.1 调用关系

TorchNPU API 在 Ascend NPU 上执行时,通常经过以下层次:

TorchNPU Python API
        ↓
ATen 算子或框架逻辑
        ↓
torch_npu / op-plugin
        ↓
ACLNN 算子接口
        ↓
NPU

1.2 映射差异来源

TorchNPU API 与 ACLNN 算子并非固定的一对一关系,主要原因包括:

  • 同一 API 存在 Tensor、Scalar、out、inplace 等多种重载;
  • 不同数据类型、数据格式或参数会触发不同的 dispatch 分支;
  • 部分 API 通过 TorchNPU Composite 机制分解为多个基础 API;
  • optimizer、callback、policy、autocast 和 export 包含框架逻辑或用户代码;
  • view 类 API 仅修改张量元数据,不执行 ACLNN 算子;
  • 分布式 API 可能调用通信库,而非 ACLNN 算子;
  • 不支持的 API 可能回退到 CPU 执行。

1.3 分析目标

映射分析需要确定目标 API 在各个可触发分支中实际调用的 ACLNN 算子,并区分以下状态:源码确认、运行时验证、无法触发、环境不支持。映射结论与 TorchNPU、torch_npu、CANN 及设备版本相关。

2. 分析原理

2.1 以 NPU 适配层为准

ACLNN 算子名称由 torch_npu 和 op-plugin 的 NPU 后端实现决定。TorchNPU 源码用于确认 ATen dispatch、Composite 分解和 API 转调关系,但不能直接替代 NPU 后端源码。

源码检查顺序:

  1. torch_npu 和 op-plugin;
  2. opapi、手写适配和 aclops 实现;
  3. TorchNPU ATen dispatch、原生实现和算子分解;
  4. NPU Profiler 运行结果。

2.2 递归到底层 ACLNN 算子

以下内容属于调用链中间层,不作为最终映射:

  • CompositeImplicitAutograd;
  • CompositeExplicitAutograd;
  • 转调到另一个 TorchNPU API;
  • 调用 callback 或 policy;
  • 返回可执行图或其他产物。

分析需要继续展开,直到得到实际执行的 ACLNN 算子,或确认该路径没有 ACLNN 算子调用。

2.3 按具体分支分析

源码中出现多个 ACLNN 算子名称并不等同于组合算子。需要确认这些算子是在同一次调用中共同执行,还是由不同条件互斥选择。

例如:

aclnnAdd|aclnnAdds

表示两个互斥的单算子分支。

aclnnMm&aclnnRelu

表示同一分支中依次执行两个 ACLNN 算子。

2.4 区分源码证据和运行时证据

源码用于确定可触发的候选分支,Profiler 用于确认当前输入实际进入的分支。两类证据应分别记录。源码存在某个分支时,还需要确认目标 API 是否能够触发该分支。被转调 API 支持某种数据格式,不代表目标 API 也允许该数据格式。

3. 分析流程

3.1 规范化 API 名称

确认目标 API 的命名空间和重载。

API 类型检查内容
torch.xxx对应的 ATen function
Tensor.xxxTensor、Scalar 和 out 重载
Tensor.xxx_inplace 重载
optimizer/module 方法Python 分支控制逻辑
torch_npu APINPU 自定义注册和实现
callback、policy、export外层逻辑、用户函数和返回产物
分布式 API通信语义,不与同名 Tensor API 合并

源码搜索使用规范化后的精确名称,模糊子串结果仅用于定位。

3.2 检查 NPU 后端实现

先检查 op_plugin_functions.yaml,再定位对应的 opapi 或手写 kernel。

常用源码入口:

op_plugin_functions.yaml
ops/opapi/
ops/aclops/
torch_npu/csrc/aten/ops/op_api/

需要搜索的调用形式:

EXEC_NPU_CMD
DO_COMPATIBILITY
GetOpApiFuncAddr
RunOpApiV2

检查内容:

  • 所有 ACLNN 算子调用;
  • Tensor、Scalar、out 和 inplace 分支;
  • 数据类型、形状、内存布局和数据格式条件;
  • CANN 或设备版本条件;
  • API 转调路径;
  • 动态加载的 ACLNN 算子调用。

同一文件中的其他重载不能直接并入目标 API。存在当前 opapi 实现时,不将 aclops 旧路径名称合并到同一映射中。YAML 中的 structured_inherit 需要递归解析。YAML 信息不完整时,以实际 kernel 源码为准。

3.3 检查 TorchNPU 分解路径

没有 NPU 后端实现时,检查:

aten/src/ATen/native/native_functions.yaml
torch/_decomp/
torch/_refs/
aten/src/ATen/native/

对分解或转调得到的每个子 API,重新执行 NPU 后端实现检查,直到得到底层 ACLNN 算子。如果路径最终仅包含 view、元数据或框架控制逻辑,则记录为无算子,不继续搜索同名 ACLNN 算子。

3.4 确认分支触发条件

对每个候选分支检查:

  • 目标 API 是否提供对应参数;
  • 数据类型和数据格式是否允许;
  • 上层逻辑是否提前执行检查或转换;
  • 当前运行库和设备是否支持;
  • 能否构造满足条件的输入。

处理规则:

状态处理方式
目标 API 可触发,运行时已触发写入源码、运行和最终映射
目标 API 可触发,当前未触发写入源码验证,备注未验证原因
目标 API 无法触发不写入目标 API 最终映射
目标 API 可触发,当前环境不支持保留源码验证,记录运行限制

3.5 设计运行时测试

测试条件由源码中的 dispatch 条件确定。常见维度包括:

  • Tensor 与 Scalar;
  • function、method、inplace 和 out;
  • 数据类型、形状和数据格式;
  • dim、keepdim、alpha、beta 等参数;
  • foreach、fused、capturable 等模式;
  • 首次调用、后续调用、export 和产物执行阶段。

仅对会改变 dispatch 的条件生成独立用例。

Profiler 用例要求:

  • 输入在 Profiler 外创建;
  • Profiler 内仅调用目标 API;
  • 调用后执行设备同步;
  • 从 key_averages() 的 event.key 收集全部 ACLNN 算子;
  • 不使用 exec();
  • 高风险用例使用独立进程。
activities = [torch.profiler.ProfilerActivity.CPU]
if hasattr(torch.profiler.ProfilerActivity, "NPU"):
    activities.append(torch.profiler.ProfilerActivity.NPU)

x = torch.randn(8, 8, device="npu")

with torch.profiler.profile(activities=activities) as prof:
    result = torch.abs(x)
    torch.npu.synchronize()

ops = []
for event in prof.key_averages():
    name = str(event.key)
    if "aclnn" in name.lower() and name not in ops:
        ops.append(name)

3.6 对比源码和运行结果

分别保留:

  1. 源码中目标 API 可触发的 ACLNN 算子;
  2. 当前环境实际捕获的 ACLNN 算子;
  3. 综合两者得到的最终映射。

运行时出现静态扫描未发现的 ACLNN 算子时,继续检查动态加载、包装逻辑和首次调用初始化。源码存在但运行时未触发时,继续检查输入条件和环境能力。

4. 结果说明

4.1 分类

具体分支中的底层 ACLNN 算子分类
0 个无算子,并按视图、框架、分布式或 CPU 回退细分
1 个单算子;API 转调后仍只有一个底层算子也按单算子处理
2 个及以上不同算子组合算子
多个算子互斥选择,一次只执行一个单算子多变体
NPU 专属 APINPU 自定义算子

4.2 ACLNN 算子表达式

算子名称字段仅使用 ACLNN 算子名称、&、| 和括号。

符号含义示例
&同一分支共同执行aclnnMm&aclnnRelu
|互斥分支或变体aclnnAdd|aclnnAdds
()标记组合分支边界(aclnnA&aclnnB)|(aclnnA&aclnnC)

分支条件和限制写入备注,不写入算子字段。

4.3 七列表格

列内容
API 名称完整 API 名称
是否单算子调用单算子、组合算子、NPU 自定义算子或无算子细分类别
aclnn 算子名称最终 ACLNN 算子表达式
分类计算、框架、分布式或自定义 API
源码验证目标 API 可触发的源码表达式;没有则 notfound
运行日志验证Profiler 实际捕获的表达式;没有则 NONE
备注分解关系、无法触发的分支、环境限制和差异

相关列需要同步更新:

  • 运行时发现辅助算子:更新最终映射、运行验证和备注;
  • 源码确认新分支:更新分类、最终映射、源码验证和备注;
  • 确认目标 API 无法触发分支:从最终映射和源码验证中移除,并记录原因。

4.4 示例:torch._addmm_activation

该 API 包含 addmm,随后根据 use_gelu 选择 ReLU 或 GELU。

运行结果:

use_gelu=False: aclnnAddmm&aclnnInplaceRelu
use_gelu=True:  aclnnAddmm&aclnnGelu

最终映射:

(aclnnAddmm&aclnnInplaceRelu)|(aclnnAddmm&aclnnGelu)

普通 addmm 支持 aclnnAddmmWeightNz,但 _addmm_activation 会拒绝对应内部格式,因此该分支不能由目标 API 触发。aclnnGeluV2 所需模式也不能由该 API 的 NPU 路径选择。

5. 补充说明

5.1 框架入口类 API

optimizer、callback、policy、autocast 和 export 分别分析:

  1. API 固定外层逻辑;
  2. 用户传入函数或模型;
  3. 返回产物的后续执行。

用户函数中的算子不作为外层 API 的固定映射。导出图执行产生的算子不作为 export 阶段的固定映射。optimizer 需要分别分析 foreach、fused 和 single-tensor 分支。

5.2 动态 ACLNN 算子调用

EXEC_NPU_CMD 搜索可能漏掉通过 GetOpApiFuncAddr 或 RunOpApiV2 执行的 ACLNN 算子。Profiler 捕获到静态扫描未发现的算子时,需要继续检查动态调用路径。

5.3 CPU 回退

Profiler 中没有 ACLNN 算子不代表 API 一定无算子。需要同时检查 stderr 中的 CPU fallback 警告和实际输出设备。

5.4 结果状态

报告中区分以下状态:

  • 源码确认且运行时验证;
  • 源码确认但尚未触发;
  • 目标 API 无法触发;
  • 当前环境不支持;
  • 运行时捕获但静态扫描未定位。

5.5 版本范围

没有 NPU 运行条件时,仅输出源码分析结果,不标记为运行时已验证。不同版本之间需要重新核对源码分支和 Profiler 结果。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐