news 2026/9/18 12:30:14

CANN ops-math 算子 API 编译与运行实战:从 CMake 配置到 aclnn 调用排错

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN ops-math 算子 API 编译与运行实战:从 CMake 配置到 aclnn 调用排错

CANN ops-math 算子 API 编译与运行实战:从 CMake 配置到 aclnn 调用排错

【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math

本文围绕 ops-math(CANN 数学类算子库)的官方文档 编译与运行样例 展开,完整讲解如何通过 aclnn 两段式接口调用算子 API 的编译运行全流程:包括开发运行合设场景的环境准备、CMakeLists.txt 的逐项配置(含通算融合算子的 HCCL 线程库扩展)、source set_env.sh环境变量生效方式、cmake/make 编译命令、可执行文件运行与结果验证,以及使用aclGetRecentErrMsg获取报错细节的排错方法。读完本文,你可以独立完成任意数学类算子 API 样例工程的搭建、编译、运行与常见问题定位。

前提说明

如需编译执行算子 API,请确保基础环境已搭建完成,包括驱动、固件、CANN 软件包、ops 包等。本文档聚焦"如何编译并运行一个 aclnn 调用工程",因此不再赘述环境搭建细节;算子 API 的调用流程与运行操作,可对照华为官方文档《应用开发(C&C++)》中"单算子调用 > 单算子API执行 > 调用 aclnn 接口示例代码"章节理解整体调用逻辑。

ops-math 仓库本身也提供了大量可直接参考的调用样例工程。例如 add_example 提供了test_aclnn_add_example.cpp调用样例,其他数学类算子的调用逻辑、流程、编译脚本与该样例大致一致,实际开发时可按本文方法替换对应的 API 头文件与调用代码。

编译前准备

本文以开发和运行环境合设场景为例,即带 AI 处理器的机器既作为开发环境又作为运行环境,代码开发和代码运行在同一台机器上。这里以Abs 算子为例,其他算子的调用逻辑、流程、编译脚本与 Abs 算子大致一样,请根据实际情况自行修改 API 调用脚本(*.cpp)和编译脚本(CMakeLists)。

示例代码 test_abs.cpp

已知 Abs 算子功能是计算张量中每个元素的绝对值,计算公式为:

$$ y_i = |x_i| $$

可以从 aclnnAbs 文档中获取调用示例代码,并将代码文件命名为test_abs.cpp。结合本仓库中 test_aclnn_add_example.cpp 这一真实样例可以看到,一个标准的 aclnn 调用程序包含如下固定骨架:

  1. Init 初始化:依次调用aclInit(nullptr)aclrtSetDevice(deviceId)aclrtCreateStream(stream),完成 ACL 运行时初始化(参见该文件Init函数,第 60~70 行);
  2. 构造输入输出张量:通过aclrtMalloc申请 device 侧内存、aclrtMemcpy拷贝 host 数据,再用aclCreateTensor创建aclTensorCreateAclTensor函数,第 72~96 行)。注意连续张量的 strides 由 shape 反向累乘得到,格式指定为ACL_FORMAT_ND
  3. 调用两段式接口:先调用第一段接口xxxGetWorkspaceSize计算 workspace 大小,按workspaceSize申请 device 内存,再调用第二段接口执行计算(第 142~155 行);
  4. 同步与取回结果aclrtSynchronizeStream(stream)同步等待任务结束,aclrtMemcpy将结果从 device 拷回 host;
  5. 资源释放aclDestroyTensor释放张量、aclrtFree释放 device 内存、aclrtDestroyStream销毁流、aclrtResetDevice重置设备、aclFinalize去初始化。

这里涉及的两段式接口约定在文档 两段式接口 中有正式说明:

aclnnStatus aclxxXxxGetWorkspaceSize(const aclTensor *src, ..., aclTensor *out, ..., uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclxxXxx(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);

必须先调用第一段接口aclxxXxxGetWorkspaceSize计算本次 API 调用需要多少 workspace 内存,获取workspaceSize后按该大小申请 NPU 内存,然后调用第二段接口aclxxXxx执行计算。其中"aclxx"表示算子接口前缀(如 aclnn),"Xxx"表示算子名(如 Add)。需要特别注意两点约束:

  • workspace是指除输入/输出外,算子在 NPU 上完成计算所需的临时内存,workspaceSize表示其大小;
  • 第二段接口不能重复调用,同一executor上重复执行aclxxXxx(...)会出现异常。

test_abs.cpp中所有 API 调用都应使用CHECK_RET宏检查返回值,样例中该宏的标准写法(可对照 test_aclnn_add_example.cpp 第 21~31 行):

#define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0)

CMakeLists 文件

CMake 文件示例如下,请根据实际情况修改:

# Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. # CMake lowest version requirement cmake_minimum_required(VERSION 3.14) # 设置工程名 project(ACLNN_EXAMPLE) # Compile options add_compile_options(-std=c++11) # 设置编译选项 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "./bin") set(CMAKE_CXX_FLAGS_DEBUG "-fPIC -O0 -g -Wall") set(CMAKE_CXX_FLAGS_RELEASE "-fPIC -O2 -Wall") # 设置可执行文件名(如opapi_test),并指定待运行算子文件*.cpp所在目录 add_executable(opapi_test test_abs.cpp) # 设置ASCEND_PATH(CANN软件包目录,请根据实际路径修改)和INCLUDE_BASE_DIR(头文件目录) if(NOT "$ENV{ASCEND_CUSTOM_PATH}" STREQUAL "") set(ASCEND_PATH $ENV{ASCEND_CUSTOM_PATH}) else() set(ASCEND_PATH "/usr/local/Ascend/cann") endif() set(INCLUDE_BASE_DIR "${ASCEND_PATH}/include") include_directories( ${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}/aclnn ) # 设置链接的库文件路径 target_link_libraries(opapi_test PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi_math.so) # 可执行文件在CMakeLists文件所在目录的bin目录下 install(TARGETS opapi_test DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

逐项理解这个 CMake 的关键点:

  • CMake 版本要求:最低 3.14;编译选项为-std=c++11
  • 输出目录CMAKE_RUNTIME_OUTPUT_DIRECTORY设为./bin,即可执行文件统一落在 CMakeLists 所在目录下的bin子目录中(与后文"进入 bin 目录运行"的步骤呼应)。
  • ASCEND_PATH 的取值优先级:优先读取环境变量ASCEND_CUSTOM_PATH(CANN 软件包目录),未设置时回退到默认安装路径/usr/local/Ascend/cann。修改 CANN 安装位置时,设置ASCEND_CUSTOM_PATH比重改 CMakeLists 更灵活。
  • 头文件目录includeinclude/aclnn两个目录,前者提供acl/acl.h等 ACL 运行时头文件,后者提供aclnn_abs.h等算子 API 头文件。
  • 链接的三个库: | 库文件 | 作用 | | --- | --- | |libascendcl.so| ACL 运行时接口(设备/流/内存管理) | |libnnopbase.so| 算子 API 基础库(aclTensor、aclOpExecutor 等) | |libopapi_math.so| ops-math 数学类算子 API 库,aclnnAbs、aclnnAdd 等接口均在其中 |

仓库脚本 scripts/build_example.sh 中非 CMake 方式的等价 g++ 编译命令也印证了同一组链接依赖:

g++ ${source_file} \ -I ${INCLUDE_PATH} \ -I ${ACLNN_INCLUDE_PATH} \ -L ${EAGER_LIBRARY_PATH} \ -lopapi_math -lascendcl -lnnopbase \ -o ${executable_name}

从脚本结构看,仓库内部就是用-lopapi_math -lascendcl -lnnopbase这三个库编译所有 eager 模式调用样例的,与上文 CMakeLists 的链接配置完全一致。

通算融合算子(MC2)的额外配置

对于集合通信和 MatMul 计算融合、并行的算子,统称为通算融合算子(简称MC2 算子),包括AllGatherMatmulAlltoAllAllGatherBatchMatMulBatchMatMulReduceScatterAlltoAllMatmulAllReduceMatmulAllReduceAddRmsNormMatmulReduceScatter等。调用该类算子 API 时,一般会涉及多线程和 HCCL(Huawei Collective Communication Library,集合通信库),因此 CMake 文件需要额外导入如下内容,否则无法成功编译:

# 设置链接的库文件路径 find_package(Threads REQUIRED) target_link_libraries(opapi_test PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi_math.so ${ASCEND_PATH}/lib64/libhccl.so # 集合通信库文件 ${CMAKE_THREAD_LIBS_INIT}) # 多线程依赖的库文件

其中find_package(Threads REQUIRED)是 CMake 用于查找线程库的命令,可自动链接线程库依赖的头文件或间接依赖的库文件;${CMAKE_THREAD_LIBS_INIT}是该命令解析出的线程库链接变量,二者缺一不可。普通数学算子(Abs、Add 等)不涉及集合通信,无需链接libhccl.so与线程库。

编译与运行

步骤一:准备文件

提前准备好算子的调用代码(*.cpp,即test_abs.cpp)和编译脚本(CMakeLists.txt)。

步骤二:配置环境变量

安装 CANN 软件后,使用 CANN 运行用户登录环境,执行如下命令生效环境变量:

source ${INSTALL_DIR}/set_env.sh

其中${INSTALL_DIR}为 CANN 软件安装后文件存储路径,请根据实际情况替换。此步骤会设置好ASCEND_HOME_PATHLD_LIBRARY_PATH等运行时依赖变量,是编译与运行前的必要前置动作。

步骤三:编译并运行

1. 新建 build 目录:进入 CMakeLists.txt 所在目录,执行如下命令,新建 build 目录存放生成的编译文件:

mkdir -p build

2. cmake 配置 + make 编译:进入 build 目录,执行 cmake 命令编译,再执行 make 命令生成可执行文件:

cd build cmake ../ -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE make

编译成功后,会在 build 目录的bin 文件夹下生成opapi_test可执行文件(与 CMakeLists 中CMAKE_RUNTIME_OUTPUT_DIRECTORY "./bin"的设定一致)。

3. 运行可执行文件:进入 bin 目录,运行opapi_test

cd bin ./opapi_test

以 Abs 算子的运行结果为例,输入为[-1, -1, -1, 2, 2, 2, 3, 3]时,运行后的结果示例如下:

result[0] is: 1.000000 result[1] is: 1.000000 result[2] is: 1.000000 result[3] is: 2.000000 result[4] is: 2.000000 result[5] is: 2.000000 result[6] is: 3.000000 result[7] is: 3.000000

每个result[i]均等于对应输入元素的绝对值,符合y_i = |x_i|的计算公式,说明算子在 NPU 上计算正确。

报错排查:aclGetRecentErrMsg 与返回码

若执行结果报错、未出现预期结果,可以使用aclGetRecentErrMsg接口获取报错具体信息。该接口返回最近一次 API 调用的错误描述字符串,建议与返回码一并打印。

典型调用写法

调用aclnnAbsGetWorkspaceSize报错时获取异常信息的示例如下:

// self is nullptr ret = aclnnAbsGetWorkspaceSize(self, out, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAbsGetWorkspaceSize failed. ERROR: %d.\n[ERROR msg]%s", ret, aclGetRecentErrMsg()); return ret);

上述构造空指针问题时的实际报错输出示例如下:

aclnnAbsGetWorkspaceSize failed. ERROR: 161001 [ERROR msg][PID:xxxx] xxx(timestamp) AclNN_Parameter_Error(EZ1001): Expected a value of type [aclTensor] for argument [self] but instead found nullptr.

从这条报错可以读出三个排查要素:错误码161001、错误类别AclNN_Parameter_Error、以及明确指出是参数[self]收到了nullptr

常见返回码对照

结合文档 aclnn返回码,调用 aclnn API 时常见的接口返回码如下,遇到报错可先查表定位大类:

状态码名称状态码值状态码说明
ACLNN_SUCCESS0成功。
ACLNN_ERR_PARAM_NULLPTR161001参数校验错误,参数中存在非法的 nullptr(上文示例即此类)。
ACLNN_ERR_PARAM_INVALID161002参数校验错误,如输入的两个数据类型不满足输入类型推导关系。
ACLNN_ERR_RUNTIME_ERROR361001API 内部调用 npu runtime 的接口异常。
ACLNN_ERR_INNER_XXX561xxxAPI 内部发生异常。

对于 561xxx 系列内部异常码,同一文档给出了更细粒度的对照表,几个高频场景值得记住:

状态码名称状态码值状态码说明
ACLNN_ERR_INNER_INFERSHAPE_ERROR561001API 内部进行输出 shape 推导发生错误。
ACLNN_ERR_INNER_TILING_ERROR561002API 内部做 npu kernel 的 tiling 时发生异常。
ACLNN_ERR_INNER_FIND_KERNEL_ERROR561003查找 npu kernel 异常(可能因为算子二进制包未安装)。
ACLNN_ERR_INNER_OPP_PATH_NOT_FOUND561107没有检测到需要配置的环境变量ASCEND_OPP_PATH
ACLNN_ERR_INNER_OPP_KERNEL_PKG_NOT_FOUND561112没有加载到算子的二进制 kernel 库。

也就是说:如果第一段接口返回 561003/561107/561112 这类内部错误,排查方向通常是 ops 包安装不完整或ASCEND_OPP_PATH未正确配置,而不是调用代码本身的问题;而 161xxx 参数类错误则应检查张量构造(device 地址、shape、dtype、strides 是否合法)。

延伸阅读

  • 仓库 examples 目录提供 AI Core、AI Core C API、AI CPU 三类算子开发样例,其中 add_example/examples/test_aclnn_add_example.cpp 是最贴近本文 Abs 示例的完整 aclnn 调用工程;
  • scripts/build_example.sh 展示了仓库批量编译运行样例的脚本实现,eager 模式编译命令可直接作为本文 CMake 配置之外的快速验证手段;
  • 概念类文档 两段式接口 与 aclnn返回码 分别解释调用流程约定与完整错误码表,可与本文配套使用。

【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 12:27:53

深入理解LLVM与llvmpipe:从IR到256位SIMD的编译艺术

1. LLVM 项目到底是个什么东西如果你去 LLVM 官网,会看到一句话:The LLVM Project is a collection of modular and reusable compiler and toolchain technologies。翻译过来其实特别直白:LLVM 是一个模块化、可复用的编译器和工具链技术集合…

作者头像 李华
网站建设 2026/9/18 12:27:51

go2rtc统一接入多品牌摄像头:Docker部署与低延迟播放实践

1. 摄像头协议割裂的痛点,才是 go2rtc 真正擅长的事1.1 一个真实场景:三种摄像头,三套接入方式我先说一个让我彻底转向 go2rtc 的经历。前年帮一个做门店的朋友改造监控,他店里同时有海康的枪机、萤石的云台、还有一台米家的室内摄…

作者头像 李华
网站建设 2026/9/18 12:25:00

2024论文降重工具评测与使用技巧

1. 论文降重工具的市场现状论文查重和降重已经成为学术写作中不可或缺的环节。随着学术规范的日益严格,越来越多的学生和研究人员开始重视论文的原创性。根据我的观察,2023-2024学年,高校对论文重复率的要求普遍提高,很多院校将硕…

作者头像 李华
网站建设 2026/9/18 12:24:46

15万条Excel导出选型:POI、SXSSF、EasyExcel、CSV实测对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华