CANN ops-transformer 两段式接口(aclnn API)调用机制与实践指南
2026/9/19 6:01:33 网站建设 项目流程

CANN ops-transformer 两段式接口(aclnn API)调用机制与实践指南

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

两段式接口是 CANN ops-transformer 中基于单算子 API 执行方式调用算子时的标准范式:先调用aclxxXxxGetWorkspaceSize获取本次调用所需的 workspace 临时内存大小,再按需申请 NPU 内存,最后调用aclxxXxx完成实际计算。本文以仓库中真实算子接口(如 AttentionUpdate、AddExample)及其头文件、示例代码为证据,系统讲解两段式接口的调用流程、参数含义、内存管理、异常排查与编译运行方法,帮助开发者写出正确、可复用的 aclnn API 调用代码。

两段式接口概述

在 CANN ops-transformer 中,基于单算子 API 执行方式调用算子 API 时,通常分为"两段式",样式形如:

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

其中:

  • aclxx表示算子接口前缀,例如aclnn
  • Xxx表示对应的算子类型,例如 Add 算子对应aclnnAdd、AttentionUpdate 算子对应aclnnAttentionUpdate

两段接口的协作关系为:必须先调用第一段接口aclxxXxxGetWorkspaceSize,用于计算本次 API 调用过程中需要多少 workspace 内存,获取到计算所需的workspaceSize后,按照workspaceSize申请 NPU 内存,然后调用第二段接口aclxxXxx执行计算

说明

  • workspace 是指除输入/输出外,算子在 NPU 上完成计算所需要的临时内存,workspaceSize表示临时内存的大小。

  • 第二段接口aclxxXxx(...)不能重复调用,如下调用方式会出现异常:

    aclxxXxxGetWorkspaceSize(...) aclxxXxx(...) aclxxXxx(...) // 重复调用会异常

第一段接口:aclxxXxxGetWorkspaceSize

第一段接口负责"规划"。它基于输入/输出张量、算子属性以及设备信息,完成参数校验、输出 shape 推导、tiling 计算,并给出执行本次算子计算所需的最小 workspace 大小,同时创建算子执行器aclOpExecutor

以仓库中 AttentionUpdate 算子为例,其第一段接口声明位于 attention/attention_update/op_host/op_api/aclnn_attention_update.h:

ACLNN_API aclnnStatus aclnnAttentionUpdateGetWorkspaceSize( const aclTensorList *lse, const aclTensorList *localOut, int64_t updateType, aclTensor *out, aclTensor *lseOut, uint64_t *workspaceSize, aclOpExecutor **executor);

其参数语义(与仓库头文件注释一致):

参数方向说明
lseinNPU device 侧的aclTensorList,数据类型支持 FLOAT32,数据格式支持 ND
localOutinNPU device 侧的aclTensorList,数据类型支持 FLOAT32、FLOAT16、BFLOAT16,数据格式支持 ND
updateTypeinint64_t,控制lseOut是否输出,支持 0、1,分别表示不输出 / 输出 lseOut
outoutNPU device 侧的aclTensor,数据类型支持 FLOAT32、FLOAT16、BFLOAT16,数据格式支持 ND
lseOutoutNPU device 侧的aclTensor,作为 lse_m 可选输出,数据类型支持 FLOAT32
workspaceSizeout返回用户需要在 NPU device 侧申请的 workspace 大小
executorout返回 op 执行器,包含算子计算流程

从 attention/attention_update/op_host/op_api/aclnn_attention_update.cpp 的源码实现可以看到,第一段接口内部会做一系列校验与推导,例如CheckNotNull逐项检查lselocalOutout及列表内每个张量不为空指针,CheckDtypeValid依据ATTENTION_UPDATE_DTYPE_SUPPORT_LIST等支持列表校验输入数据类型是否合法,这些正是两段式接口"提前暴露参数问题"的价值所在——参数错误会在 GetWorkspaceSize 阶段即被拦截并返回错误码,而不是等到真正执行时才失败

第二段接口:aclxxXxx 执行计算

