☰
Ascend C 自定义算子编译工程与 Aclnn/Aclop 算子调用实战指南(asc-devkit 示例库解析)
2026/10/15 17:06:59 网站建设 项目流程
  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。

项目地址:https://gitcode.com/cann/asc-devkit
点击查看免费下载

导读

本文围绕 asc-devkit 仓库中 99_acl_based 示例专题展开,系统讲解基于 Ascend C 的自定义算子编译工程(编译、打包、部署)以及 Aclnn / Aclop 两种单算子调用方式的完整实现路径。读完本文,你将掌握自定义算子工程的目录组织与 CMake 构建链路、Tiling 模板与 Tiling 下沉编程范式、静态库链接与多厂商并行打包方案,以及aclnn两级接口与aclopExecuteV2单算子模型调用的可运行代码骨架,能够直接对照仓库示例落地自己的算子开发与部署流程。

示例专题总览

examples/01_simd_cpp_api/02_features/99_acl_based/README_en.md是整个示例专题的入口文档,它将专题划分为两个子目录:

目录名称功能描述
00_acl_compilation自定义算子的编译、打包和部署实现方法
01_acl_invocationAclnn 和 Aclop 算子调用的实现方法

其中 00_acl_compilation 进一步包含四个独立工程:custom_op(标准编译打包部署全流程)、custom_op_simplified_key(自定义简化 key 与 BINARY_JSON 内核二进制选择)、custom_op_static_lib(自定义算子静态库编译与 aclnn 链接执行)、parallel_ops_package(多厂商并行编译打包部署);01_acl_invocation 包含aclnn_invocation与aclop_invocation两个调用示例。下文依次深入。

一、custom_op:自定义算子工程编译、打包与部署全流程

1.1 适用产品与 CANN 版本

产品CANN 版本
Ascend 950PR&950DT>= CANN 9.1.0
Atlas A3>= CANN 9.0.0
Atlas A2>= CANN 9.0.0
Atlas 200I/500 A2 推理>= CANN 9.0.0
Atlas 推理系列>= CANN 9.0.0

注意:本示例包含多个算子示例,实际支持的产品型号请以各算子示例为准。

1.2 工程目录结构

custom_op 采用标准的 host/kernel 分离布局(详见 custom_op 目录):

custom_op ├── CMakeLists.txt // 顶层构建工程文件 ├── framework // 框架适配插件目录 │ ├── CMakeLists.txt │ ├── onnx_plugin // ONNX 框架适配插件 │ └── tf_plugin // TensorFlow 框架适配插件 ├── op_host // Host 侧算子实现目录 │ ├── CMakeLists.txt │ ├── add_custom // Add 算子 host 实现 │ ├── add_custom_template // Add 模板算子 host 实现 │ ├── add_custom_tiling_sink // Add Tiling 下沉算子 host 实现 │ └── leaky_relu_custom // LeakyRelu 算子 host 实现 ├── op_kernel // Kernel 侧算子实现目录 │ ├── CMakeLists.txt │ ├── add_custom // Add 算子 kernel 实现 │ ├── add_custom_template // Add 模板算子 kernel 实现 │ ├── add_custom_tiling_sink // Add Tiling 下沉算子 kernel 实现 │ └── leaky_relu_custom // LeakyRelu 算子 kernel 实现 └── README.md // 示例文档

1.3 顶层 CMake 构建入口

顶层 CMakeLists.txt 是整个工程的构建枢纽,关键点如下:

set(ASCEND_SOC_SERIES ascend910b ascend910_93 ascend950) find_package(ASC REQUIRED)
  • ASCEND_SOC_SERIES声明算子工程适配的昇腾 AI 处理器系列,find_package(ASC)引入 CANN 提供的 Ascend C 编译基础设施(对应 cmake/asc/asc_modules/FindASC.cmake 与 cmake/asc/ascendc.cmake);
  • npu_op_package()定义算子安装包名称(此处为customize)与安装路径,并声明包类型TYPE RUN;
  • 顶层按需add_subdirectory挂载framework、op_host、op_kernel三个子目录。

