CANN ops-math 算子调用实战:快速体验、aclnn API 与 GE 图模式全解析
【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math
本文围绕 CANN ops-math 算子库的官方调用指南,系统讲解在 NPU 环境下调用内置算子与自定义算子的完整方案:既可以通过项目自带
build.sh一行命令快速体验算子样例,也可以自行搭建 CMake 调用工程,将算子以 PyTorch API、aclnn C API 或 GE 图模式集成进实际业务。读完本文,你将掌握三种调用方式的选型原则、完整编译运行步骤以及底层两段式接口与图构建原理,可直接上手验证abs、add_example等算子。
使用须知
调用前提
调用算子前,请先参考本项目 README 完成环境准备和源码下载,这里不再赘述。环境准备的核心要求如下:
- 已安装 CANN-toolkit 包(运行时依赖
libascendcl.so、libnnopbase.so、libopapi_math.so等动态库均来自该包); - 已编译好对应的算子包(自定义算子包或 ops-math 整包);
- 已获取与本项目版本配套的分支源码,通用下载命令为
git clone -b ${tag_version} https://gitcode.com/cann/ops-math.git,${tag_version}替换为分支标签名,具体对应关系参见 README。
调用范围
支持调用的内置算子清单参见算子列表。该清单以表格形式列出了每个算子的分类、目录、算子实现(op_kernel/op_host)、调用支持情况(op_api 对应 aclnn 调用、op_graph 对应图模式调用)以及算子执行硬件单元(AI Core 或 AI CPU)。此外,还支持调用自定义算子,例如 experimental 贡献目录下由开发者贡献的算子。
提示:部分算子存在多个 V 版本(如 pad_v2、pad_v3),使用时选择最高 V 版本即可,高版本算子已兼容低版本算子的所有能力,详见 算子列表 的使用说明。
调用场景选型
请根据实际场景诉求选择合适的算子调用方案,官方提供两套场景:
| 调用场景 | 场景说明 | 特点 |
|---|---|---|
| 快速调用算子 | 适用于快速体验和验证算子功能的场景。 | 无需搭建调用工程,基于源码编译包和项目脚本build.sh可直接调用算子样例。 |
| 业务应用集成算子 | 适用于将算子灵活集成到实际业务应用中。 | 需自行搭建调用工程,手动创建调用脚本/CMake 工程,灵活实现算子编译和运行。 |
调用方式选型
当前主要提供如下算子调用方式,请按需选择:
| 调用方式 | 说明 |
|---|---|
| PyTorch API | 将算子 Kernel 注册到 PyTorch 原生框架,以类似于 Torch 原生 API 方式实现算子调用。 |
| aclnn API | 针对算子提供相应的 C 语言 API(前缀为 aclnn),无需提供 IR 定义,实现 API 直接调用算子。 |
| GE 图模式 | 通过算子 IR(Intermediate Representation)定义,以构图方式实现算子调用。 |
其中 aclnn API 对应的全量接口清单参见 aclnn 列表;GE 图模式依赖算子 op_graph 目录下的 IR 定义(如 add_example_proto.h)。
快速调用算子
如需快速体验或验证项目中已有算子功能,可参考本章最简调用方法,通过build.sh执行算子样例。
该方法的优点是无需搭建调用工程(即无需手动创建编译/运行脚本),简单易操作,适合快速验证算子功能正确性。
说明:对于 Ascend 950PR 产品,可通过 Simulator 仿真工具执行算子样例,详见仿真指导。
步骤1:环境准备
在调用算子前,请先确保您的环境已安装 CANN-toolkit 包和编译好的算子包。
步骤2:执行项目中已有算子的样例
build.sh支持三种算子包形态,命令格式与参数略有差异,分别介绍如下。
基于自定义算子包执行算子样例
自定义算子包(cust 模式)适用于调用experimental等自定义/贡献算子,命令如下:
bash build.sh --run_example ${op} ${mode} ${pkg_mode} [--vendor_name=${vendor_name}] [--soc=${soc_version}] [--experimental] # 以Abs算子example执行为例 # bash build.sh --run_example abs eager cust --vendor_name=custom # 以Abs算子experimental执行为例 # bash build.sh --experimental --run_example abs eager cust --vendor_name=custom参数说明:
${op}:表示待执行算子,算子名小写下划线形式,如abs;${mode}:表示调用方式,目前支持eager(aclnn 调用)、graph(图模式调用);${pkg_mode}:表示包模式,目前仅支持cust,即自定义算子包;${vendor_name}(可选):与构建的自定义算子包设置一致,默认名为custom;${soc_version}(可选):表示 NPU 型号;${experimental}(可选):表示执行用户保存在 experimental 贡献目录下的算子。
说明:
${mode}为graph时,不指定${pkg_mode}和${vendor_name}。
基于 ops-math 包执行算子样例
对于仓库内置的标准算子(如 math/abs),直接基于 ops-math 整包执行即可,命令如下:
bash build.sh --run_example ${op} ${mode} [--soc=${soc_version}] # 以Abs算子example执行为例 # bash build.sh --run_example abs eager参数说明:
${op}:表示待执行算子,算子名小写下划线形式,如abs;${mode}:表示算子执行模式,目前支持eager(aclnn 调用)、graph(图模式调用);${soc_version}(可选):表示 NPU 型号。
基于 ops-math 静态库执行算子样例
当环境无法加载动态库、需要将算子链接进独立可执行程序时,可使用静态库方式,共三步:
1. 前提条件
ops-math 静态库依赖于 ops-legacy 静态库,将上述静态库准备好,解压并将所有lib64、include目录移动至统一目录${static_lib_path}下。
说明:ops-legacy 静态库
cann-${soc_name}-ops-legacy-static_${cann_version}_linux-${arch}.tar.gz可通过 CANN 发布渠道获取(如 Ascend 官方软件下载站);ops-math 静态库暂未提供软件包,请通过本地编译生成(build.sh支持--static相关选项构建静态库,参见 scripts/build_lib.sh)。
2. 创建 run.sh
在待执行算子examples/test_aclnn_${op_name}.cpp同级目录下创建run.sh文件。以 Abs 算子执行test_aclnn_abs.cpp为例,示例如下:
# 静态库文件路径 static_lib_path="" # 环境变量生效 if [ -n "$ASCEND_INSTALL_PATH" ]; then _ASCEND_INSTALL_PATH=$ASCEND_INSTALL_PATH elif [ -n "$ASCEND_HOME_PATH" ]; then _ASCEND_INSTALL_PATH=$ASCEND_HOME_PATH else _ASCEND_INSTALL_PATH="/usr/local/Ascend/cann" fi source ${_ASCEND_INSTALL_PATH}/bin/setenv.bash # 编译可执行文件 g++ test_aclnn_abs.cpp \ -I ${static_lib_path}/include \ -L ${static_lib_path}/lib64 \ -I ${_ASCEND_INSTALL_PATH}/include \ -I ${_ASCEND_INSTALL_PATH}/include/aclnnop \ -L ${_ASCEND_INSTALL_PATH}/lib64 \ -Wl,--allow-multiple-definition \ -Wl,--start-group -lcann_math_static -lcann_legacy_static -Wl,--end-group -lgraph -lgraph_base \ -lpthread -lmmpa -lmetadef -lascendalog -lregister -lopp_registry -lops_base -lascendcl -ltiling_api -lplatform \ -ldl -lc_sec -lnnopbase -lruntime -lerror_manager -lunified_dlog \ -o test_aclnn_abs # 替换为实际算子可执行文件名 # 执行程序 ./test_aclnn_abs脚本关键点说明:
${static_lib_path}表示静态库统一放置路径;${ASCEND_INSTALL_PATH}已通过环境变量配置,表示 CANN toolkit 包安装路径;最终可执行文件名请替换为实际算子可执行文件名;-lcann_math_static、-lcann_legacy_static表示算子依赖的静态库文件,从静态库统一放置路径${static_lib_path}中获取;-lgraph、-lmetadef等表示算子依赖的底层库文件,可在 CANN toolkit 包获取;-Wl,--allow-multiple-definition用于允许符号重定义,--start-group/--end-group用于解决静态库之间的循环依赖。
3. 执行 run.sh
bash run.sh步骤3:检查执行结果
算子样例执行后会打印结果,以 Abs 算子结果为例:
abs result[0] is: 1.000000 abs result[1] is: 1.000000 abs result[2] is: 1.000000 abs result[3] is: 2.000000 abs result[4] is: 2.000000 abs result[5] is: 2.000000 abs result[6] is: 3.000000 abs result[7] is: 3.000000result[i]即输入张量第i个元素取绝对值后的结果,可与 math/abs/README.md 中描述的算子功能对照验证。
build.sh 快速调用的底层实现
从源码看,--run_example功能由 scripts/build_example.sh 中的build_example()实现,它接收主脚本 build.sh 解析出的EXAMPLE_NAME、EXAMPLE_MODE参数,逻辑如下:
- 根据
${mode}决定样例文件匹配模式与可执行文件名:eager对应test_aclnn_*.cpp、可执行文件test_aclnn_${EXAMPLE_NAME};graph对应test_geir_*.cpp、可执行文件test_geir_${EXAMPLE_NAME}; - 在项目根目录(或
--experimental指定的 experimental 目录)下按${EXAMPLE_NAME}/examples/*路径查找样例源码,即每个算子的调用样例统一存放在其目录的examples子目录下; - eager 模式编译时,根据包模式选择链接方式:默认模式链接
-lopapi_math(内置算子库),cust 模式则通过ASCEND_CUSTOM_OPP_PATH或${ASCEND_HOME_PATH}/opp/vendors/${VENDOR}_math/op_api定位自定义算子包并链接-lcust_opapi; - 当
--soc=ascend950时,查找路径会额外包含${EXAMPLE_NAME}/examples/arch35/架构目录,并在指定--soc时通过get_simulator_chip_version()映射到仿真芯片版本(如ascend950→dav_3510、ascend910b→dav_2201),进而链接 Simulator 仿真库libruntime_camodel.so与libnpu_drv_camodel.so,这也是 Ascend 950PR 可通过 Simulator 执行样例的实现基础。
业务应用集成算子
如需将算子集成到实际业务应用中,可参考本章自行搭建调用工程。通过自定义调用脚本/CMake 工程等,实现算子编译和运行。
该方法的优点是手动搭建调用工程,场景灵活度高,可移植性强。不同调用方式对应的编译工程不同,当前支持 PyTorch API、aclnn API、GE 图模式三种调用方式,请按需选择。
PyTorch API
该方式将算子 Kernel 注册到 PyTorch 原生框架,使其可以像原生 Torch API 一样被直接调用。具体调用原理和过程请参考 examples/fast_kernel_launch_example。
从仓库结构看,examples/fast_kernel_launch_example 提供了算子快速内核启动的完整示例工程,包含.asc内核源码、.cmake构建配置与 Python 调用脚本,展示了通过 torch 扩展将 CANN 算子桥接到 PyTorch 前端的完整链路。此外,仓库中算子目录的op_api子目录(如 math/abs/op_api)即为算子对外暴露的 aclnn 与 torch 接口封装实现。
aclnn API
调用流程
为方便调用算子,Host 侧提供算子对应的 C 语言 API(即以 aclnn 为前缀的 API)实现算子调用,无需提供算子 IR(Intermediate Representation)定义。aclnn API 调用流程如下:
整个调用链为:调用方(业务应用/框架)→ aclnn 接口(GetWorkspaceSize 两段式)→ 算子库(opapi)→ 底层 runtime 驱动 → NPU 执行。aclnn 接口的返回类型为aclnnStatus,常用返回码含义可参考 aclnn 返回码说明。
两段式接口原理
所有 aclnn 单算子 API 均遵循"两段式"接口形态(详见 两段式接口):
aclnnStatus aclxxXxxGetWorkspaceSize(const aclTensor *src, ..., aclTensor *out, ..., uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclxxXxx(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);- 第一段接口
aclnnXxxGetWorkspaceSize:计算本次 API 调用过程中需要多少 workspace 临时内存,并返回workspaceSize与executor; - 拿到
workspaceSize后,调用方需按此大小通过aclrtMalloc申请 NPU 内存; - 第二段接口
aclnnXxx:真正下发计算任务。注意第二段接口不能重复调用,重复调用会出现异常,需重新走第一段接口获取 executor。
workspace 是指除输入/输出外,算子在 NPU 上完成计算所需要的临时内存,
workspaceSize表示临时内存的大小。
编译运行
注意:操作过程中,如遇到日志提示设置环境变量,请按提示操作。
1. 环境准备
在编译运行前,请先确保您的环境已安装 CANN-toolkit 包和编译好的算子包。
2. 创建调用脚本
在环境任意目录下,新建调用 cpp 脚本,命名自定义(例如${test_aclnn_op_name}.cpp)。为方便理解,以AddExample算子为例,调用脚本如下,仅供参考,全量代码参见 test_aclnn_add_example.cpp。
int main() { // 1.调用acl进行device/stream初始化 int32_t deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, &stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret); // 2.构造输入与输出,需要根据API的接口自定义构造 aclTensor* selfX = nullptr; void* selfXDeviceAddr = nullptr; std::vector<int64_t> selfXShape = {32, 4, 4, 4}; std::vector<float> selfXHostData(2048, 1); ret = CreateAclTensor(selfXHostData, selfXShape, &selfXDeviceAddr, aclDataType::ACL_FLOAT, &selfX); CHECK_RET(ret == ACL_SUCCESS, return ret); aclTensor* selfY = nullptr; void* selfYDeviceAddr = nullptr; std::vector<int64_t> selfYShape = {32, 4, 4, 4}; std::vector<float> selfYHostData(2048, 1); ret = CreateAclTensor(selfYHostData, selfYShape, &selfYDeviceAddr, aclDataType::ACL_FLOAT, &selfY); CHECK_RET(ret == ACL_SUCCESS, return ret); aclTensor* out = nullptr; void* outDeviceAddr = nullptr; std::vector<int64_t> outShape = {32, 4, 4, 4}; std::vector<float> outHostData(2048, 1); ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out); CHECK_RET(ret == ACL_SUCCESS, return ret); // 3.调用CANN算子库API,需要修改为具体的API名称 uint64_t workspaceSize = 0; aclOpExecutor* executor; // 4.调用aclnnAddExample第一段接口 ret = aclnnAddExampleGetWorkspaceSize(selfX, selfY, out, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAddExampleGetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr = nullptr; if (workspaceSize > static_cast<uint64_t>(0)) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } // 5.调用aclnnAddExample第二段接口 ret = aclnnAddExample(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAddExample failed. ERROR: %d\n", ret); return ret); // 6.(固定写法)同步等待任务执行结束 ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); // 7.获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改 PrintOutResult(outShape, &outDeviceAddr); // 8.释放aclTensor,需要根据具体API的接口定义修改 aclDestroyTensor(selfX); aclDestroyTensor(selfY); aclDestroyTensor(out); // 9.释放device资源 aclrtFree(selfXDeviceAddr); aclrtFree(selfYDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize > static_cast<uint64_t>(0)) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); // 10. acl去初始化 aclFinalize(); return 0; }上述脚本是 aclnn 调用的标准骨架,可归纳为十个步骤:acl 初始化 → 构造aclTensor输入输出 → 调用两段式接口(GetWorkspaceSize+ 执行接口)→ 同步等待 → 回拷结果 → 释放 tensor 与 device 资源 → acl 去初始化。从 test_aclnn_add_example.cpp 源码可以看到,CreateAclTensor内部依次完成aclrtMalloc申请 device 内存、aclrtMemcpy将 host 数据拷入 device、根据 shape 计算连续 strides、最后调用aclCreateTensor创建张量描述;Init则完成aclInit、aclrtSetDevice、aclrtCreateStream三步初始化。
3. 创建 CMakeLists.txt 文件
在${test_aclnn_op_name}.cpp同级目录下创建CMakeLists.txt文件。需注意的是,调用自定义算子(如 experimental 目录)和标准项目算子(内置算子)时编译脚本有差异,示例如下,仅供参考,请根据实际情况自行修改。
调用自定义算子(依赖自定义算子包):
cmake_minimum_required(VERSION 3.14) # 设置工程名 project(ACLNN_EXAMPLE) # 设置C++编译标准 add_compile_options(-std=c++11) # 设置编译输出目录为当前目录下的bin文件夹 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "./bin") # 设置调试和发布模式的编译选项 set(CMAKE_CXX_FLAGS_DEBUG "-fPIC -O0 -g -Wall") set(CMAKE_CXX_FLAGS_RELEASE "-fPIC -O2 -Wall") # 添加可执行文件(自定义:替换为实际调用算子的*.cpp文件) add_executable(${test_aclnn_op_name} ${test_aclnn_op_name}.cpp) # ASCEND_PATH(如遇CANN包路径有误,请根据实际路径修改) if(NOT "$ENV{ASCEND_HOME_PATH}" STREQUAL "") set(ASCEND_PATH $ENV{ASCEND_HOME_PATH}) else() set(ASCEND_PATH "/usr/local/Ascend/cann") endif() # 获取自定义算子包名称,存在多个自定义算子包时,只会使用其中一个 set(VENDORS_DIR "${ASCEND_PATH}/opp/vendors") file(GLOB CUSTOM_DIRS "${VENDORS_DIR}/*") foreach(CUSTOM_DIR ${CUSTOM_DIRS}) if(IS_DIRECTORY ${CUSTOM_DIR}) set(TARGET_SUBDIR ${CUSTOM_DIR}) endif() endforeach() if(NOT DEFINED TARGET_SUBDIR) message(FATAL_ERROR "在路径${ASCEND_PATH}中未找到自定义算子包") endif() # 设置头文件路径 set(INCLUDE_BASE_DIR "${ASCEND_PATH}/include") include_directories( ${INCLUDE_BASE_DIR} ${TARGET_SUBDIR}/op_api/include ) include_directories( ${INCLUDE_BASE_DIR} ) # 链接所需的动态库(自定义:替换为实际算子可执行文件) target_link_libraries(${test_aclnn_op_name} PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${TARGET_SUBDIR}/op_api/lib/libcust_opapi.so # 链接自定义算子库文件 ) target_link_options(${test_aclnn_op_name} PRIVATE "-Wl,-rpath,${TARGET_SUBDIR}/op_api/lib" ) # 安装目标文件到bin目录(自定义:替换为实际算子可执行文件) install(TARGETS ${test_aclnn_op_name} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})调用标准算子(内置算子)(依赖 ops-math 整包):
cmake_minimum_required(VERSION 3.14) # 设置工程名 project(ACLNN_EXAMPLE) # 设置C++编译标准 add_compile_options(-std=c++11) # 设置编译输出目录为当前目录下的bin文件夹 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "./bin") # 设置调试和发布模式的编译选项 set(CMAKE_CXX_FLAGS_DEBUG "-fPIC -O0 -g -Wall") set(CMAKE_CXX_FLAGS_RELEASE "-fPIC -O2 -Wall") # 添加可执行文件(自定义:替换为实际调用算子的*.cpp文件) add_executable(${test_aclnn_op_name} ${test_aclnn_op_name}.cpp) # ASCEND_PATH(如遇CANN包路径有误,请根据实际路径修改) if(NOT "$ENV{ASCEND_HOME_PATH}" STREQUAL "") set(ASCEND_PATH $ENV{ASCEND_HOME_PATH}) else() set(ASCEND_PATH "/usr/local/Ascend/cann") endif() # 设置头文件路径 set(INCLUDE_BASE_DIR "${ASCEND_PATH}/include") include_directories( ${INCLUDE_BASE_DIR} ${ASCEND_PATH}/include/aclnnop ) # 链接所需的动态库(自定义:替换为实际算子可执行文件) target_link_libraries(${test_aclnn_op_name} PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi_math.so # 链接内置算子库文件 ) # 安装目标文件到bin目录(自定义:替换为实际算子可执行文件) install(TARGETS ${test_aclnn_op_name} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})两种 CMake 工程的核心差异在于库的链接目标:自定义算子包链接${TARGET_SUBDIR}/op_api/lib/libcust_opapi.so并从opp/vendors下按目录扫描定位自定义算子包(存在多个包时只取其一);内置算子则直接链接${ASCEND_PATH}/lib64/libopapi_math.so,且需把头文件目录${ASCEND_PATH}/include/aclnnop加入 include 路径以获取 aclnn 接口声明。此外,请留意 aclnn 接口的返回码判读(aclnnStatus各类返回码说明见 aclnn 返回码说明)。
4. 创建 run.sh 文件
在${test_aclnn_op_name}.cpp同级目录下创建run.sh文件,以AddExample算子为例,示例如下,请根据实际情况自行修改。
if [ -n "$ASCEND_INSTALL_PATH" ]; then # 实际CANN包安装路径 _ASCEND_INSTALL_PATH=$ASCEND_INSTALL_PATH elif [ -n "$ASCEND_HOME_PATH" ]; then _ASCEND_INSTALL_PATH=$ASCEND_HOME_PATH else _ASCEND_INSTALL_PATH="/usr/local/Ascend/cann" fi source ${_ASCEND_INSTALL_PATH}/bin/setenv.bash rm -rf build mkdir -p build cd build cmake ../ -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE # 执行构建命令 make cd bin ./${test_aclnn_op_name} # 替换为实际算子可执行文件名5. 运行 run.sh 文件
在 run.sh 文件所在路径执行如下命令:
bash run.sh默认在当前执行路径/build/bin下生成可执行文件${test_aclnn_op_name}。运行结果以test_aclnn_add_example为例:
Print the first 10 groups of data: add_example first input[0] is: 1.000000, second input[0] is: -257.000000, result[0] is: -256.000000 add_example first input[1] is: 2.000000, second input[1] is: 608.000000, result[1] is: 610.000000 ...以上为当前样例使用固定随机数种子42时的示例输出,result[i]为first input[i]与second input[i]逐元素相加的结果,对应 AddExample 算子说明 中的计算公式y = x1 + x2(x1、x2 支持 FLOAT、INT32 类型,仅支持 4 维 ND 格式输入输出)。从样例源码可见,first input通过std::iota从 1.0 递增填充,second input通过std::mt19937以种子 42 生成[-1024, 1024]范围内的随机整数,因此输出具有确定性,可用于回归验证。
GE 图模式
调用流程
该方式基于算子 GE IR(Intermediate Representation)定义,以构图方式调用算子,调用流程如下:
与 aclnn 直调不同,GE 图模式需要先在 Host 侧构建计算图(Graph),通过GEInitialize初始化图引擎,再经Session将图下发给 GE 编译器完成算子编译与执行。算子自身的 IR 定义(即 op_graph 目录下的 proto 定义,如 add_example_proto.h)为构图提供了算子原型信息。
编译运行
注意:操作过程中,如遇到日志提示设置环境变量,请按提示操作。
1. 环境准备
在编译运行前,请先确保您的环境已安装 CANN-toolkit 包和编译好的算子包。
2. 创建调用脚本
在目标算子examples目录下,新建调用脚本test_geir_${op_name}.cpp,${op_name}表示目标算子名。以AddExample算子为例,调用脚本如下,仅供参考,全量代码参见 test_geir_add_example.cpp。
int main() { // 1.创建图对象 Graph graph(graphName); // 2.图全局编译选项初始化 Status ret = ge::GEInitialize(globalOptions); // 3.创建AddExample算子实例 auto add1 = op::AddExample("add1"); // 4.定义图输入输出向量 std::vector<Operator> inputs{}; std::vector<Operator> outputs{}; // 5.准备输入数据 std::vector<int64_t> xShape = {32,4,4,4}; // 宏展开方式处理变量赋值 ADD_INPUT(1, x1, inDtype, xShape); ADD_INPUT(2, x2, inDtype, xShape); ADD_OUTPUT(1, y, inDtype, xShape); outputs.push_back(add1); // 6.设置图对象的输入算子和输出算子 graph.SetInputs(inputs).SetOutputs(outputs); // 7.创建session对象 ge::Session* session = new Session(buildOptions); // 8. session添加图 ret = session->AddGraph(graphId, graph, graphOptions); // 9.运行图 ret = session->RunGraph(graphId, input, output); // 10.释放资源 GEFinalize(); return 0; }从 test_geir_add_example.cpp 源码可以看到构图细节:
- 全局编译选项
global_options示例为{{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}},其中ge.graphRunMode=1表示在线推理运行模式; ADD_INPUT宏内部创建op::Data占位算子,设置TensorDesc(ND 格式、指定 dtype、kPlacementHost),调用GenOnesData生成全 2 输入数据,并通过graph.AddOp将占位算子加入图、add1.set_input_x1(...)建立输入连接;ADD_OUTPUT宏通过add1.update_output_desc_y(...)声明输出张量描述;- 图运行结束后,脚本会将输入输出 tensor 落盘为
tc_ge_irrun_test_0008_npu_input_*.bin/npu_output_*.bin文件,便于离线比对数据,并调用aclgrphDumpGraph将图结构 dump 到./dump目录; - 通过
GEGetErrorMsgV2()/GEGetWarningMsgV2()获取图引擎的错误与告警信息,辅助定位问题。
3. 创建 CMakeLists.txt 文件
在test_geir_${op_name}.cpp同级目录下创建CMakeLists.txt文件,以AddExample算子为例,示例如下,请根据实际情况自行修改。
cmake_minimum_required(VERSION 3.14) # 设置工程名 project(GE_IR_EXAMPLE) if(NOT "$ENV{ASCEND_OPP_PATH}" STREQUAL "") get_filename_component(ASCEND_PATH $ENV{ASCEND_OPP_PATH} DIRECTORY) elseif(NOT "$ENV{ASCEND_HOME_PATH}" STREQUAL "") set(ASCEND_PATH $ENV{ASCEND_HOME_PATH}) else() set(ASCEND_PATH "/usr/local/Ascend/cann") endif() set(FWK_INCLUDE_DIR "${ASCEND_PATH}/compiler/include") message(STATUS "ASCEND_PATH: ${ASCEND_PATH}") file(GLOB files CONFIGURE_DEPENDS test_geir_add_example.cpp ) # 添加可执行文件(请替换为实际算子可执行文件) add_executable(test_geir_add_example ${files}) find_library(GRAPH_LIBRARY_DIR libgraph.so "${ASCEND_PATH}/compiler/lib64/stub") find_library(GE_RUNNER_LIBRARY_DIR libge_runner.so "${ASCEND_PATH}/compiler/lib64/stub") find_library(GRAPH_BASE_LIBRARY_DIR libgraph_base.so "${ASCEND_PATH}/compiler/lib64") find_library(GE_COMPILER_LIBRARY_DIR libge_compiler.so "${ASCEND_PATH}/compiler/lib64") # 链接所需的动态库 target_link_libraries(test_geir_add_example PRIVATE ${GRAPH_LIBRARY_DIR} ${GE_RUNNER_LIBRARY_DIR} ${GRAPH_BASE_LIBRARY_DIR} ${GE_COMPILER_LIBRARY_DIR} ) # 设置头文件路径 target_include_directories(test_geir_add_example PRIVATE ${FWK_INCLUDE_DIR}/graph/ ${FWK_INCLUDE_DIR}/ge/ ${ASCEND_PATH}/opp/built-in/op_proto/inc/ ${CMAKE_CURRENT_SOURCE_DIR} ${ASCEND_PATH}/compiler/include )与 aclnn 工程的关键差异在于:GE 图模式的编译产物位于 CANN 的compiler组件中,需要链接libgraph.so、libge_runner.so、libgraph_base.so、libge_compiler.so四类图引擎库(其中前两者从compiler/lib64/stub下查找),并将compiler/include/graph、compiler/include/ge及opp/built-in/op_proto/inc(算子原型头文件)加入 include 路径;ASCEND_PATH优先由ASCEND_OPP_PATH环境变量推导(取其父目录)。
4. 创建 run.sh 脚本
在test_geir_${op_name}.cpp同级目录下创建run.sh文件,以AddExample算子为例,示例如下,请根据实际情况自行修改。
if [ -n "$ASCEND_INSTALL_PATH" ]; then # 实际CANN包安装路径 _ASCEND_INSTALL_PATH=$ASCEND_INSTALL_PATH elif [ -n "$ASCEND_HOME_PATH" ]; then _ASCEND_INSTALL_PATH=$ASCEND_HOME_PATH else _ASCEND_INSTALL_PATH="/usr/local/Ascend/cann" fi source ${_ASCEND_INSTALL_PATH}/bin/setenv.bash rm -rf build mkdir -p build cd build cmake ../ -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE # 执行构建命令 make ./test_geir_add_example # 替换为实际算子可执行文件名5. 运行 run.sh 脚本
在 run.sh 文件所在路径执行如下命令:
bash run.sh默认在当前执行路径/build/bin下生成可执行文件test_geir_add_example,运行结果如下:
INFO - [XIR]: Finalize ir graph session success该日志表示图会话(ir graph session)成功结束,说明构图、图编译、图运行全链路执行成功。运行过程中还会输出Initialize ge ... success、Add ir compute graph to ir session success、Run ir compute graph success等阶段日志,便于分步确认构图各环节状态。
小结
本文完整覆盖了 CANN ops-math 算子的两条调用主线:
- 快速调用:借助
build.sh --run_example一行命令,无需搭建工程即可体验任意算子样例,支持自定义算子包(cust)、ops-math 整包与静态库三种包形态,底层由 scripts/build_example.sh 自动完成样例查找、编译与运行,Ascend 950PR 场景还可切换 Simulator 仿真执行; - 业务集成:通过 PyTorch API、aclnn API、GE 图模式三种方式将算子集成到实际业务,其中 aclnn 两段式接口(
GetWorkspaceSize+ 执行接口)与 GE 构图(Graph+Session)是核心编程模型,本文给出的 CMakeLists 与 run.sh 均可直接套用到任意算子(替换算子名与接口名即可)。
后续如需进一步了解算子开发与调试,可继续阅读开发指南与调试调优指南,并结合各算子目录下的 README 示例 进行实操验证。
【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考