第二段接口负责"执行"。它接收第一段接口返回的workspace指针、workspaceSizeexecutor以及用户指定的aclrtStream流,将算子计算任务下发到 NPU。

ACLNN_API aclnnStatus aclnnAttentionUpdate(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);

其中executor是框架定义的一种执行器数据结构,用来执行算子计算的容器。通常调用第一段接口aclxxXxxGetWorkspaceSize时,框架会自动创建aclOpExecutor;调用第二段接口aclxxXxx后会自动释放该对象(参见 docs/zh/context/data_structure.md 中对aclOpExecutor的说明)。

这里需要特别强调的是原文档中的警告:第二段接口aclxxXxx不能重复调用。因为executor在一次调用生命周期内只能消费一次,重复调用会触发ACLNN_ERR_INNER_NOT_TRANS_EXECUTOR(561102,API 内部未调用 uniqueExecutor ReleaseTo)等异常。正确的做法是:一次GetWorkspaceSize+ 一次Xxx严格配对使用。

workspace 内存的申请与释放

workspaceSize返回的是字节大小,用户需要自行在 NPU device 侧通过aclrtMalloc申请内存,并在计算结束后通过aclrtFree释放。仓库示例 attention/attention_update/examples/test_aclnn_attention_update.cpp 中的标准写法为:

uint64_t workspaceSize = 0; aclOpExecutor *executor = nullptr; ret = aclnnAttentionUpdateGetWorkspaceSize(lseList, localOutList, update_type, out, nullptr, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAttentionUpdateGetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // 根据第一段接口计算出的 workspaceSize 申请 device 内存 void *workspaceAddr = nullptr; if (workspaceSize > 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); } // 调用第二段接口执行计算 ret = aclnnAttentionUpdate(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAttentionUpdate failed. ERROR: %d\n", ret); return ret);

要点:

  • 必须判空workspaceSize > 0时才需要申请内存,若为 0 则传nullptr即可;
  • 内存属性:使用ACL_MEM_MALLOC_HUGE_FIRST标志申请 NPU 侧大页内存;
  • 释放时机:必须在流同步(aclrtSynchronizeStream)之后释放,避免任务尚未完成时内存被回收;
  • 使用智能指针(如std::unique_ptr<void, aclError (*)(void *)>)管理workspaceAddr可以在异常分支自动释放,示例工程中即采用了该模式。

完整的 aclnn API 调用流程

两段式接口并非孤立的两个函数,而是嵌入在一整套 aclnn API 调用流程中。仓库文档 docs/zh/invocation/quick_op_invocation.md 给出了完整的调用流程图:

完整调用步骤可归纳为以下七步(以仓库示例 examples/add_example/examples/test_aclnn_add_example.cpp 和 AttentionUpdate 示例中的通用骨架为参考):

  1. 初始化 AscendCL:调用aclInitaclrtSetDeviceaclrtCreateStream完成 device/stream 初始化;
  2. 构造输入与输出:使用aclrtMalloc申请张量内存、aclrtMemcpy搬运数据,再调用aclCreateTensor创建aclTensor(多个张量可组合为aclTensorList);
  3. 调用第一段接口aclxxXxxGetWorkspaceSize(...),得到workspaceSizeexecutor
  4. 申请 workspace 内存:按workspaceSize调用aclrtMalloc申请 device 内存;
  5. 调用第二段接口aclxxXxx(workspaceAddr, workspaceSize, executor, stream)
  6. 同步等待:调用aclrtSynchronizeStream(stream)等待异步任务执行结束(固定写法);
  7. 获取输出并释放资源:将 device 侧结果aclrtMemcpy拷回 host 侧打印,依次aclDestroyTensoraclrtFreeaclrtDestroyStreamaclrtResetDeviceaclFinalize

其中第 3~5 步即"两段式接口"的核心部分,其余步骤为 aclnn API 调用的通用外壳。仓库中的算子示例(如 attention/attention_update/examples/test_aclnn_attention_update.cpp)均按此骨架组织,可作为新算子调用的模板。

返回码与异常排查

两段式接口的每一段都会返回aclnnStatus状态码。常见返回码如下(详见 docs/zh/context/aclnn_return_code.md):

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

其中与两段式接口生命周期强相关的内部异常码包括:

  • ACLNN_ERR_INNER_INFERSHAPE_ERROR(561001):API 内部进行输出 shape 推导发生错误;
  • ACLNN_ERR_INNER_TILING_ERROR(561002):API 内部做 npu kernel 的 tiling 时发生异常;
  • ACLNN_ERR_INNER_CREATE_EXECUTOR(561101):API 内部创建aclOpExecutor失败(可能因为操作系统异常);
  • ACLNN_ERR_INNER_NOT_TRANS_EXECUTOR(561102):API 内部未调用 uniqueExecutor ReleaseTo(典型场景即重复调用第二段接口);
  • ACLNN_ERR_INNER_OPP_KERNEL_PKG_NOT_FOUND(561112):没有加载到算子的二进制 kernel 库。

排查方法:对于异常状态码值,可通过aclGetRecentErrMsg接口获取具体的错误信息。仓库文档 docs/zh/context/compile_and_run_sample.md 给出了实际排查示例——构造空指针q调用aclnnFlashAttentionScoreGetWorkspaceSize后:

aclnnFlashAttentionScoreGetWorkspaceSize failed. ERROR: 161001 [ERROR msg][PID:xxxx] xxx(timestamp) AclNN_Parameter_Error(EZ1001): The query cannot be nullptr.

可以看到,返回码 161001(ACLNN_ERR_PARAM_NULLPTR)配合aclGetRecentErrMsg能精确定位到具体是哪个参数为空,这正是两段式接口第一段"参数校验前置"优势的体现。

编译与运行两段式接口示例

编写好包含两段式接口调用的.cpp文件后,需要创建 CMakeLists.txt 并链接算子库。对于本项目标准内置算子(依赖 ops-transformer 整包),CMake 关键配置如下(完整示例见 docs/zh/context/compile_and_run_sample.md):

cmake_minimum_required(VERSION 3.18.4) project(ACLNN_EXAMPLE) add_compile_options(-std=c++11) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "./bin") 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 ) add_executable(opapi_test test_aclnn_xxx.cpp) 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/libopapi_transformer.so ${ASCEND_PATH}/lib64/libc_sec.so)