1.4 Host 侧:算子定义与 Tiling 库

op_host/CMakeLists.txt 集中体现了 Host 侧四类产物的构建方式:

  • npu_op_code_gen:根据 host 源码自动生成算子原型(op_proto)与 aclnn 接口(aclnn_*.cpp);
  • npu_op_library(... TILING):将 tiling 逻辑编为 Tiling 库cust_optiling;
  • npu_op_device_tiling_library(... SHARED):将 Tiling 下沉函数编为设备侧共享库cust_opmaster;
  • npu_op_library(... ACLNN):将自动生成的aclnn_*.cpp编为单算子 API 库cust_opapi;
  • npu_op_library(... GRAPH):编为算子原型库cust_op_proto;
  • 最后通过npu_op_package_add将上述库统一收进customize安装包。

以 AddCustom 的 Host 实现 add_custom_host.cpp 为例,可以看到算子定义的三段式结构:

static ge::graphStatus TilingFunc(gert::TilingContext* context) { AddCustomTilingData* tiling = context->GetTilingData<AddCustomTilingData>(); uint32_t totalLength = context->GetInputShape(0)->GetOriginShape().GetShapeSize(); context->SetSimdNumBlocks(NUM_BLOCKS); tiling->totalLength = totalLength; tiling->tileNum = TILE_NUM; ... } class AddCustom : public OpDef { public: explicit AddCustom(const char* name) : OpDef(name) { this->Input("x").ParamType(REQUIRED).DataType({ge::DT_FLOAT16}).Format({ge::FORMAT_ND}); this->Input("y").ParamType(REQUIRED).DataType({ge::DT_FLOAT16}).Format({ge::FORMAT_ND}); this->Output("z").ParamType(REQUIRED).DataType({ge::DT_FLOAT16}).Format({ge::FORMAT_ND}); this->SetInferShape(ge::InferShape).SetInferDataType(ge::InferDataType); this->AICore().SetTiling(optiling::TilingFunc).AddConfig("ascend910b"); } }; OP_ADD(AddCustom);

要点:OpDef中声明输入/输出的数据类型与 Format(float16、ND),InferShape/InferDataType完成输出 shape 与 dtype 推导(输出直接继承输入 shape 与 dtype),AICore().SetTiling(...)挂接 Tiling 函数并声明适配配置ascend910b,最后OP_ADD注册算子。

1.5 Kernel 侧:算子二进制打包

op_kernel/CMakeLists.txt 通过npu_op_kernel_sources为每个算子登记OP_TYPE、KERNEL_DIR与KERNEL_FILE:

npu_op_kernel_sources(ascendc_kernels OP_TYPE AddCustom KERNEL_DIR add_custom KERNEL_FILE add_custom_kernel.cpp ) ... npu_op_kernel_library(ascendc_kernels SRC_BASE ${CMAKE_CURRENT_SOURCE_DIR} TILING_LIBRARY cust_optiling )

npu_op_kernel_library将 kernel 源码与 Tiling 库cust_optiling绑定,最终产出算子 kernel 二进制库ascendc_kernels并加入安装包。

1.6 四个算子示例的规格与实现解析

AddCustom(Add)
  • 计算式:z = x + y;
  • 输入x/y与输出z均为[8, 2048]、float16、ND;
  • Kernel 函数名:add_custom。

Kernel 实现 add_custom_kernel.cpp 展示了典型的 vector 流水编程:Init()中按GetBlockNum()切分 block、按tileNum * BUFFER_NUM计算循环次数;Process()中使用LocalMemAllocator<Hardware::UB>在统一缓存 UB 上分配三个LocalTensor,通过SetFlag/WaitFlag在 MTE2(数据搬运)、Vector(向量计算)、MTE3(搬出)流水间做事件同步,循环内完成DataCopy搬入、AscendC::Add计算、DataCopy搬出。入口函数add_custom通过REGISTER_TILING_DEFAULT(AddCustomTilingData)与GET_TILING_DATA获取 TilingData。

