☰
CANN asc-devkit 算子调用实战:基于 aclnn 单算子 API 与 aclop 单算子模型的两种调用方式解析
2026/10/3 2:15:09 网站建设 项目流程
  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

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

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

导读

本文以 CANN asc-devkit 仓库中 01_acl_invocation 样例 为核心,系统讲解在完成自定义算子开发部署后,如何通过aclnn 单算子 API 执行(基于 C 语言的两段式接口,免离线模型转换)与aclop 单算子模型调用(基于atc --singleop生成的单算子离线模型)两种方式在 Host 侧驱动昇腾 AI 处理器执行自定义算子。读完本文,你将掌握aclnnAddCustomGetWorkspaceSize/aclnnAddCustom与aclopExecuteV2的完整调用流程、工程编译配置、单算子描述文件编写以及结果验证方法,可直接套用到自己的自定义算子验证场景。

背景:为什么要区分 aclnn 与 aclop 两种调用方式

在 CANN 生态中,算子开发完成后并不等于可以直接被上层框架使用,还需要在 Host 侧通过 ACL(Ascend Computing Language)运行时接口把算子"发下去"执行。面向不同的集成场景,CANN 提供了两种单算子执行路径,本样例的父级文档 99_acl_based 目录 将其归纳为:

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

其中 01_acl_invocation 正是本文的主体,它包含两个子样例:

目录名称功能描述支持的产品
aclnn_invocation介绍 Aclnn 算子调用(单算子 API 执行)的实现方法Ascend 950PR/Ascend 950DT、Atlas A3/A2 训练与推理系列产品、Atlas 200I/500 A2 推理产品、Atlas 推理系列产品
aclop_invocation介绍 Aclop 算子调用(单算子模型执行)的实现方法与 aclnn 样例相同

两个样例都以自定义的AddCustom算子(z = x + y,输入输出 shape 均为[8, 2048],float16,ND 格式)为对象,分别演示两种调用路径的完整代码,是理解单算子调用的最小可运行范本。

两种调用方式的原理差异

aclnn:单算子 API 执行(免离线模型转换)

单算子 API 执行是基于 C 语言 API 直接执行算子的方式:无需提供单算子描述文件、无需经过离线模型转换,只要自定义算子工程已完成编译、打包和部署,Host 程序即可直接链接算子侧生成的 op_api 库并调用对应的aclnnOpType接口。

以本样例为例,对应算子生成的接口是aclnnAddCustom,采用两段式调用:

  1. aclnnAddCustomGetWorkspaceSize:获取本次计算所需 workspace 大小,并在 Device 上申请对应内存;
  2. aclnnAddCustom:真正下发算子计算。

这种"先查 workspace、再执行"的模式是 aclnn 系列接口的统一约定:workspace 是算子执行过程中 Kernel 侧所需的临时存储空间,由框架根据算子实现动态计算,Host 侧需要按其返回值申请显存后传入。

aclop:单算子模型调用(离线模型驱动)

单算子模型调用走的是离线模型路径:在 Host 应用运行前,需要先用atc --singleop工具将单算子描述文件(add_custom.json)转换为单算子离线模型,然后在应用中通过aclopSetModelDir指定模型目录,再调用aclopExecuteV2执行。该路径不需要生成aclnnOpType形式的算子专用接口,而是以"算子类型名(OpType)+ 输入输出描述 + 数据 Buffer"的通用形式驱动模型执行。

两种方式的取舍可归纳为:aclnn 免模型转换、接口语义更贴近具体算子(参数即为张量),适合快速验证算子功能与算子侧自研集成;aclop 需要提前用 atc 生成模型,但接口更通用,适合以 OpType 为键的框架级调度场景。

前置条件:先编译、打包、部署自定义算子工程