注意:本项目编译生成算子样例时,需额外链接libopapi_math.so动态库,因为算子在实现过程中调用了部分 L0 接口,这些接口封装在libopapi_math.so中,因此在编译链接阶段需显式声明此项依赖。若调用自定义算子(如 experimental 贡献目录下算子),则需将libopapi_transformer.so替换为自定义算子包中的libcust_opapi.so,并增加${ASCEND_PATH}/opp/vendors/${vendor_name}_transformer/op_api/include头文件路径(${vendor_name}默认为custom)。

编译运行流程:

# 1. 生效 CANN 环境变量(${INSTALL_DIR} 为 CANN 软件安装路径) source ${INSTALL_DIR}/set_env.sh # 2. 新建 build 目录并编译 mkdir -p build && cd build cmake ../ -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE make # 3. 运行生成的可执行文件 cd bin ./opapi_test

若执行结果报错、未出现预期结果,可使用aclGetRecentErrMsg接口获取报错具体信息(调用方式见上文"返回码与异常排查"一节)。

小结

两段式接口是 CANN ops-transformer 中所有 aclnn API 的统一调用范式:GetWorkspaceSize负责参数校验、shape 推导与 workspace 规划,Xxx负责执行计算。正确使用两段式接口的关键在于:严格先调第一段、按返回的workspaceSize申请内存、第二段只调用一次、计算完成后再释放内存,并结合aclGetRecentErrMsg与返回码表进行问题定位。读者可进一步阅读仓库中的 两段式接口(本文依据)、数据结构、算子调用总览 与 编译运行样例,并结合各算子目录下的examples/test_aclnn_*.cpp示例文件加深理解。

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

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

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

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

立即咨询