TilingData 设计:AddCustomTilingData本质是并行切分参数,共 2 个字段——totalLength(待计算数据总量)与tileNum(每个核的数据块总数)。

AddCustomTemplate(模板算子)
  • 计算式:z = x + y;
  • 输入输出支持float16/float,shape[8, 2048],ND;
  • Kernel 函数名:add_custom_template;
  • 模板参数:template<typename D_T_X, typename D_T_Y, typename D_T_Z, int TILE_NUM, int IS_SPLIT>。

模板参数含义:

参数类型说明
D_T_Xtypename输入 x 数据类型(half/float)
D_T_Ytypename输入 y 数据类型(half/float)
D_T_Ztypename输出 z 数据类型(half/float)
TILE_NUMint每个核的数据块总数
IS_SPLITint是否使能数据块切分(0 时 TILE_NUM 不生效)

实现要点(add_custom_template.cpp):模板参数组合取代传统 TilingKey,编译期即可针对不同 dtype/切分策略生成特化内核;if constexpr (IS_SPLIT == 0)走单块Process1,IS_SPLIT == 1走多块流水Process2。Host 侧 Tiling 函数(add_custom_template.cpp)根据输入 dtype 组合出D_T_X/D_T_Y/D_T_Z,当totalLength < 2048时置IS_SPLIT=0, TILE_NUM=1,否则IS_SPLIT=1, TILE_NUM=8,并通过ASCENDC_TPL_SEL_PARAM将模板参数作为 TilingKey 下发。TilingData 只保留 1 个字段totalLength(全核待计算数据总量)。

AddCustomTilingSink(Tiling 下沉)
  • 计算式:z = x + y;
  • 输入输出为[8, 2048]、float、ND;
  • Kernel 函数名:add_custom_tiling_sink。

实现要点(add_custom_tiling_sink_kernel.cpp 与 add_custom_tiling_sink_tiling.cpp):

  • kernel 通过KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY)强制以 AIC/AIV 混合场景运行,满足 Tiling 下沉算子的条件;
  • 全部 Tiling 逻辑独立实现于 tiling 源文件,并通过DEVICE_IMPL_OP_OPTILING(AddCustomTilingSink).Tiling(...)注册下沉 Tiling 函数,使 Tiling 同时可在 host 与 device 执行;
  • Tiling 函数中通过platform_ascendc::PlatformAscendC(context->GetPlatformInfo()).GetLibApiWorkSpaceSize()获取系统 workspace 大小;编译期(InputTensor 的 Data 为空指针时)按系统 workspace + 用户 workspace 最大值(示例为 32 字节)设置,运行期按系统 workspace + 0设置,保证运行期高效、编译期内存充足。
LeakyReluCustom(LeakyRelu)
  • 计算式:y = x (x≥0),y = a*x (x<0),a 为标量;
  • 输入x为[8, 200, 1024]、float、ND,negative_slope为标量0.0;
  • 输出y为[8, 200, 1024]、float、ND;
  • Kernel 函数名:leaky_relu_custom。

实现要点(leaky_relu_custom_kernel.cpp):组合基础 API 完成计算——Maxs(tmp1, x, 0)取正半轴部分、Mins(tmp2, x, 0)取负半轴部分、Muls(tmp2, tmp2, negativeSlope)乘负斜率、最后Add(y, tmp1, tmp2)合并。TilingData 共 3 个字段:totalLength、tileNum(含义与 Add 相同)与negativeSlope(LeakyRelu 负半轴斜率系数,作为计算参数传入 kernel)。

框架适配插件(framework)

custom_op 还包含 ONNX 与 TensorFlow 框架适配插件,用于将第三方框架模型中的算子映射到自定义算子。以 leaky_relu_custom_plugin.cc 为例:

