背景概述

在深度学习框架的生态建设中,将专用硬件(如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 算子注册流程

整个算子注册流程遵循"配置驱动、代码生成"的设计理念:

  1. YAML配置声明:在npu_native_functions.yaml中声明支持的算子及其属性
  2. 代码生成:通过torchgen工具链自动生成注册代码
  3. 编译集成:生成的代码与手写实现一起编译进torch_npu扩展
  4. 运行时调度:PyTorch Dispatcher根据DispatchKey将算子调用路由到NPU实现

2.3 DispatchKey 分发机制

torch_npu使用PyTorch的PrivateUse1作为NPU设备的DispatchKey,并重命名为NPU。主要涉及以下几个DispatchKey:

DispatchKey用途实现文件
PrivateUse1 (NPU)NPU前向算子分发RegisterNPU.cpp
AutogradPrivateUse1NPU自动微分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
配置字段说明
字段类型说明
backendstring后端名称,固定为"NPU"
cpp_namespacestringC++命名空间
supportedlist支持的标准PyTorch算子
customlist自定义NPU算子
autogradlist自定义autograd的算子
symintlist支持符号整数的算子
算子配置选项
选项类型说明
funcstring算子签名
op_apibool是否使用OpApi实现
device_checkstring设备检查类型(NoCheck跳过检查)
dispatchdict指定不同DispatchKey的实现
exposedbool是否暴露给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 核心类关系

contains

creates

uses

uses

fallback

NPUNativeFunctions

<>

...

+empty(IntArrayRef size, ...) : Tensor

+clone(Tensor src, optional<MemoryFormat> format) : Tensor

+copy_(Tensor& self, Tensor src, bool non_blocking) : Tensor&

+npu_format_cast(Tensor self, int64_t acl_format) : Tensor

+resize_(Tensor& self, IntArrayRef size) : Tensor&

+view(Tensor self, IntArrayRef size) : Tensor

NPUNativeOpApiFunctions

<>

...

+clone(Tensor src, optional<MemoryFormat> format) : Tensor

+copy_(Tensor& self, Tensor src, bool non_blocking) : Tensor&

NPUGeneratorImpl

-intrusive_ptr<NPUGeneratorState> state_

+set_current_seed(uint64_t seed) : void

+current_seed() : uint64_t

+seed() : uint64_t

+philox_npu_state(uint64_t increment) : PhiloxNpuState

+get_state() : intrusive_ptr<TensorImpl>

+set_state(TensorImpl& new_state) : void

+clone() : shared_ptr<NPUGeneratorImpl>

NPUGeneratorState

+uint64_t seed_

+uint64_t philox_offset_per_thread_

+uint32_t offset_intragraph_

+bool capturing_

+Tensor seed_extragraph_

+Tensor offset_extragraph_

+increase(uint64_t increment) : void

+register_graph(NPUGraph* graph) : void

+unregister_graph(NPUGraph* graph) : void

+clone() : intrusive_ptr<NPUGeneratorState>

PhiloxNpuState

+Payload seed_

+Payload offset_

+uint32_t offset_intragraph_

+bool captured_

FormatCastHelper

<>

+ApplyBaseFormatTensorBy(Tensor src) : Tensor

+IsSameGroupType(Tensor src, Tensor dst) : bool

+format_cast_between_group(...) : bool

+format_cast_as_base_format(Tensor& self, aclFormat format) : void

StorageDescHelper

<>

+SetDesc(Tensor tensor, ...) : void

+CopyDesc(Tensor dst, Tensor src) : void

+IsSameDesc(Tensor a, Tensor b) : bool

+GetValidMemorySize(Tensor tensor) : int64_t

6.2 代码生成类关系

creates

uses

contains

indexes

GenBackendStubs

+main() : void

+run(source_yaml, output_dir, ...) : void

+parse_native_and_custom_yaml(...) : ParsedYaml

+parse_backend_yaml(...) : ParsedExternalYaml

+gen_dispatcher_registrations(...) : void

+create_backend_index(...) : BackendIndex

BackendIndex

+DispatchKey dispatch_key

+bool use_out_as_primary

+bool external

+bool device_guard

+Dict<OperatorName, BackendMetadata> index

+has_kernel(NativeFunction f) : bool

+get_kernel(NativeFunction f) : BackendMetadata

BackendMetadata

+string kernel

+bool structured

+string cpp_namespace

NativeFunction

+FunctionSchema func

+Dict<DispatchKey, str> dispatch

+DeviceCheckType device_check

+Set<Tag> tags

FileManager

+string install_dir

+string template_dir

+write_with_template(filename, template, ...) : void

+write_sharded(...) : void


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强制FP32exp, log, softmax, layer_norm
fp32_set_opt_dtype设置可选的dtype为FP32sum, 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.cppFallback机制实现
torch_npu/csrc/aten/NPUGeneratorImpl.cpp随机数生成器
torch_npu/csrc/aten/common/CopyKernel.cpp数据拷贝核心
torch_npu/csrc/aten/common/TensorFactories.cppTensor创建工厂
torch_npu/csrc/aten/common/FormatCastKernelNpu.cpp格式转换算子
torch_npu/csrc/aten/ops/op_api/CopyKernelOpApi.cppOpApi Copy实现
generate_code.sh代码生成脚本
torchnpugen/gen_backend_stubs.py算子注册生成器
torchnpugen/custom_functions.py自定义函数生成

10. 设计优势与优化空间

10.1 设计优势

  1. 符合PyTorch设计规范

    • 使用标准的DispatchKey机制,与PyTorch深度集成
    • 利用YAML配置声明式地定义算子,与PyTorch原生方式一致
  2. 代码生成自动化

    • 通过torchgen自动生成大量模板代码
    • 减少手写代码量,降低出错概率
  3. 灵活的实现选择

    • 支持多种实现路径:NPUNativeFunctions、OpApi、op_plugin
    • 运行时可根据配置选择最优实现
  4. 完善的回退机制

    • 未实现的算子自动回退到CPU
    • 提供清晰的警告信息便于调试
  5. 良好的扩展性

    • 支持自定义算子通过custom字段添加
    • 支持量化、稀疏等特殊类型算子
  6. 完整的AMP支持

    • 针对NPU优化的自动混合精度策略
    • 区分计算密集型和数值敏感型算子

10.2 设计权衡与优化空间

当前设计在工程实现中经过了多方面的权衡,以下几个方面可在后续演进中进一步优化:

  1. 代码生成链路

    • 依赖 torchgen 工具链完成代码生成,可通过工具版本跟踪与自动化回归,降低框架版本演进带来的维护成本
    • 生成代码以模板化结构为主,调试时可结合代码生成日志与生成中间产物进行定位
  2. 配置分散

    • 算子配置分布在 torch_npu 与 op_plugin 两处,需要合并处理,可通过统一配置管理(见「优化建议」)进一步收敛
  3. 多路径共存成本

    • 多种实现路径在提供灵活性的同时带来一定维护成本,可通过编译期注册与选择机制进一步降低运行时分支开销
  4. 设备检查开销

    • 每次调用包含设备检查逻辑,对高频小算子可通过分层缓存与编译期开关(见「优化建议」)进一步优化

11. 优化建议

11.1 架构层面

  1. 统一配置管理
# 建议:将op_plugin配置整合到单一文件
supported:
  - func: clone
    impl:
      pta: NPUNativeFunctions::clone
      opapi: NPUNativeOpApiFunctions::clone
      opplugin: op_plugin::clone
    preferred: opapi  # 优先实现
  1. 引入算子Registry模式

    • 使用编译时注册替代部分运行时判断
    • 减少运行时分支开销
  2. 分层缓存机制

    • 缓存算子的实现选择结果
    • 避免每次调用都进行能力检测

11.2 性能优化

  1. 减少不必要的检查
// 建议:使用编译期常量或配置开关
#ifdef NPU_DEBUG_MODE
if (c10_npu::get_npu_data_unsafe_flag()) {
    c10_npu::check_npu_tensor_is_safe(tensor);
}
#endif
  1. 批量算子优化

    • 对foreach系列算子进行批量处理优化
    • 减少多次kernel launch开销
  2. Device Guard优化

    • 缓存当前设备状态
    • 避免重复的设备切换

11.3 开发体验

  1. 完善文档

    • 添加详细的算子开发指南
    • 提供算子注册checklist
  2. 调试支持

    • 添加算子调用链追踪
    • 提供性能分析hook点
  3. 错误信息增强

    • 提供更详细的错误上下文
    • 建议可能的修复方案

11.4 代码组织

  1. 分离生成代码和手写代码
torch_npu/csrc/aten/
├── generated/       # 自动生成的代码
│   ├── RegisterNPU.cpp
│   └── ...
└── impl/            # 手写实现
    ├── ops/
    └── common/
  1. 统一命名规范
    • 所有NPU特有函数使用npu_前缀
    • 保持与PyTorch原生算子命名一致

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_COMPATIBILITYOpApi兼容性检查和回退
EXEC_NPU_CMD执行ACLNN命令
NPU_CHECK_ERRORACL错误检查

B. 相关项目链接

  • PyTorch torchgen: https://github.com/pytorch/pytorch/tree/main/torchgen
  • op-plugin: 昇腾算子插件库
  • ACL: Ascend Computing Language

C. 版本兼容性

torch_npu版本PyTorch版本主要变更
2.1.x2.1.x引入OpApi支持
2.0.x2.0.x基础DispatchKey支持

总结

ATen算子注册模块是torch_npu与PyTorch深度集成的关键桥梁。通过YAML配置驱动、代码自动生成、多路径实现选择和完备的Fallback机制,该模块在保证功能完整性的同时,实现了高性能的算子调度。随着OpApi的引入和代码生成工具的持续优化,算子开发效率和运行性能都将得到进一步提升。对于希望在PyTorch生态中集成专用硬件的开发者而言,本文所阐述的设计模式和实现细节具有重要的参考价值。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