| Files | Last commit | Last update |
|---|---|---|
| 3 months ago | ||
| 3 months ago | ||
| 3 months ago | ||
| 4 months ago | ||
| 3 months ago |
cann_ops_nn
面向 Ascend NPU 的高性能算子扩展库,通过 JIT 编译将 PyTorch 接口与 ACLNN 算子库桥接。
构建与安装
前置条件
- 操作系统:Linux
- Python:3.8+
- 编译器:GCC 9.4.0+
- 框架:PyTorch>=2.6.0、torch_npu(需匹配 PyTorch 版本)
- 工具包:Ascend CANN Toolkit
构建 Wheel 包
支持两种构建方式:通过 build.sh 构建和直接使用 python3 -m build 构建。
方式一:通过 build.sh 构建(推荐)
# 构建整包(包含所有算子)
bash build.sh --torch_extension
# 构建单算子包(仅包含指定算子)
bash build.sh --torch_extension --ops=swiglu_group --vendor_name=custom
# 构建多算子包
bash build.sh --torch_extension --ops=swiglu_group,swiglu_group_quant --vendor_name=custom
构建完成后,wheel 包会自动复制到 build_out/ 目录。
参数说明:
| 参数 | 说明 |
|---|---|
--torch_extension |
仅构建 torch_extension wheel 包,不执行 cmake 编译 |
--ops=op1,op2,... |
指定编译的算子名(逗号分隔),不指定则编译所有算子 |
--vendor_name=name |
指定子包名,用于子包命名和隔离 |
包命名规则:
| 场景 | 包名 | 安装目录 |
|---|---|---|
| 整包 | cann_ops_nn |
cann_ops_nn/ |
| 单算子/多算子包 | cann_ops_nn_<vendor> |
cann_ops_nn_<vendor>/ |
方式二:直接使用 python3 -m build 构建
# 安装依赖
python3 -m pip install -r requirements.txt
# 构建整包
python3 -m build --wheel -n
# 构建单算子包
TORCH_EXTENSION_OPS=swiglu_group TORCH_EXTENSION_VENDOR=custom python3 -m build --wheel -n
安装
# 安装整包
python3 -m pip install dist/cann_ops_nn-1.0.0-*.whl --no-deps
# 安装单算子包
python3 -m pip install dist/cann_ops_nn_myvendor-1.0.0-*.whl --no-deps
整包与子包共存机制
整包和单算子包可以同时安装,互不冲突:
- 整包安装到
cann_ops_nn/目录,包含所有算子。 - 单算子包安装到
cann_ops_nn_<vendor>/目录,与整包物理隔离。 - 单算子包通过 entry point 机制注册算子,优先级高于整包。用户调用
cann_ops_nn.swiglu_group(x)时,若子包已安装则使用子包的算子实现。 - 卸载单算子包后,整包的同名算子自动接管。
# 安装整包
pip install cann_ops_nn-1.0.0-*.whl --no-deps
# 安装单算子包(覆盖整包中的同名算子)
pip install cann_ops_nn_myvendor-1.0.0-*.whl --no-deps
# 卸载单算子包(整包算子自动恢复)
pip uninstall cann-ops-nn-myvendor
目录结构
├── torch_extension
│ ├── build.sh # 构建脚本(支持 --torch_extension 参数)
│ ├── setup.py # wheel 打包配置(支持单算子编包)
│ ├── requirements.txt
│ ├── cann_ops_nn # 整包安装目录
│ │ ├── __init__.py # 包入口,from .ops import *
│ │ ├── op_builder/
│ │ │ └── builder.py # OpBuilder 基类,JIT 编译管理
│ │ ├── common/
│ │ │ └── aclnn_common.h # ACLNN_CMD 宏等公共能力
│ │ ├── csrc/
│ │ │ └── <category>/<op>.cpp # C++ kernel wrapper
│ │ ├── ops/
│ │ │ ├── __init__.py # 自动发现算子 + entry point 加载
│ │ │ └── <category>/<op>/<op>.py # Python 前端
│ │ └── docs/
│ │ └── torch_extension_guidelines.md # 开发规范
│ └── dist/ # 构建产物输出目录
├── <category>/<op>/torch_extension/ # 分布式算子(仓库根目录下)
│ ├── <op>.py
│ ├── __init__.py
│ └── csrc/<op>.cpp
└── build_out/ # build.sh 输出目录
快速入门
import torch
import torch_npu
import cann_ops_nn
# 初始化 NPU 张量
x = torch.randn(16, 32, dtype=torch.float16).npu()
# 调用算子(两种方式等价)
result = cann_ops_nn.swiglu_group(x)
result = cann_ops_nn.ops.swiglu_group(x)
print(result.shape) # torch.Size([16, 16])
开发者指南:新增算子
以添加新算子 new_operator 为例,需提供 C++ 绑定和 Python 构建器。
1. C++ 后端 (cann_ops_nn/csrc/<op_category>/new_operator.cpp)
该文件将 PyTorch 张量桥接到 ACLNN C-API。
#include <torch/extension.h>
#include "aclnnop/aclnn_new_operator.h"
#include "../common/aclnn_common.h"
namespace cann_ops_nn {
namespace <op_category> {
at::Tensor new_operator(
const at::Tensor& input1,
const at::Tensor& input2,
int64_t param1,
const std::string& param2)
{
// 设备检查
TORCH_CHECK(input1.device().type() == at::kPrivateUse1, "input1 must be on NPU device");
TORCH_CHECK(input2.device().type() == at::kPrivateUse1, "input2 must be on NPU device");
// 输出创建
at::Tensor out = at::empty(output_shape,
at::TensorOptions().dtype(output_dtype).device(at::kPrivateUse1));
// ACLNN 调用(宏内部自动处理类型转换和 workspace)
ACLNN_CMD(aclnnNewOperator, input1, input2, out);
return out;
}
} // namespace <op_category>
} // namespace cann_ops_nn
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("new_operator", &cann_ops_nn::<op_category>::new_operator,
"NewOperator on NPU");
}
2. Python 前端 (cann_ops_nn/ops/<op_category>/new_operator.py)
该文件管理 JIT 编译逻辑并将算子注册到 PyTorch Dispatcher。
import torch
import torch_npu
from torch.library import impl
from cann_ops_nn.op_builder import OpBuilder, get_as_library
class NewOperatorOpBuilder(OpBuilder):
def __init__(self):
super().__init__("new_operator")
def sources(self):
"""C++ 源码路径。"""
return ['csrc/<op_category>/new_operator.cpp']
def schema(self) -> str:
"""PyTorch 算子签名。"""
return "new_operator(Tensor input1, Tensor input2, int param1=0, str param2=\"\") -> Tensor"
def register_meta(self):
"""
注册 Meta 实现(形状/类型推导)。
对 Autograd 和 FakeTensor 支持至关重要。
"""
@impl(get_as_library(), self.name, "Meta")
def new_operator_meta(input1, input2, param1=0, param2=""):
return torch.empty_like(input1)
# 实例化构建器
builder = NewOperatorOpBuilder()
@impl(get_as_library(), builder.name, "PrivateUse1")
def new_operator(input1, input2, param1=0, param2=""):
"""
Dispatcher 的 NPU 实现。
'PrivateUse1' 是自定义 NPU 后端的分发键。
"""
op_module = builder.load() # 编译/加载 .so 文件
return op_module.new_operator(input1, input2, param1, param2)
技术说明
| 组件 | 职责 |
|---|---|
| OpBuilder | 使用 ninja 处理 C++ 源码的 JIT 编译 |
| Meta 分发 | 允许 PyTorch 在不运行 NPU 代码的情况下推导输出形状/类型 |
| PrivateUse1 | PyTorch 路由 NPU 特定操作使用的后端分发键 |
| ACLNN_CMD 宏 | 自动处理 at::Tensor → aclTensor* 类型转换、workspace 申请/释放、流调度 |