REGISTER_CUSTOM_OP("LeakyReluCustom") .FrameworkType(ONNX) .OriginOpType({ge::AscendString("ai.onnx::8::LeakyRelu"), ...}) .ParseParamsByOperatorFn(ParseOnnxParamsLeakyReluCustom) .ImplyType(ImplyType::TVM);

ParseOnnxParamsLeakyReluCustom从 ONNX 原始算子的attribute(JSON 形式)中解析alpha属性(float 精度损失问题通过 string 形式存储规避),写入自定义算子negative_slope属性。

1.7 构建、打包与部署步骤

在示例根目录执行以下步骤(环境变量配置依据 docs/en/quick_start.md 中 CANN 开发套件的安装方式):

# 1. 配置环境变量(默认安装路径 /usr/local/Ascend) source ${install_path}/cann/set_env.sh # 2. 构建、打包并部署 mkdir -p build && cd build cmake .. && make -j binary package ./custom_opp_*.run

执行成功输出:

SUCCESS

make -j binary package中的binary目标产出算子二进制,package目标将cust_optiling、cust_opapi、cust_op_proto、cust_opmaster、ascendc_kernels等库打成custom_opp_*.run自解压安装包,运行该安装包即完成向 CANN 环境的部署。

1.8 ccache 构建加速(可选)

custom_op 工程支持 ccache 加速重复构建,分为本地缓存与分布式缓存两种模式。前提:安装ccache(建议>= 4.6.1,apt install ccache);分布式场景需确认当前版本支持 Redis 存储(ccache --version输出 Features 包含redis-storage)。

本地缓存:通过 CMake 参数-DENABLE_CCACHE=ON开启(无需修改 CMakeLists.txt,顶层 CMakeLists.txt 中已内置该 option,并在找不到 ccache 时自动回退普通编译):

mkdir -p build && cd build cmake -DENABLE_CCACHE=ON .. && make -j binary package

验证与清理:

ccache -Cz # 清空缓存内容与统计 rm -rf build && mkdir -p build && cd build cmake -DENABLE_CCACHE=ON .. && make -j binary package # 第一次构建:写缓存 cd .. && ccache -z rm -rf build && mkdir -p build && cd build cmake -DENABLE_CCACHE=ON .. && make -j binary package # 第二次构建:命中缓存 ccache --show-stats -v # 观察 Local storage Hits 显著上升 # 清理方式:ccache -z(仅清统计)/ ccache -C(仅清内容)/ ccache -Cz(两者都清)

分布式缓存(ccache + Redis):适用于多机共享缓存——A 机构建并写入 Redis,B 机在源码、构建选项、工具链版本一致时可从 Redis 命中,减少重复编译。ccache 以本地缓存为一级缓存、Redis 为二级共享缓存;若两机编译器路径不同但内容一致,可设置compiler_check=content。网络要求:A 机(首建机)、B 机(二次构建机)、C 机(Redis 服务器,C_IP:6379)处于同一网络,A、B 均能访问 Redis。

  • C 机部署 Redis:
apt install redis-server redis-server --daemonize yes --bind 0.0.0.0 --port 6379 --requirepass <PASSWORD> redis-cli -h <C_IP> -p 6379 -a <PASSWORD> ping

注意:以上配置仅适用于受控测试环境;共享或生产环境需开启访问控制、认证与网络隔离。

  • A/B 机配置 ccache:
apt install redis-tools redis-cli -h <C_IP> -p 6379 -a <PASSWORD> ping # 配置 Redis 为二级存储并带密码认证,格式:redis://default:<PASSWORD>@<C_IP>:6379 ccache --set-config=secondary_storage=redis://default:<PASSWORD>@<C_IP>:6379 # 以内容校验编译器,避免路径差异导致缓存 miss ccache --set-config=compiler_check=content
  • A/B 机执行构建:
ccache -Cz rm -rf build && mkdir -p build && cd build cmake -DENABLE_CCACHE=ON .. && make -j binary package