两个调用样例的 README 都明确要求:运行前必须先进入 自定义算子工程样例 完成编译、打包和部署。该工程内同时提供AddCustom、AddCustomTemplate、AddCustomTilingSink、LeakyRelu四个算子实现,其中被本样例调用的AddCustom基于 Ascend C 的矢量计算接口Add实现,tiling 侧使用totalLength、tileNum两个参数控制数据切分。

部署命令(在该工程根目录下执行):

mkdir -p build && cd build cmake .. && make -j binary package ./custom_opp_*.run

执行结果出现SUCCESS即部署成功。部署产物会安装到ASCEND_OPP_PATH(默认${install_path}/cann/opp)下的vendors/customize目录,这正是后续 aclnn 样例链接cust_opapi库、aclop 样例查找模型文件时依赖的环境。

环境变量配置

根据 CANN 安装方式 配置环境变量:

source ${install_path}/cann/set_env.sh

说明:${install_path}为 CANN 包安装目录,未指定安装目录时默认安装至/usr/local/Ascend下。编译脚本中使用的ASCEND_HOME_PATH、ASCEND_OPP_PATH均由该脚本注入。

aclnn 样例详解:aclnnAddCustom 两段式调用

目录结构

aclnn_invocation/ ├── CMakeLists.txt // 编译工程文件 ├── main.cpp // 算子调用主程序 ├── README.md // 样例说明文档 └── scripts/ └── gen_data.py // 测试数据生成脚本

数据准备:gen_data.py

gen_data.py 使用 numpy 生成 shape 为(8, 2048)的测试数据:

  • input0.bin:全 1.1 的 float16 矩阵
  • input1.bin:全 2.2 的 float16 矩阵
  • golden.bin:(input0 + input1)的 float16 结果,作为验证基准

数据以二进制格式写入input/与output/目录,供主程序ReadFile读取。

主程序核心流程

aclnn_invocation/main.cpp 完整演示了三段式生命周期,可拆解为以下五步:

Step 1:初始化 ACL 环境

const int32_t deviceId = 0; aclrtStream stream = nullptr; CHECK_ACL(aclnnInit(nullptr)); // 初始化 aclnn(替代传统 aclInit) CHECK_ACL(aclrtSetDevice(deviceId)); // 指定设备 CHECK_ACL(aclrtCreateStream(&stream)); // 创建流

注意 aclnn 路径使用aclnnInit/aclnnFinalize而非aclInit/aclFinalize,这是 aclnn 接口族的使用约定。

Step 2:创建 aclTensor 并准备 Device 数据

先申请 Device 显存,再用aclCreateTensor将显存包装为带 shape、dtype、format 语义的张量描述:

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);

其中 shape 为{8, 2048}、bufferSize = 8 * 2048 * sizeof(aclFloat16),三个张量(input0、input1、output0)同构创建。随后通过aclrtMemcpy以ACL_MEMCPY_HOST_TO_DEVICE方向将 Host 数据拷入 Device:

CHECK_ACL(aclrtMemcpy(input0DeviceMem, bufferSize, input0HostData.data(), bufferSize, ACL_MEMCPY_HOST_TO_DEVICE));

Step 3:两段式调用——先查 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));

这段代码体现了 aclnn 两段式接口的标准用法:第一段返回workspaceSize(可能为 0,即算子无需额外 workspace)和复用的executor句柄;第二段把 workspace 显存与流传入执行。workspaceSize 为 0 时不能申请 0 字节显存,因此代码用if (workspaceSize > 0)做了保护。

Step 4:同步与结果回拷

CHECK_ACL(aclrtSynchronizeStream(stream)); // 等待算子异步执行完成 CHECK_ACL(aclrtMemcpy(output0HostData.data(), bufferSize, output0DeviceMem, bufferSize, ACL_MEMCPY_DEVICE_TO_HOST));

Step 5:结果校验与资源释放

程序打印前 10 个结果元素,并用VerifyResult与 golden 数据比对(float16 先转 float,容差1e-3F):

printf("\ntest %s\n", VerifyResult(output0HostData, goldenData) ? "pass" : "failed");

