【TorchNPU 中 ATen算子注册模块技术解析】
背景概述
在深度学习框架的生态建设中,将专用硬件(如NPU)无缝集成到主流框架中是一项关键挑战。PyTorch作为业界广泛使用的深度学习框架,其通过DispatchKey机制为不同后端提供了灵活的扩展能力。torch_npu项目正是基于这一机制,将华为昇腾NPU的算子实现注册到PyTorch调度系统中,使得开发者能够以透明的方式在NPU设备上运行PyTorch模型。
本文聚焦于torch_npu中ATen算子注册模块的设计与实现,从架构设计、核心机制、代码生成到优化实践,系统性地阐述如何构建一个高效、可扩展的算子注册体系。
1. 模块概述
ATen算子注册模块是torch_npu项目的核心组件,负责将NPU后端的算子实现注册到PyTorch的调度系统中。该模块通过PyTorch的DispatchKey机制,使得当用户在NPU设备上调用PyTorch算子时,能够自动路由到相应的NPU实现。
1.1 主要职责
- 算子声明与定义:通过YAML配置文件声明NPU支持的算子列表
- 代码生成:基于YAML配置自动生成C++注册代码
- 算子分发:利用PyTorch Dispatcher机制将算子调用路由到NPU实现
- 自动混合精度(AMP):为NPU设备提供自动混合精度支持
- Autograd支持:集成自动微分机制
- CPU回退:对于未实现的算子提供CPU回退机制
1.2 文件组织结构
torch_npu/csrc/aten/
├── npu_native_functions.yaml # 算子配置文件
├── AutoCastOps.cpp # 自动混合精度注册
├── VariableFallbackKernel.cpp # Autograd和CPU回退
├── BinaryOps.cpp # 二元操作注册
├── PinMemory.cpp # Pin Memory相关操作
├── NPUGeneratorImpl.cpp/h # 随机数生成器实现
├── common/ # 通用功能实现
│ ├── CopyKernel.cpp # 数据拷贝核心实现
│ ├── TensorFactories.cpp # Tensor创建工厂
│ ├── EmptyTensor.cpp # 空Tensor创建
│ └── ...
├── mirror/ # PyTorch镜像功能
└── ops/ # 算子实现
└── op_api/ # OpApi算子实现
2. 核心架构设计
2.1 整体架构
算子注册模块在PyTorch生态中处于承上启下的关键位置,其整体架构如下:
┌─────────────────────────────────────────────────────────────────────────────┐
│ PyTorch用户调用层 │
│ torch.xxx(tensor.npu()) │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ PyTorch Dispatcher 调度系统 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │DispatchKey │ │DispatchKey │ │DispatchKey │ │DispatchKey │ │
│ │PrivateUse1 │ │Autograd │ │AutoCast │ │SparsePrivateUse1 │ │
│ │ (NPU) │ │PrivateUse1 │ │PrivateUse1 │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ torch_npu ATen注册层 │
│ ┌────────────────────┐ ┌────────────────────┐ ┌─────────────────────┐ │
│ │ RegisterNPU.cpp │ │ AutoCastOps.cpp │ │VariableFallback.cpp│ │
│ │ (生成的注册代码) │ │ (AMP注册) │ │ (回退机制) │ │
│ └────────────────────┘ └────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 算子实现层 │
│ ┌────────────────────┐ ┌────────────────────┐ ┌─────────────────────┐ │
│ │ NPUNativeFunctions │ │NPUNativeOpApi │ │ op_plugin │ │
│ │ (PTA本地实现) │ │Functions (OpApi) │ │ (算子插件实现) │ │
│ └────────────────────┘ └────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ ACL (Ascend Computing Language) 层 │
│ 华为昇腾计算库 │
└─────────────────────────────────────────────────────────────────────────────┘
2.2 算子注册流程
整个算子注册流程遵循"配置驱动、代码生成"的设计理念:
- YAML配置声明:在
npu_native_functions.yaml中声明支持的算子及其属性 - 代码生成:通过torchgen工具链自动生成注册代码
- 编译集成:生成的代码与手写实现一起编译进torch_npu扩展
- 运行时调度:PyTorch Dispatcher根据DispatchKey将算子调用路由到NPU实现
2.3 DispatchKey 分发机制
torch_npu使用PyTorch的PrivateUse1作为NPU设备的DispatchKey,并重命名为NPU。主要涉及以下几个DispatchKey:
| DispatchKey | 用途 | 实现文件 |
|---|---|---|
PrivateUse1 (NPU) | NPU前向算子分发 | RegisterNPU.cpp |
AutogradPrivateUse1 | NPU自动微分 | RegisterAutogradNPU.cpp |
AutocastPrivateUse1 | 自动混合精度 | AutoCastOps.cpp |
SparsePrivateUse1 | 稀疏张量支持 | RegisterSparsePrivateUse1.cpp |
QuantizedPrivateUse1 | 量化算子支持 | QuantizedRegister.cpp |
2.4 代码生成架构
代码生成流程将YAML配置转化为可编译的C++注册代码,其架构如下:
┌─────────────────────────────────────────────────────────────────────────────┐
│ YAML配置输入 │
│ ┌────────────────────────┐ ┌──────────────────────────────────────────┐ │
│ │npu_native_functions. │ │op_plugin_functions.yaml │ │
│ │yaml (torch_npu) │ │(op-plugin) │ │
│ └────────────────────────┘ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ generate_code.sh │
│ 代码生成脚本 │
└─────────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐
│ gen_backend_stubs.py │ │ gen_autograd.py │ │ codegen_ops_info.py │
│ (算子注册生成) │ │ (自动微分生成) │ │ (算子信息生成) │
└──────────────────────┘ └──────────────────┘ └──────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 生成的C++代码 │
│ ┌──────────────┐ ┌──────────────────────┐ ┌─────────────────────────────┐ │
│ │RegisterNPU │ │NPUNativeFunctions.h │ │CustomRegisterSchema.cpp │ │
│ │.cpp │ │ │ │ │ │
│ └──────────────┘ └──────────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
3. 代码生成与调度流程
3.1 算子定义流程
算子定义通过YAML配置文件进行声明:
# torch_npu/csrc/aten/npu_native_functions.yaml
backend: NPU
cpp_namespace: at_npu::native
supported: # 支持的标准PyTorch算子
- _local_scalar_dense
- clone
- copy_
- empty.memory_format
...
custom: # 自定义NPU算子
- func: npu_format_cast(Tensor self, int acl_format) -> Tensor
exposed: True
...
symint: # 支持SymInt的算子
- as_strided_
- new_empty_strided
3.2 代码生成流程
┌─────────────────────────────────────────────────────────────────────────┐
│ 1. 执行 generate_code.sh │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 2. 调用 torchnpugen.gen_backend_stubs │
│ - 解析 native_functions.yaml (PyTorch原生) │
│ - 解析 npu_native_functions.yaml (torch_npu) │
│ - 合并 op_plugin_functions.yaml (op-plugin) │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 3. 构建 BackendIndex │
│ - 为每个支持的算子创建 BackendMetadata │
│ - 建立 OperatorName -> Kernel映射 │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 4. 生成注册代码 │
│ - RegisterNPU.cpp: PrivateUse1 DispatchKey注册 │
│ - RegisterAutogradNPU.cpp: AutogradPrivateUse1注册 │
│ - NPUNativeFunctions.h: 函数声明 │
│ - CustomRegisterSchema.cpp: 自定义算子注册 │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 5. 调用 torchnpugen.autograd.gen_autograd │
│ - 生成自动微分相关代码 │
└─────────────────────────────────────────────────────────────────────────┘
3.3 算子注册流程
算子注册使用PyTorch的TORCH_LIBRARY_IMPL宏:
// 生成的RegisterNPU.cpp示例结构
TORCH_LIBRARY_IMPL(aten, PrivateUse1, m) {
// 算子注册
m.impl("clone", TORCH_FN(wrapper_NPU_clone));
m.impl("copy_", TORCH_FN(wrapper_NPU_copy_));
m.impl("empty.memory_format", TORCH_FN(wrapper_NPU_memory_format_empty));
...
}
3.4 算子调度流程
用户调用: tensor.clone()
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ PyTorch Dispatcher │
│ 检测tensor设备类型 -> PrivateUse1 (NPU) │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ wrapper_NPU_clone (生成的wrapper函数) │
│ - Device Guard设置 │
│ - 数据安全检查 │
│ - Profiler记录 │
│ - Op Hook检查 │
└─────────────────────────────────────────────────────────────────────────┘
│
├─────────────────┬─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│NPUNative │ │NPUNativeOpApi │ │op_plugin:: │
│Functions:: │ │Functions::clone │ │clone │
│clone │ │(OpApi实现) │ │(插件实现) │
└──────────────┘ └──────────────────┘ └──────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ ACL API调用 │
└─────────────────────────────────────────────────────────────────────────┘
4. 多路径实现策略
4.1 三种实现路径
torch_npu为算子提供了三种可选的实现路径,在运行时根据配置和可用性进行选择:
| 实现路径 | 说明 | 优势 |
|---|---|---|
| NPUNativeFunctions | 传统实现方式,基于OpCommand封装 | 成熟稳定,兼容性好 |
| NPUNativeOpApiFunctions | 基于ACLNN(Ascend Computing Language Neural Network)接口 | 性能更优,支持新特性 |
| op_plugin | 第三方算子插件库 | 扩展性强,社区维护 |
4.2 运行时选择逻辑
// 运行时根据环境变量和配置选择实现路径
if (at_npu::native::env::CheckJitDisable()) {
// 优先使用OpApi实现
return at_npu::native::NPUNativeOpApiFunctions::clone(self, memory_format);
}
// 回退到传统实现
return at_npu::native::NPUNativeFunctions::clone(self, memory_format);
4.3 兼容性处理
对于OpApi实现,通过DO_COMPATIBILITY宏进行兼容性检查和回退:
at::Tensor NPUNativeOpApiFunctions::special_sinc(const at::Tensor& self) {
// 检查aclnn接口是否可用,不可用则回退到传统实现
DO_COMPATIBILITY(aclnnSinc, NPUNativeFunctions::special_sinc(self));
// 准备输出tensor
auto output = OpPreparation::apply_tensor_without_format(self);
// 调用ACLNN接口
EXEC_NPU_CMD(aclnnSinc, self, output);
return output;
}
5. 代码实现细节
5.1 YAML 配置格式详解
文件路径:torch_npu/csrc/aten/npu_native_functions.yaml
# 后端标识 - 指定这是NPU后端
backend: NPU
# C++命名空间 - 所有实现函数所在的命名空间
cpp_namespace: at_npu::native
# 支持的标准PyTorch算子列表
supported:
# 简单形式 - 直接列出算子名
- _local_scalar_dense
- _pin_memory
# 带重载名的算子
- empty.memory_format
- set_.source_Storage
# 带额外配置的算子
- func: clone
op_api: True # 标记使用OpApi实现
- func: copy_
op_api: True
- func: resize_as_
device_check: NoCheck # 跳过设备检查
# 自定义NPU特有算子
custom:
# 基本格式
- func: get_npu_format(Tensor self) -> int
# 带dispatch配置的算子
- func: empty_with_format(int[] size, ...) -> Tensor
dispatch:
CompositeExplicitAutograd: empty_with_format
# 暴露给Python的算子
- func: npu_format_cast(Tensor self, int acl_format) -> Tensor
exposed: True
# 跳过设备检查
- func: copy_memory_(Tensor(a!) self, Tensor src, bool non_blocking=False) -> Tensor(a!)
device_check: NoCheck
# 支持SymInt的算子列表
symint:
- as_strided_
- new_empty_strided
配置字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
backend | string | 后端名称,固定为"NPU" |
cpp_namespace | string | C++命名空间 |
supported | list | 支持的标准PyTorch算子 |
custom | list | 自定义NPU算子 |
autograd | list | 自定义autograd的算子 |
symint | list | 支持符号整数的算子 |
算子配置选项
| 选项 | 类型 | 说明 |
|---|---|---|
func | string | 算子签名 |
op_api | bool | 是否使用OpApi实现 |
device_check | string | 设备检查类型(NoCheck跳过检查) |
dispatch | dict | 指定不同DispatchKey的实现 |
exposed | bool | 是否暴露给Python层 |
5.2 生成的代码结构
代码生成后的文件位于 torch_npu/csrc/aten/ 目录下:
RegisterNPU.cpp(示例结构)
// 自动生成的算子注册代码
#include "torch_npu/csrc/aten/NPUNativeFunctions.h"
#include "torch_npu/csrc/aten/NPUOpApiNativeFunctions.h"
#include "op_plugin/OpInterface.h"
namespace at {
namespace {
// 算子wrapper函数定义
at::Tensor wrapper_NPU_clone(const at::Tensor& self,
c10::optional memory_format) {
#ifndef BUILD_LIBTORCH
torch_npu::profiler::NPURecordFunction guard;
#endif
// 数据安全检查
if (c10_npu::get_npu_data_unsafe_flag()) {
c10_npu::check_npu_tensor_is_safe(self);
}
// Device Guard
const OptionalDeviceGuard device_guard(device_of(self));
// 根据配置选择实现
if (at_npu::native::env::CheckJitDisable()) {
return at_npu::native::NPUNativeOpApiFunctions::clone(self, memory_format);
}
return at_npu::native::NPUNativeFunctions::clone(self, memory_format);
}
// 注册到PyTorch Dispatcher
TORCH_LIBRARY_IMPL(aten, PrivateUse1, m) {
m.impl("clone", TORCH_FN(wrapper_NPU_clone));
m.impl("copy_", TORCH_FN(wrapper_NPU_copy_));
// ... 更多算子注册
}
} // anonymous namespace
} // namespace at
NPUNativeFunctions.h(示例结构)
// 自动生成的函数声明
#pragma once
namespace at_npu {
namespace native {
struct TORCH_API NPUNativeFunctions {
static at::Tensor clone(const at::Tensor& self,
c10::optional memory_format);
static at::Tensor& copy_(at::Tensor& self,
const at::Tensor& src,
bool non_blocking);
static at::Tensor empty(c10::IntArrayRef size,
c10::optional dtype_opt,
c10::optional layout_opt,
c10::optional device_opt,
c10::optional pin_memory_opt,
c10::optional memory_format_opt);
// ... 更多函数声明
};
} // namespace native
} // namespace at_npu
5.3 算子实现模式
模式1:直接实现
文件路径:torch_npu/csrc/aten/common/TensorFactories.cpp
at::Tensor NPUNativeFunctions::empty(
c10::IntArrayRef size,
c10::optional dtype_opt,
c10::optional layout_opt,
c10::optional device_opt,
c10::optional pin_memory_opt,
c10::optional memory_format_opt)
{
// 设备初始化
auto device_ = c10::device_or_default(device_opt);
torch_npu::utils::maybe_initialize_npu(device_);
// 分配内存
c10::Allocator *allocator = c10_npu::NPUCachingAllocator::get();
auto dtype = c10::scalarTypeToTypeMeta(dtype_or_default(dtype_opt));
int64_t size_bytes = nelements * dtype.itemsize();
// 创建Storage
c10::intrusive_ptr storage_impl =
torch_npu::make_npu_storage_impl(...);
// 创建Tensor
auto tensor = at::detail::make_tensor(
storage_impl, dtype);
return tensor;
}
模式2:OpApi实现(带回退)
文件路径:torch_npu/csrc/aten/ops/op_api/CloneKernelOpApi.cpp
at::Tensor NPUNativeOpApiFunctions::clone(
const at::Tensor &src,
c10::optional format)
{
// DO_COMPATIBILITY宏: 检查aclnn接口是否可用,不可用则回退
DO_COMPATIBILITY(aclnnInplaceCopy,
NPUNativeFunctions::clone(src, format));
auto baseSelf = OpPreparation::apply_tensor_without_format(src);
baseSelf.copy_(src);
at::namedinference::propagate_names(baseSelf, src);
return baseSelf;
}
模式3:使用OpCommand
文件路径:torch_npu/csrc/aten/common/FormatCastKernelNpu.cpp
at::Tensor format_cast_impl_out_npu(at::Tensor& dst, const at::Tensor& src)
{
// 使用OpCommand封装ACL调用
OpCommand cmd;
cmd.Name("Identity")
.InputWithoutContiguous(src)
.Output(dst)
.Run();
return dst;
}
模式4:调用ACLNN接口
void copy_d2d_baseformat_opapi(at::Tensor& dst, const at::Tensor& src,
bool non_blocking)
{
// 直接调用ACLNN宏
EXEC_NPU_CMD(aclnnInplaceCopy, dst, src);
}
6. 核心类设计
6.1 核心类关系
6.2 代码生成类关系
7. 自动混合精度(AMP)支持
7.1 AutoCast 注册机制
AutoCast模块为NPU设备提供自动混合精度支持,文件路径为torch_npu/csrc/aten/AutoCastOps.cpp:
// Fallback处理:未注册的算子直接透传
TORCH_LIBRARY_IMPL(_, AutocastPrivateUse1, m) {
m.fallback(torch::CppFunction::makeFallthrough());
}
// 针对aten命名空间的算子注册
TORCH_LIBRARY_IMPL(aten, AutocastPrivateUse1, m) {
// 低精度计算 (FP16/BF16)
KERNEL_PRIVATEUSEONE(_convolution, deprecated, lower_precision_fp)
KERNEL_PRIVATEUSEONE(conv1d, lower_precision_fp)
KERNEL_PRIVATEUSEONE(matmul, lower_precision_fp)
KERNEL_PRIVATEUSEONE(mm, lower_precision_fp)
KERNEL_PRIVATEUSEONE(linear, lower_precision_fp)
KERNEL_PRIVATEUSEONE(bmm, lower_precision_fp)
// 强制FP32计算 (数值敏感)
KERNEL_PRIVATEUSEONE(acos, fp32)
KERNEL_PRIVATEUSEONE(exp, fp32)
KERNEL_PRIVATEUSEONE(log, fp32)
KERNEL_PRIVATEUSEONE(softplus, fp32)
KERNEL_PRIVATEUSEONE(layer_norm, fp32)
// 类型提升
KERNEL_PRIVATEUSEONE(addcdiv, promote)
KERNEL_PRIVATEUSEONE(addcmul, promote)
// 禁用的算子 (需要特殊处理)
m.impl(TORCH_SELECTIVE_NAME("aten::binary_cross_entropy"),
TORCH_FN((&binary_cross_entropy_banned)));
}
7.2 AutoCast 精度策略
| 策略 | 说明 | 典型算子 |
|---|---|---|
lower_precision_fp | 使用低精度(FP16) | conv, matmul, linear |
fp32 | 强制FP32 | exp, log, softmax, layer_norm |
fp32_set_opt_dtype | 设置可选的dtype为FP32 | sum, prod, cumsum |
promote | 类型提升 | addcdiv, atan2 |
8. Fallback 机制
8.1 CPU Fallback
对于未在NPU后端实现的算子,提供CPU回退机制以确保功能的完整性:
void npu_cpu_fallback(const c10::OperatorHandle& op, torch::jit::Stack* stack)
{
// 打印提示信息
if (!has_op_name_warned(c10::toString(op.schema().operator_name()))) {
TORCH_NPU_WARN("The operator '",
op.schema().operator_name(),
"' is not yet adapted for the NPU backend and will fall back to run on the CPU.");
}
// 调用CPU实现
at::native::cpu_fallback(op, stack);
}
// 注册CPU Fallback
TORCH_LIBRARY_IMPL(_, PrivateUse1, m) {
m.fallback(torch::CppFunction::makeFromBoxedFunction<&npu_cpu_fallback>());
}
8.2 Autograd Fallback
为未注册autograd kernel的算子提供警告机制:
// 为没有注册autograd kernel的算子提供警告
TORCH_LIBRARY_IMPL(_, AutogradPrivateUse1, m) {
m.fallback(torch::CppFunction::makeFromBoxedFunction<
&npuBasicAutogradNotImplementedFallbackImpl>());
}
8.3 Sparse Fallback
对于稀疏算子,提供明确的错误提示:
void npu_Sparse_fallback(const c10::OperatorHandle& op, torch::jit::Stack* stack)
{
TORCH_CHECK(false, "The operator '",
op.schema().operator_name(),
"' is not yet adapted for the sparse path on the NPU backend.");
}
TORCH_LIBRARY_IMPL(_, SparsePrivateUse1, m) {
m.fallback(torch::CppFunction::makeFromBoxedFunction<&npu_Sparse_fallback>());
}
9. 关键文件路径汇总
| 文件路径 | 功能描述 |
|---|---|
torch_npu/csrc/aten/npu_native_functions.yaml | 算子配置主文件 |
torch_npu/csrc/aten/AutoCastOps.cpp | 自动混合精度注册 |
torch_npu/csrc/aten/VariableFallbackKernel.cpp | Fallback机制实现 |
torch_npu/csrc/aten/NPUGeneratorImpl.cpp | 随机数生成器 |
torch_npu/csrc/aten/common/CopyKernel.cpp | 数据拷贝核心 |
torch_npu/csrc/aten/common/TensorFactories.cpp | Tensor创建工厂 |
torch_npu/csrc/aten/common/FormatCastKernelNpu.cpp | 格式转换算子 |
torch_npu/csrc/aten/ops/op_api/CopyKernelOpApi.cpp | OpApi Copy实现 |
generate_code.sh | 代码生成脚本 |
torchnpugen/gen_backend_stubs.py | 算子注册生成器 |
torchnpugen/custom_functions.py | 自定义函数生成 |
10. 设计优势与优化空间
10.1 设计优势
-
符合PyTorch设计规范
- 使用标准的DispatchKey机制,与PyTorch深度集成
- 利用YAML配置声明式地定义算子,与PyTorch原生方式一致
-
代码生成自动化
- 通过torchgen自动生成大量模板代码
- 减少手写代码量,降低出错概率
-
灵活的实现选择
- 支持多种实现路径:NPUNativeFunctions、OpApi、op_plugin
- 运行时可根据配置选择最优实现
-
完善的回退机制
- 未实现的算子自动回退到CPU
- 提供清晰的警告信息便于调试
-
良好的扩展性
- 支持自定义算子通过
custom字段添加 - 支持量化、稀疏等特殊类型算子
- 支持自定义算子通过
-
完整的AMP支持
- 针对NPU优化的自动混合精度策略
- 区分计算密集型和数值敏感型算子
10.2 设计权衡与优化空间
当前设计在工程实现中经过了多方面的权衡,以下几个方面可在后续演进中进一步优化:
-
代码生成链路
- 依赖 torchgen 工具链完成代码生成,可通过工具版本跟踪与自动化回归,降低框架版本演进带来的维护成本
- 生成代码以模板化结构为主,调试时可结合代码生成日志与生成中间产物进行定位
-
配置分散
- 算子配置分布在 torch_npu 与 op_plugin 两处,需要合并处理,可通过统一配置管理(见「优化建议」)进一步收敛
-
多路径共存成本
- 多种实现路径在提供灵活性的同时带来一定维护成本,可通过编译期注册与选择机制进一步降低运行时分支开销
-
设备检查开销
- 每次调用包含设备检查逻辑,对高频小算子可通过分层缓存与编译期开关(见「优化建议」)进一步优化
11. 优化建议
11.1 架构层面
- 统一配置管理
# 建议:将op_plugin配置整合到单一文件
supported:
- func: clone
impl:
pta: NPUNativeFunctions::clone
opapi: NPUNativeOpApiFunctions::clone
opplugin: op_plugin::clone
preferred: opapi # 优先实现
-
引入算子Registry模式
- 使用编译时注册替代部分运行时判断
- 减少运行时分支开销
-
分层缓存机制
- 缓存算子的实现选择结果
- 避免每次调用都进行能力检测
11.2 性能优化
- 减少不必要的检查
// 建议:使用编译期常量或配置开关
#ifdef NPU_DEBUG_MODE
if (c10_npu::get_npu_data_unsafe_flag()) {
c10_npu::check_npu_tensor_is_safe(tensor);
}
#endif
-
批量算子优化
- 对foreach系列算子进行批量处理优化
- 减少多次kernel launch开销
-
Device Guard优化
- 缓存当前设备状态
- 避免重复的设备切换
11.3 开发体验
-
完善文档
- 添加详细的算子开发指南
- 提供算子注册checklist
-
调试支持
- 添加算子调用链追踪
- 提供性能分析hook点
-
错误信息增强
- 提供更详细的错误上下文
- 建议可能的修复方案
11.4 代码组织
- 分离生成代码和手写代码
torch_npu/csrc/aten/
├── generated/ # 自动生成的代码
│ ├── RegisterNPU.cpp
│ └── ...
└── impl/ # 手写实现
├── ops/
└── common/
- 统一命名规范
- 所有NPU特有函数使用
npu_前缀 - 保持与PyTorch原生算子命名一致
- 所有NPU特有函数使用
12. 算子实现示例
12.1 添加新的标准算子
假设需要为NPU添加torch.special.sinc算子支持:
步骤1:修改YAML配置
文件:torch_npu/csrc/aten/npu_native_functions.yaml
supported:
# ... 其他算子
- func: special_sinc
op_api: True
步骤2:实现算子(OpApi方式)
文件:torch_npu/csrc/aten/ops/op_api/SincKernelOpApi.cpp
#include "torch_npu/csrc/aten/NPUOpApiNativeFunctions.h"
#include "torch_npu/csrc/aten/NPUNativeFunctions.h"
#include "torch_npu/csrc/framework/utils/OpPreparation.h"
#include "third_party/op-plugin/op_plugin/utils/op_api_common.h"
namespace at_npu {
namespace native {
at::Tensor NPUNativeOpApiFunctions::special_sinc(const at::Tensor& self) {
// 检查aclnn接口是否可用,不可用则回退到传统实现
DO_COMPATIBILITY(aclnnSinc, NPUNativeFunctions::special_sinc(self));
// 准备输出tensor
auto output = OpPreparation::apply_tensor_without_format(self);
// 调用ACLNN接口
EXEC_NPU_CMD(aclnnSinc, self, output);
return output;
}
} // namespace native
} // namespace at_npu
步骤3:实现回退版本(可选)
文件:torch_npu/csrc/aten/ops/SincKernelNpu.cpp
#include "torch_npu/csrc/aten/NPUNativeFunctions.h"
#include "torch_npu/csrc/framework/utils/OpAdapter.h"
namespace at_npu {
namespace native {
at::Tensor NPUNativeFunctions::special_sinc(const at::Tensor& self) {
// 使用OpCommand封装
auto output = OpPreparation::ApplyTensorWithSizes(self.sizes(), self.options());
OpCommand cmd;
cmd.Name("Sinc")
.Input(self)
.Output(output)
.Run();
return output;
}
} // namespace native
} // namespace at_npu
步骤4:重新生成代码
cd /path/to/torch_npu
python -m torchnpugen.gen_backend_stubs \
--output_dir="torch_npu/csrc/aten" \
--source_yaml="torch_npu/csrc/aten/npu_native_functions.yaml" \
...
12.2 添加自定义 NPU 算子
假设需要添加一个NPU特有的npu_fast_gelu算子:
步骤1:定义算子签名
文件:torch_npu/csrc/aten/npu_native_functions.yaml
custom:
# ... 其他自定义算子
- func: npu_fast_gelu(Tensor self) -> Tensor
exposed: True # 暴露给Python
步骤2:实现算子
文件:torch_npu/csrc/aten/ops/FastGeluKernelNpu.cpp
#include "torch_npu/csrc/aten/NPUNativeFunctions.h"
#include "torch_npu/csrc/framework/utils/OpAdapter.h"
#include "torch_npu/csrc/framework/utils/OpPreparation.h"
#include "torch_npu/csrc/core/npu/NPUGuard.h"
namespace at_npu {
namespace native {
at::Tensor NPUNativeFunctions::npu_fast_gelu(const at::Tensor& self) {
// 输入检查
TORCH_CHECK(self.device().is_privateuseone(),
"npu_fast_gelu only supports NPU tensors");
// 设备Guard
c10_npu::NPUGuard guard(self.device());
// 准备输出
at::Tensor output = OpPreparation::ApplyTensorWithSizes(
self.sizes(), self.options());
// 调用ACL算子
OpCommand cmd;
cmd.Name("FastGelu")
.Input(self)
.Output(output)
.Run();
return output;
}
} // namespace native
} // namespace at_npu
步骤3:重新生成并编译
# 生成代码
bash generate_code.sh python3 2.1.0
# 编译
python setup.py build
步骤4:Python中使用
import torch
import torch_npu
# 方式1:通过torch.ops
x = torch.randn(3, 4).npu()
y = torch.ops.npu.npu_fast_gelu(x)
# 方式2:通过torch_npu (如果exposed: True)
y = torch_npu.npu_fast_gelu(x)
12.3 算子开发检查清单
- 在YAML配置中添加算子声明
- 实现NPUNativeFunctions版本(传统方式)
- 实现NPUNativeOpApiFunctions版本(OpApi方式,可选)
- 添加DO_COMPATIBILITY回退逻辑
- 添加输入参数检查
- 添加Device Guard
- 考虑添加AutoCast支持
- 编写单元测试
- 运行代码生成脚本
- 验证编译通过
- 验证功能正确
- 验证性能符合预期
附录
A. 常用宏定义
| 宏 | 用途 |
|---|---|
TORCH_LIBRARY_IMPL | 注册算子到指定DispatchKey |
TORCH_FN | 包装函数指针 |
DO_COMPATIBILITY | OpApi兼容性检查和回退 |
EXEC_NPU_CMD | 执行ACLNN命令 |
NPU_CHECK_ERROR | ACL错误检查 |
B. 相关项目链接
- PyTorch torchgen: https://github.com/pytorch/pytorch/tree/main/torchgen
- op-plugin: 昇腾算子插件库
- ACL: Ascend Computing Language
C. 版本兼容性
| torch_npu版本 | PyTorch版本 | 主要变更 |
|---|---|---|
| 2.1.x | 2.1.x | 引入OpApi支持 |
| 2.0.x | 2.0.x | 基础DispatchKey支持 |
总结
ATen算子注册模块是torch_npu与PyTorch深度集成的关键桥梁。通过YAML配置驱动、代码自动生成、多路径实现选择和完备的Fallback机制,该模块在保证功能完整性的同时,实现了高性能的算子调度。随着OpApi的引入和代码生成工具的持续优化,算子开发效率和运行性能都将得到进一步提升。对于希望在PyTorch生态中集成专用硬件的开发者而言,本文所阐述的设计模式和实现细节具有重要的参考价值。
更多推荐



所有评论(0)