A 机构建后写入本地缓存与 Redis,B 机本地缓存为空、从 Redis 读取共享缓存;构建后对比两机ccache --show-stats -v,B 机Remote storage Hits应显著上升。建议 A/B 两机保持源码内容、构建命令、编译器版本、源码路径与构建目录路径一致,否则可能缓存 miss。更多 ccache 配置与缓存行为细节可参考 ccache 官方文档。

二、custom_op_simplified_key:自定义简化 Key 与内核二进制选择

该示例基于AddCustomTemplate演示 Host 回调生成自定义simplifiedKey,以及BINARY_JSON如何针对不同输入数据类型选择不同的 Kernel Binary。它是一个独立的自定义算子工程,不改变既有 custom_op 示例的编译行为。目录结构(详见 custom_op_simplified_key):

custom_op_simplified_key ├── CMakeLists.txt ├── op_host │ ├── CMakeLists.txt │ ├── add_custom_template │ └── config │ └── ascend910b │ └── add_custom_template_binary.json └── op_kernel └── add_custom_template

AddCustomTemplate计算z = x + y,支持float16与float32输入输出。Host 侧 add_custom_template.cpp 中的GenSimplifiedKeyFunc依据第一个输入dtype返回简化 key:

输入 dtypesimplified keyKernel Binary
float16custom_fp16AddCustomTemplate_FP16.o
float32custom_fp32AddCustomTemplate_FP32.o

实现要点:

  • OpDef中通过.SetGenSimplifiedKey(optiling::GenSimplifiedKeyFunc)挂接回调;回调内部按context->GetInputDesc(0)->GetDataType()分支返回 key,并用strcpy_s安全写入 256 字节缓冲;
  • 与普通 TilingKey 不同,简化 key 由开发者自定义,直接映射到二进制选择;
  • add_custom_template_binary.json 描述两个 Kernel Binary 及其输入/输出的dtype、format、shape;kernel 编译选项通过--kernel-json-file传入该文件。

构建命令(示例根目录):

source ${install_path}/cann/set_env.sh rm -rf build && mkdir build && cd build cmake .. make AddCustomTemplate_ascend910b # 仅构建单个算子二进制 # 或构建完整二进制与安装包: make -j binary package

构建结果验证:build目录下应包含AddCustomTemplate_FP16.o、AddCustomTemplate_FP32.o、含simplifiedKey与simplifiedKeyMode的算子 JSON 文件,以及汇总 Kernel Binary 查找信息的binary_info_config.json:

find . -name 'AddCustomTemplate_*.o' -o -name 'binary_info_config.json'

说明:示例已预置输入配置目录与 Binary JSON,无需手工配置;开发自有算子时需自行提供 Binary JSON,op_host/config/<soc>仅为推荐的目录布局,CMake 不会自动创建;build下的配置文件由构建过程生成。

三、custom_op_static_lib:自定义算子静态库编译与 aclnn 执行

该示例用AddCustom演示自定义算子静态库的编译、打包、链接,并通过 aclnn 执行算子(详见 custom_op_static_lib)。适用产品为 Ascend 950PR&950DT(>= CANN 9.1.0)、Atlas A3/A2(>= CANN 9.0.0)。目录结构:

custom_op_static_lib ├── CMakeLists.txt // 顶层构建工程文件 ├── app // 链接静态库并执行算子 └── op // 编译并打包自定义算子静态库

实现方式:op目录负责编译打包自定义算子静态库,生成路径为./build/customize-install/lib/lib${package_name}.a;app目录通过find_package(${package_name})引入静态库,编译 main.cpp 生成execute_add_op,调用aclnnAddCustom执行并校验结果。AddCustom 算子描述与 custom_op 一致。

构建与运行:

source ${install_path}/cann/set_env.sh # 默认安装路径 /usr/local/Ascend mkdir -p build && cd build python3 ../app/scripts/gen_data.py # 生成输入/黄金数据 cmake .. && make -j ./execute_add_op

执行成功输出:

test pass

四、parallel_ops_package:多算子工程并行打包部署

