昇腾社区首页
中文
注册

适配开发

支持Ascend C实现自定义算子Kernel,并集成在PyTorch框架,通过PyTorch的API实现算子调用。

前提条件

完成CANN软件的安装具体请参见CANN 软件安装指南(商用版)或CANN 软件安装指南(社区版),完成PyTorch框架的安装具体请参见Ascend Extension for PyTorch 软件安装指南

适配文件结构

├ examples
│  ├ cpp_extension
│  │   ├ op_extension          // 包含Python脚本, 用于初始化模块
│  │   ├ csrc                  // C++目录
│  │   │   ├ kernel            // 算子kernel实现    
│  │   │   ├ host              // 算子注册torch
│  │   │   │   ├ tiling        // 算子tiling实现
│  │   ├ test                  // 测试用例目录
│  │   ├ CMakeLists.txt        // CMake文件
│  │   ├ setup.py              // setup文件
│  │   ├ README.md             // 模块使用说明

操作步骤

以下流程以add_custom kernel算子适配为例进行说明。

  1. kernel算子实现。
    1. 在./cpp_extension/csrc/kernel目录下完成kernel算子实现,具体可参考CANN Ascend C算子开发指南
    2. 在CMakeLists.txt中配置对应kernel的编译选项。

      Ascend C编写的算子,是否需要workspace具有不同编译选项。以下示例中提供了两种算子的实现:

      • 不需要workspace:add_custom算子。
      • 需要workspace:matmul_leakyrelu_custom算子。
      • add_custom算子示例:
        ascendc_library(no_workspace_kernel STATIC
          csrc/kernel/add_custom.cpp
        )
      • matmul_leakyrelu_custom算子示例:
        ascendc_library(workspace_kernel STATIC
          csrc/kernel/matmul_leakyrelu_custom.cpp
        )
        ascendc_compile_definitions(workspace_kernel PRIVATE
        -DHAVE_WORKSPACE
        -DHAVE_TILING
        )
  2. 在./cpp_extension/csrc/host目录下,封装实现的kernel算子,注册为PyTorch的API,子目录tiling存放算子的tiling函数。以下示例以自定义torch.ops.npu.my_add API(封装add_custom kernel算子)为例:
    1. Aten IR定义。
      PyTorch通过TORCH_LIBRARY宏将C++算子实现绑定到Python。Python侧可以通过torch.ops.namespace.op_name方式进行调用。如果在相同namespace下的不同API,定义在不同文件,需要遵循PyTorch官方规则使用TORCH_LIBRARY_FRAGMENT。自定义my_add API注册在npu命名空间如下:
      TORCH_LIBRARY_FRAGMENT(npu, m)
      {
        m.def("my_add(Tensor x, Tensor y) -> Tensor");
      }

      通过此注册,Python侧可通过torch.ops.npu.my_add调用自定义的API。

    2. Aten IR实现。
      1. 按需包含头文件。注意需要包含对应的核函数调用接口声明所在的头文件,aclrtlaunch_{kernel_name}.h(该头文件为Ascend C工程框架自动生成),kernel_name为算子核函数的名称。
      2. 算子适配,根据Aten IR定义适配算子,包括按需实现输出Tensor申请,workspace申请,调用kernel算子等。torch_npu的算子下发和执行是异步的,通过TASKQUEUE实现。以下示例中通过EXEC_KERNEL_CMD宏封装了算子的ACLRT_LAUNCH_KERNEL方法,将算子执行入队到torch_npu的TASKQUEUE。
        #include "utils.h"
        #include "aclrtlaunch_add_custom.h"
        at::Tensor run_add_custom(const at::Tensor &x, const at::Tensor &y)
        {
          at::Tensor z = at::empty_like(x);
          uint32_t blockDim = 8;
          uint32_t totalLength = 1;
          for (uint32_t size : x.sizes()) {
              totalLength *= size;
          }
          EXEC_KERNEL_CMD(add_custom, blockDim, x, y, z, totalLength);
          return z;
        }
    3. Aten IR注册。
      通过PyTorch提供的TORCH_LIBRARY_IMPL注册算子实现,运行在NPU设备需要注册在PrivateUse1上。示例如下:
      TORCH_LIBRARY_IMPL(npu, PrivateUse1, m)
      {
        m.impl("my_add", TORCH_FN(run_add_custom));
      }
  3. 运行自定义算子。
    1. 设置编译的AI处理器型号,将CMakeLists.txt内的SOC_VERSION修改为所需产品型号。对应代码位置如下:
      set(SOC_VERSION "Ascendxxxyy" CACHE STRING "system on chip type")

      需将Ascendxxxyy修改为对应产品型号。

      SOC_VERSION获取方法如下:

      • Atlas A3 训练系列产品/Atlas A3 推理系列产品:在安装昇腾AI处理器的服务器执行npu-smi info命令进行查询,获取Name信息。实际配置值为AscendName,例如Name取值为xxxyy,实际配置值为Ascendxxxyy。
      • Atlas A3 训练系列产品/Atlas A3 推理系列产品:在安装昇腾AI处理器的服务器执行npu-smi info -t board -i id -c chip_id命令进行查询,获取Chip NameNPU Name信息,实际配置值为Chip Name_NPU Name。例如Chip Name取值为Ascendxxx,NPU Name取值为1234,实际配置值为Ascendxxx_1234。

        其中:

        • id:设备id,通过npu-smi info -l命令查出的NPU ID即为设备id。
        • chip_id:芯片id,通过npu-smi info -m命令查出的Chip ID即为芯片id。
    2. 运行setup脚本,编译生成whl包。

      编译工程通过setuptools为用户封装好编译算子kernel和集成到PyTorch,如果需要更多的编译配置,可按需更改CMakeLists.txt文件。

      python setup.py bdist_wheel
    3. 安装whl包。
      cd dist
      pip install *.whl
    4. 运行样例。
      cd test
      python test.py