CANN opbase aclSetTensorAddr API 详解:复用 aclOpExecutor 时动态刷新 aclTensor 设备地址

📅 发布时间:2026/9/19 9:42:12
CANN opbase aclSetTensorAddr API 详解:复用 aclOpExecutor 时动态刷新 aclTensor 设备地址
CANN opbase aclSetTensorAddr API 详解复用 aclOpExecutor 时动态刷新 aclTensor 设备地址【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase导读aclSetTensorAddr是 CANN opbase 框架为 aclnn 单算子 API 提供的一个元数据管理接口用于在aclOpExecutor进入可复用状态后动态更新 aclTensor 记录的设备存储地址从而在不重复走第一阶GetWorkspaceSize建图流程的前提下多次执行同一算子、交替绑定不同的输入输出内存。读完本文你将掌握该接口的原型、参数约束、返回值含义、与aclSetAclOpExecutorRepeatable/aclDestroyAclOpExecutor的配套使用流程以及其在 acl_op_api.cpp 中的底层实现原理与典型示例。接口定位可复用执行器与地址刷新在常规 aclnn 单算子调用流程中每次执行一个算子都需要先调用第一阶 API形如aclnnXxxGetWorkspaceSize完成参数校验、算子图构建与 workspace 计算再调用第二阶 API形如aclnnXxx真正下发执行。这种模式在“同一算子反复执行、仅输入输出内存地址变化”的高频推理场景下开销较大。CANN opbase 通过aclSetAclOpExecutorRepeatable将aclOpExecutor置为可复用状态从而让第二阶 API 可以被重复调用具体约定见 aclSetAclOpExecutorRepeatable 文档。而aclSetTensorAddr正是这一复用机制的关键配套接口当执行器被复用时如果输入或输出 tensor 的设备内存地址发生了变化就必须调用本接口把新的设备地址同步更新到对应的 aclTensor 中否则第二阶 API 仍会沿用旧地址执行。从实现角度看该接口在 acl_op_api.cpp 中完成“更新 tensor 内部记录 刷新执行器中的真实下发地址”两件事并在源文件头部的aclCheckPcieAddrRefresh辅助函数acl_op_api.cpp中对 PCIe 直通场景下的地址类型切换做了合法性校验。函数原型aclnnStatus aclSetTensorAddr(aclOpExecutor *executor, const size_t index, aclTensor *tensor, void *addr)该原型声明位于公共头文件 acl_meta.h。此外头文件中还提供了同语义的首字母大写变体AclSetTensorAddracl_meta.h它只是对aclSetTensorAddr的一层转发封装见 acl_op_api.cpp两者可互换使用。相关配套接口同样声明于 acl_meta.haclSetAclOpExecutorRepeatable(executor)使执行器进入可复用状态acl_meta.h。aclDestroyAclOpExecutor(executor)销毁执行器并释放资源acl_meta.h。aclSetInputTensorAddr/aclSetOutputTensorAddr分别只针对输入或输出 tensor 刷新地址acl_meta.h。aclSetDynamicInputTensorAddr/aclSetDynamicOutputTensorAddr/aclSetDynamicTensorAddr针对动态输入输出aclTensorList 内元素刷新地址acl_meta.h。aclSetTensorAddr不区分输入输出通过index在全部输入输出 tensor 中定位目标使用上更为简洁。参数说明参数输入/输出说明executor输入已通过aclSetAclOpExecutorRepeatable置为可复用状态的aclOpExecutor。index输入待更新地址的 aclTensor 在所有输入输出 tensor 中的下标取值范围为 [0, tensor 总数 – 1]。tensor 总数按“先全部输入、后全部输出”的顺序统计输入中的 aclTensorList 按其元素个数计入。tensor输入待更新设备地址的 aclTensor 指针即需要在第一阶 API 中传入过、被执行器记录下来的那个 tensor。addr输入要更新到指定 aclTensor 的新设备存储地址。该地址必须 32 字节对齐否则可能产生未定义错误。关于addr的 32 字节对齐要求算子下发到 NPU 的输入输出内存一般要求满足对齐条件若传入未对齐地址执行器在搬运、计算过程中可能发生无法预期的行为因此务必在调用前对地址做对齐处理例如通过内存池分配、或对指针做向上取整对齐。返回值返回0表示成功返回其他值表示失败。通用返回码说明见 Common API Return Codes核心码值如下错误码数值含义ACLNN_SUCCESS0成功。ACLNN_ERR_PARAM_NULLPTR161001参数校验错误参数中包含非法nullptr。ACLNN_ERR_PARAM_INVALID161002参数校验错误例如参数取值不合法。ACLNN_ERR_INNER_XXX561xxx内部异常其中 561103ACLNN_ERR_INNER_NULLPTR表示内部出现空指针。这些码值在仓库头文件 op_errno.h 中以宏定义形式给出其中ACLNN_ERR_PARAM_INVALID定义为161002、ACLNN_ERR_INNER_NULLPTR定义为561103与文档描述一一对应。结合源码实现aclSetTensorAddr可能的失败原因如下返回 561103内部空指针executor或tensor为 null 指针。实现中通过NNOPBASE_ASSERT_NULLPTR_WITH_RETURN在函数入口处对两者逐一校验acl_op_api.cpp。返回 161002参数非法index取值越界超出 [0, tensor 总数 – 1]或者第一阶 API 首次调用时传入的 aclTensor 本身就是nullptr此时执行器内部没有记录该 tensor无法更新其地址。返回 161002参数非法在 PCIe 直通PCIe Through使能且新旧地址分属 PCIe 与普通内存两种地址空间时aclCheckPcieAddrRefresh会拒绝“PCIe 地址与非 PCIe 地址互相刷新”的操作并返回该错误码acl_op_api.cpp。注意tensor必须是第一阶 API如aclnnXxxGetWorkspaceSize中实际传入并被执行器登记过的 aclTensor。如果第一阶调用时该位置传入的是nullptr之后即使调用本接口也无法补录地址。使用流程与代码示例完整调用流程aclSetTensorAddr的正确使用必须遵循以下顺序创建输入输出 aclTensor / aclTensorList通过aclCreateTensor、aclCreateTensorList声明见 acl_meta.h。调用第一阶 APIaclnnXxxGetWorkspaceSize获得workspaceSize与executor。立即调用aclSetAclOpExecutorRepeatable(executor)开启复用。每次更换输入输出内存后调用aclSetTensorAddr刷新对应 tensor 的设备地址。调用第二阶 APIaclnnXxx(workspace, workspaceSize, executor, stream)执行算子。复用结束后调用aclDestroyAclOpExecutor(executor)销毁执行器。可复用执行器在第二阶执行后不会自动清理资源必须显式销毁详见 aclSetAclOpExecutorRepeatable 的限制说明。代码示例以下代码摘自 aclSetTensorAddr 示例仅用于说明调用关系不可直接复制运行// 创建输入输出aclTensor 与 aclTensorList。 std::vectorint64_t shape {1, 2, 3}; aclTensor tensor1 aclCreateTensor(shape.data(), shape.size(), aclDataType::ACL_FLOAT, nullptr, 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), nullptr); aclTensor tensor2 aclCreateTensor(shape.data(), shape.size(), aclDataType::ACL_FLOAT, nullptr, 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), nullptr); aclTensor tensor3 aclCreateTensor(shape.data(), shape.size(), aclDataType::ACL_FLOAT, nullptr, 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), nullptr); aclTensor output aclCreateTensor(shape.data(), shape.size(), aclDataType::ACL_FLOAT, nullptr, 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), nullptr); aclTensor *list[] {tensor1, tensor2}; auto tensorList aclCreateTensorList(list, 2); uint64_t workspaceSize 0; aclOpExecutor *executor; // AddCustom 算子有两个输入aclTensorList 与 aclTensor和一个输出aclTensor。 // 调用第一阶 API。 aclnnAddCustomGetWorkspaceSize(tensorList, tensor3, output, workspaceSize, executor); // 将执行器置为可复用。 aclSetAclOpExecutorRepeatable(executor); void *addr; aclSetTensorAddr(executor, 0, tensor1, addr); // 更新输入 tensor 列表中第一个 aclTensor 的设备地址。 aclSetTensorAddr(executor, 1, tensor2, addr); // 更新输入 tensor 列表中第二个 aclTensor 的设备地址。 aclSetTensorAddr(executor, 2, tensor3, addr); // 更新输入 aclTensor 的设备地址。 aclSetTensorAddr(executor, 3, output, addr); // 更新输出 aclTensor 的设备地址。 // 调用第二阶 API。 aclnnAddCustom(workspace, workspaceSize, executor, stream); // 销毁执行器。 aclDestroyAclOpExecutor(executor);示例中的 index 编号规则上例中AddCustom的形参顺序为(aclTensorList tensorList, aclTensor tensor3, aclTensor output)因此index 0对应tensorList中的第 1 个元素tensor1index 1对应tensorList中的第 2 个元素tensor2index 2对应tensor3index 3对应输出output。可见index的统计规则是“先按输入顺序展开 aclTensorList 中的每个元素再排列普通输入最后排列输出”tensor 总数即 4。示例中的addr为示意变量实际使用时必须替换为 32 字节对齐的合法设备内存地址。源码级原理剖析更新路径从 aclTensor 到执行器aclSetTensorAddr的核心实现在 acl_op_api.cpp执行逻辑分三步参数校验依次断言tensor、executor非空否则返回ACLNN_ERR_INNER_NULLPTR即 561103随后通过aclCheckPcieAddrRefresh校验 PCIe 直通场景下的地址类型一致性。更新 tensor 元数据调用tensor-SetStorageAddr(addr)将新地址写入 aclTensor 的存储地址字段使该 tensor 自身记录与外部内存状态保持一致。刷新执行器内登记的下发地址通过读取executor内存首部的魔数NNOPBASE_EXECUTOR_MAGIC_NUMBER判断执行器是否为 opbase 原生实现若是则用NnopbaseIsInput判定index指向的是输入还是输出并分别调用NnopbaseSetInputTensorAddr或NnopbaseSetOutputTensorAddr把tensor-GetData()得到的真实地址更新到执行器内部的参数描述paramDescs中从而影响后续第二阶 API 的实际下发。从源码结构看该分支设计表明当执行器由 opbase 创建带魔数标记时地址刷新会真正落入执行器内部的数据结构若执行器来自其他实现魔数不匹配函数在更新 aclTensor 后直接返回OK地址变更仅体现在 tensor 层面。PCIe 地址一致性校验aclCheckPcieAddrRefreshacl_op_api.cpp在 PCIe 直通IsPcieThroughEnabled使能时会比较 aclTensor 当前存储地址与新地址是否属于同一地址空间PCIe 范围或普通内存范围。若新旧地址从 PCIe 切换为普通内存或反之说明内存来源发生跨空间变更函数记录错误日志并返回ACLNN_ERR_PARAM_INVALID161002提示“PCIe 与非 PCIe 地址不能互相刷新”。这保证了地址更新不会破坏 PCIe 直通场景下的数据一致性。与地址刷新接口族的对比接口定位方式适用场景aclSetTensorAddr全局 index输入输出统一编号输入输出 tensor 数量固定、需要一次性统一刷新的场景aclSetInputTensorAddr / aclSetOutputTensorAddr分别按输入、输出编号明确知道目标 tensor 属于输入还是输出aclSetDynamicTensorAddr 及其输入输出变体irIndex relativeIndex配合 aclTensorList输入输出为动态 tensor 列表aclTensorList的场景其中动态地址刷新接口如aclSetDynamicInputTensorAddr会先通过tensors-Size()校验relativeIndex越界再定位列表内元素并刷新地址acl_op_api.cpp机制与aclSetTensorAddr同源。使用限制与注意事项必须与可复用执行器配套aclSetTensorAddr仅在执行器经aclSetAclOpExecutorRepeatable置为可复用后才生效未开启复用时无需也不应调用本接口刷新地址。必须在第一阶 API 之后、第二阶 API 之前调用地址刷新发生在两次调用间隙第二阶执行后地址即被锁定到当次下发。地址必须 32 字节对齐否则可能触发未定义行为这是文档明确强调的硬性约束。tensor 必须真实登记第一阶 API 首次调用时若该位置传入nullptr则无法通过本接口更新其地址返回 161002。必须显式销毁执行器可复用执行器执行完第二阶 API 后不会自动释放资源需配合aclDestroyAclOpExecutor手动销毁参见 aclDestroyAclOpExecutor 文档。地址空间一致性开启 PCIe 直通后新旧地址必须在同一地址空间内否则返回 161002。常见问题排查现象可能原因处理建议返回 561103executor或tensor为 null 指针检查执行器是否已由第一阶 API 正常创建、tensor 是否仍存活返回 161002index越界核对 index 取值范围为 [0, tensor 总数 – 1]并确认 aclTensorList 内元素展开后的编号返回 161002第一阶调用时该位置传入nullptrtensor 未登记第一阶调用时传入真实 aclTensor或改用aclSetDynamicTensorAddr系列接口返回 161002PCIe 直通下新旧地址跨地址空间保证新旧地址同为 PCIe 地址或同为普通内存地址执行结果与预期不符addr未做 32 字节对齐对地址做 32 字节对齐后再传入相关文档索引aclSetTensorAddr 英文原文档本文基础与 中文版aclSetAclOpExecutorRepeatable开启执行器复用aclDestroyAclOpExecutor销毁执行器Common API Return Codes通用返回码公共接口声明头文件接口实现源码错误码宏定义【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考