该示例演示在顶层 CMake 工程中用ExternalProject_Add并行构建两个独立自定义算子工程:add_custom(AddCustom,扁平目录布局,host/kernel/tiling 源码同级)与leaky_relu_custom(LeakyReluCustom,分层目录布局);每个子工程独立完成编译打包并生成独立的custom_opp_*.run安装包(详见 parallel_ops_package)。Add/LeakyRelu 的算子描述、规格与代码实现均与 custom_op 一致,可对照 custom_op/README_en.md 的 "Example Description"、"Example Specification Description" 与 "Code Implementation" 小节。

构建与运行(示例根目录):

source ${install_path}/cann/set_env.sh cmake -S . -B build cmake --build build -j ./build/add_custom/custom_opp_*.run # add_custom 安装包 ./build/leaky_relu_custom/custom_opp_*.run # leaky_relu_custom 安装包

执行成功输出:

SUCCESS

构建结果:顶层工程在build/下生成两个相互独立的子目录build/add_custom/与build/leaky_relu_custom/,便于多厂商/多算子场景下并行开发与独立发布。

五、Aclnn 单算子 API 调用:aclnn_invocation

5.1 原理与适用场景

单算子 API 执行是纯 C 语言 API 方式,无需为离线模型转换提供单算子描述文件,可直接调用单算子 API 接口。示例基于自定义算子工程部署后,使用两级接口aclnnAddCustomGetWorkspaceSize与aclnnAddCustom执行固定 shape 的算子计算。核心流程:

  1. 创建输入/输出aclTensor对象并准备 device 侧数据;
  2. 调用aclnnAddCustomGetWorkspaceSize获取本次计算所需 workspace 大小,并分配对应 device 内存;
  3. 调用aclnnAddCustom执行计算,aclrtSynchronizeStream同步流,将结果拷回 host 侧校验。

5.2 关键代码解析(main.cpp)

aclnn_invocation/main.cpp 展示了完整调用骨架:

#include "aclnn/aclnn_base.h" #include "aclnn/acl_meta.h" #include "acl/acl_rt.h" #include "aclnn_add_custom.h" // 编译期由 npu_op_code_gen 自动生成 const std::vector<int64_t> shape = {8, 2048}; // 固定 shape // 1. 初始化与建流 CHECK_ACL(aclnnInit(nullptr)); CHECK_ACL(aclrtSetDevice(deviceId)); CHECK_ACL(aclrtCreateStream(&stream)); // 2. 分配 device 内存并构造 aclTensor(ACL_FLOAT16, ACL_FORMAT_ND) void* input0DeviceMem = nullptr; CHECK_ACL(aclrtMalloc(&input0DeviceMem, bufferSize, ACL_MEM_MALLOC_HUGE_FIRST)); aclTensor* input0 = aclCreateTensor(shape.data(), shape.size(), ACL_FLOAT16, nullptr, 0, ACL_FORMAT_ND, shape.data(), shape.size(), input0DeviceMem); // 3. H2D 拷贝输入 CHECK_ACL(aclrtMemcpy(input0DeviceMem, bufferSize, input0HostData.data(), bufferSize, ACL_MEMCPY_HOST_TO_DEVICE)); // 4. 两级接口:先取 workspace 大小,再执行 uint64_t workspaceSize = 0; aclOpExecutor* executor = nullptr; CHECK_ACL(aclnnAddCustomGetWorkspaceSize(input0, input1, output0, &workspaceSize, &executor)); void* workspaceDeviceMem = nullptr; if (workspaceSize > 0) { CHECK_ACL(aclrtMalloc(&workspaceDeviceMem, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST)); } CHECK_ACL(aclnnAddCustom(workspaceDeviceMem, workspaceSize, executor, stream)); // 5. 同步流并 D2H 拷回,与 golden 数据按 1e-3 容差比对 CHECK_ACL(aclrtSynchronizeStream(stream)); CHECK_ACL(aclrtMemcpy(output0HostData.data(), bufferSize, output0DeviceMem, bufferSize, ACL_MEMCPY_DEVICE_TO_HOST));