释放顺序为:aclDestroyTensor→aclrtFree(含 workspace)→aclrtDestroyStream→aclrtResetDevice→aclnnFinalize,与申请顺序严格对称。

编译配置:CMakeLists.txt

aclnn_invocation/CMakeLists.txt 的关键点在于:除 ACL 运行时头文件与库外,还要链接部署自定义算子包时生成的 op_api 产物:

target_include_directories(execute_add_op PRIVATE $ENV{ASCEND_HOME_PATH}/include $ENV{ASCEND_OPP_PATH}/vendors/customize/op_api/include ) target_link_directories(execute_add_op PRIVATE $ENV{ASCEND_HOME_PATH}/lib64 $ENV{ASCEND_OPP_PATH}/vendors/customize/op_api/lib ) target_link_libraries(execute_add_op PRIVATE cust_opapi // 自定义算子生成的 aclnn 接口库 nnopbase // 算子基础库 acl_rt // ACL 运行时 )

cust_opapi正是aclnnAddCustom等接口的载体——这也是为什么必须先部署自定义算子工程再编译本样例。

编译运行

mkdir -p build && cd build python3 ../scripts/gen_data.py # 生成 input/ 与 output/ 下的测试数据 cmake .. && make -j ./execute_add_op

执行结果:

result is: 3.3 3.3 3.3 3.3 3.3 3.3 3.3 3.3 3.3 3.3 test pass

(1.1 + 2.2 = 3.3,符合z = x + y预期。)

aclop 样例详解:aclopExecuteV2 单算子模型执行

目录结构

aclop_invocation/ ├── add_custom.json // 单算子描述文件(atc 模型转换输入) ├── CMakeLists.txt // 编译工程文件 ├── main.cpp // 算子调用主程序 ├── README.md // 样例说明文档 └── scripts/ └── gen_data.py // 测试数据生成脚本

与 aclnn 样例相比,多了一个add_custom.json,它是离线模型转换的输入描述文件。

单算子描述文件 add_custom.json

add_custom.json 声明了算子类型、输入输出描述:

[ { "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" } ] } ]

其中op必须与算子工程注册的 OpType(AddCustom)一致;input_desc/output_desc的name需与算子 Host 侧注册的输入输出名对应(本算子为 x、y、z),shape、type、format决定 atc 生成固定 shape 模型的输入输出规格。

主程序核心流程

aclop_invocation/main.cpp 使用经典的 ACL C 接口,流程如下:

Step 1:初始化并指定模型目录

CHECK_ACL(aclInit(nullptr)); // aclop 路径使用 aclInit/aclFinalize CHECK_ACL(aclrtSetDevice(deviceId)); CHECK_ACL(aclrtCreateStream(&stream)); CHECK_ACL(aclopSetModelDir(".")); // 指定 atc 生成的单算子模型所在目录

注意与 aclnn 样例的区别:这里调用aclInit,且执行前必须aclopSetModelDir指向模型目录(本例模型输出到.,即 build 目录)。

Step 2:用 TensorDesc + DataBuffer 描述输入输出

与 aclnn 的aclTensor不同,aclop 路径使用"描述(aclCreateTensorDesc)+ 数据(aclCreateDataBuffer)"分离的模型:

const char* opType = "AddCustom"; auto* input0 = aclCreateTensorDesc(dataType, shape.size(), shape.data(), format); // input1、output0 同理,format 为 ACL_FORMAT_ND,dataType 为 ACL_FLOAT16 void* input0DeviceMem = nullptr; CHECK_ACL(aclrtMalloc(&input0DeviceMem, bufferSize, ACL_MEM_MALLOC_HUGE_FIRST)); auto* input0Buffer = aclCreateDataBuffer(input0DeviceMem, bufferSize);

TensorDesc 放入inputDesc/outputDesc两个std::vector,DataBuffer 放入inputBuffers/outputBuffers,保持下标一一对应。