要点:aclnn_add_custom.h与aclnnAddCustom*接口是在算子工程编译阶段由npu_op_code_gen依据 OpDef 自动生成的;两级接口将"计算所需 workspace 查询"与"计算执行"分离,执行前必须按返回的workspaceSize分配 device 内存,workspaceSize == 0时workspaceDeviceMem传空指针即可。结果校验将 float16 转 float 后与 golden 比较,容差1e-3,通过则打印test pass。

5.3 构建与运行

运行前需先进入 custom_op 完成编译、打包与部署:

source ${install_path}/cann/set_env.sh # 默认安装路径 /usr/local/Ascend mkdir -p build && cd build python3 ../scripts/gen_data.py cmake .. && make -j ./execute_add_op

执行成功输出:

test pass

六、Aclop 单算子模型调用:aclop_invocation

6.1 原理与适用场景

单算子模型执行(aclop 方式)需要先用atc --singleop在离线模式生成单算子离线模型,并在应用中配置模型目录。示例基于自定义算子工程部署后,使用aclopExecuteV2接口执行单算子模型。核心流程:

  1. 通过aclCreateTensorDesc创建输入/输出张量描述,用aclCreateDataBuffer准备 device 侧数据缓冲;
  2. 调用aclopSetModelDir指定模型目录,然后调用aclopExecuteV2执行算子计算;
  3. 通过aclrtSynchronizeStream同步流,将 device 结果拷回 host 侧校验。

6.2 单算子描述文件

add_custom.json 是 ATC 模型转换的输入描述文件,固定描述算子AddCustom的输入输出(ND、float16、shape [8, 2048]):

[ { "op": "AddCustom", "input_desc": [ { "name": "x", "param_type": "required", "format": "ND", "shape": [8, 2048], "type": "float16" }, { "name": "y", "param_type": "required", "format": "ND", "shape": [8, 2048], "type": "float16" } ], "output_desc": [ { "name": "z", "param_type": "required", "format": "ND", "shape": [8, 2048], "type": "float16" } ] } ]

6.3 关键代码解析(main.cpp)

aclop_invocation/main.cpp 展示了 aclop 调用骨架:

#include "acl/acl_base.h" #include "acl/acl_rt.h" #include "acl/acl_mdl.h" #include "acl/acl_op.h" CHECK_ACL(aclInit(nullptr)); CHECK_ACL(aclrtSetDevice(deviceId)); CHECK_ACL(aclrtCreateStream(&stream)); CHECK_ACL(aclopSetModelDir(".")); // 指定单算子离线模型目录 const char* opType = "AddCustom"; // 1. 创建张量描述(ACL_FLOAT16, ACL_FORMAT_ND)与数据缓冲 auto* input0 = aclCreateTensorDesc(dataType, shape.size(), shape.data(), format); auto* input0Buffer = aclCreateDataBuffer(input0DeviceMem, bufferSize); // 2. 执行单算子模型 CHECK_ACL(aclopExecuteV2(opType, inputDesc.size(), inputDesc.data(), inputBuffers.data(), outputDesc.size(), outputDesc.data(), outputBuffers.data(), opAttr, stream)); // 3. 同步流并 D2H 校验 CHECK_ACL(aclrtSynchronizeStream(stream)); CHECK_ACL(aclrtMemcpy(output0HostData.data(), bufferSize, output0DeviceMem, bufferSize, ACL_MEMCPY_DEVICE_TO_HOST));

要点:aclopSetModelDir必须在执行前指定由 ATC 生成的单算子离线模型所在目录;aclopExecuteV2通过算子类型名opType与张量描述/数据缓冲数组完成调用,无需算子专属头文件。

6.4 构建与运行

source ${install_path}/cann/set_env.sh # 默认安装路径 /usr/local/Ascend mkdir -p build && cd build python3 ../scripts/gen_data.py # 使用 ATC 工具生成单算子离线模型 atc --singleop=../add_custom.json --output=. --soc_version=${soc_version} cmake .. && make -j ./execute_add_op

${soc_version}(AI 处理器型号)获取方式:

  • 对于 Atlas A2、Atlas 200I/500 A2 推理、Atlas 推理系列、Atlas 训练系列:在装有昇腾 AI 处理器的服务器上执行npu-smi info查询Name信息,实际配置值为 AscendName。例如Name为 xxxyy,则实际配置值为 Ascendxxxyy;
  • 对于 Ascend 950PR&950DT、Atlas A3:执行npu-smi info -t board -i <id> -c <chip_id>查询Chip Name与NPU Name,实际配置值为 Chip Name_NPU Name。例如Chip Name为 Ascendxxx、NPU Name为 1234,则实际配置值为 Ascendxxx_1234。其中id为设备 ID(由npu-smi info -l查询 NPU ID 获得),chip_id为芯片 ID(由npu-smi info -m查询 Chip ID 获得)。

同一系列的 AI 处理器型号创建的算子工程共享相同的基础功能(基于工程的算子开发、编译与部署)。执行成功输出:

test pass

七、Aclnn 与 Aclop 两条调用路径的对比与选型

对比维度aclnn 单算子 API(aclnn_invocation)aclop 单算子模型(aclop_invocation)
接口形态C 语言 API,aclnnAddCustomGetWorkspaceSize+aclnnAddCustom两级接口C 语言 API,aclopSetModelDir+aclopExecuteV2
离线模型不需要单算子描述文件,无需离线模型转换需先atc --singleop生成单算子离线模型,并在应用内配置模型目录
张量对象aclCreateTensor构造aclTensoraclCreateTensorDesc+aclCreateDataBuffer
接口来源aclnn_*.h由算子工程npu_op_code_gen自动生成通用acl/acl_op.h等 ACL 头文件
前置条件均需先完成 custom_op 的编译、打包与部署同左
适用场景固定 shape 算子快速验证,接口贴近算子定义、无需模型管理依赖 ATC 单算子模型,适合与离线模型链路统一的场景

从源码结构看,两条路径共享相同的底层设备侧算子二进制与 Tiling 产物,差异主要体现在 Host 侧调用封装:aclnn 面向自动生成的单算子接口、面向算子开发者直接调用;aclop 面向通用算子模型执行框架。开发者可按自身集成方式(直连算子 API 或模型化执行)二选一。

八、整体工作流串联与仓库导航

综合上述示例,一条完整的"自定义算子开发 → 部署 → 调用"链路为:

  1. 在op_kernel编写 Ascend C kernel(vector 计算 + 事件同步流水);
  2. 在op_host编写 OpDef 注册、InferShape/InferDataType 与 Tiling 函数(普通 Tiling / 模板 TilingKey / Tiling 下沉三种范式);
  3. 由 CMake 的npu_op_package系列接口完成 host 库、aclnn 自动接口、kernel 二进制与安装包的生成,运行custom_opp_*.run部署到 CANN;
  4. 编写调用侧程序,二选一使用aclnnAddCustomGetWorkspaceSize/aclnnAddCustom或aclopExecuteV2执行并校验结果。

建议深入阅读的仓库资源:

  • 示例入口:99_acl_based/README_en.md、00_acl_compilation/README_en.md、01_acl_invocation/README_en.md
  • 编译基础设施:cmake/asc/ascendc.cmake、cmake/asc/asc_modules/FindASC.cmake、cmake/asc/fwk_modules/device_task.cmake
  • 快速上手文档:docs/en/quick_start.md
  • Kernel 接口实现:impl/basic_api 下的kernel_operator_vec_*_impl.h(如AscendC::Add、Maxs/Mins/Muls对应实现)

说明:本文所有构建与运行步骤均需在已安装对应版本 CANN 开发套件的环境(source ${install_path}/cann/set_env.sh)中执行,${install_path}默认为/usr/local/Ascend。

  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。

项目地址:https://gitcode.com/cann/asc-devkit
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询