Step 3:执行 aclopExecuteV2

CHECK_ACL(aclopExecuteV2(opType, inputDesc.size(), inputDesc.data(), inputBuffers.data(), outputDesc.size(), outputDesc.data(), outputBuffers.data(), opAttr, stream));

opAttr由aclopCreateAttr()创建(本算子无属性,传入空属性即可),opType字符串即单算子描述文件中的"op"值,ACL 依据该 OpType 在模型目录中查找对应模型并执行。

Step 4:同步、回拷与校验

CHECK_ACL(aclrtSynchronizeStream(stream)); CHECK_ACL(aclrtMemcpy(output0HostData.data(), bufferSize, output0DeviceMem, bufferSize, ACL_MEMCPY_DEVICE_TO_HOST));

校验逻辑与 aclnn 样例相同(容差1e-3F的逐元素比对),释放资源时额外调用aclDestroyDataBuffer、aclDestroyTensorDesc、aclopDestroyAttr。

编译配置:CMakeLists.txt

aclop_invocation/CMakeLists.txt 不需要链接cust_opapi,只需 ACL 基础库:

target_link_libraries(execute_add_op PRIVATE acl_op_executor // aclop 单算子模型执行库 acl_mdl // 模型加载/执行库 acl_rt // ACL 运行时 )

这从工程层面印证了两条路径的本质差异:aclnn 依赖算子工程产物cust_opapi,aclop 依赖通用模型执行库。

编译运行:先 atc 生成单算子离线模型

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 推理系列产品:在安装昇腾 AI 处理器的服务器执行npu-smi info查询,取Name信息,实际配置值为 AscendName(例如Name取值为 xxxyy,实际配置值为 Ascendxxxyy);
  • 针对Ascend 950PR/Ascend 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 处理器型号创建的算子工程,其基础功能(算子开发、编译和部署)通用。atc命令在 CANN 包安装目录(默认/usr/local/Ascend)的cann/atc/bin下,环境变量配置后可直接使用。

执行结果与 aclnn 样例一致,出现test pass即验证通过。

两种调用方式对比与选型建议

对比维度aclnn 单算子 API 执行aclop 单算子模型执行
前置步骤仅需部署自定义算子包部署算子包 +atc --singleop生成离线模型
模型转换不需要需要(输入add_custom.json)
关键接口aclnnOpTypeGetWorkspaceSize/aclnnOpType(两段式)aclopSetModelDir/aclopExecuteV2
数据对象aclTensor(描述与数据一体)aclTensorDesc+aclDataBuffer(分离)
初始化aclnnInit/aclnnFinalizeaclInit/aclFinalize
链接依赖cust_opapi+nnopbase+acl_rtacl_op_executor+acl_mdl+acl_rt
适用场景算子功能快速验证、算子侧集成框架级以 OpType 为键的通用调度

选择建议:如果你的目标是验证刚开发的自定义算子功能是否正确,aclnn 路径更直接——部署算子包后即可调用,且接口参数即张量,错误定位直观;如果你需要模拟真实推理/训练框架加载算子模型的路径,或在同一应用中按 OpType 动态调度多个算子,则 aclop 路径更贴近生产形态。

延伸阅读

  • 算子调用样例总览:99_acl_based/README.md
  • 前置的自定义算子工程编译、打包、部署流程:00_acl_compilation/custom_op/README.md
  • CANN 开发套件包安装与环境准备:docs/zh/quick_start.md
  • 若需以 git 方式获取源码,可执行git clone https://gitcode.com/cann/asc-devkit后进入examples/01_simd_cpp_api/02_features/99_acl_based/01_acl_invocation目录复现本文全部步骤。
  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

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

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

相关推荐

上一篇:如何快速下载网页视频资源:猫抓浏览器扩展完整使用指南
下一篇:Lenovo Legion Toolkit:深度自定义联想笔记本性能控制的终极解决方案

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

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

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

立即咨询