CANN opbase framework_op 框架算子接口详解CopyToNpu / CopyToNpuSync / CopyNpuToNpu 数据搬运实战指南【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase导读framework_op是 CANN opbase 为算子开发者提供的框架级数据搬运接口集合位于docs/zh/api/nnopbase/opdev/framework_op/目录包含CopyToNpu、CopyToNpuSync、CopyNpuToNpu三个接口。它们以aclOpExecutor算子执行器为桥梁在 L2 接口aclnn 组合算子开发场景中完成 host 侧到 device 侧、device 侧到 device 侧的数据拷贝任务编排。阅读本文后你将掌握三个接口的功能差异、函数签名、约束条件、调用示例并能从源码层理解其底层aclrtMemcpyAsync/aclrtMemcpy实现与 executor 任务队列的协作机制直接用于组合算子的数据搬运代码编写与调试。framework_op 接口族概览framework_op索引页共收录 3 个接口均声明在头文件 include/nnopbase/opdev/framework_op.h 中位于op命名空间const aclTensor* CopyToNpu(const aclTensor* src, aclOpExecutor* executor); // host → device异步入队 const aclTensor* CopyToNpuSync(const aclTensor* src, aclOpExecutor* executor); // host → device同步阻塞 aclnnStatus CopyNpuToNpu(const aclTensor* src, const aclTensor* dst, aclOpExecutor* executor); // device → device异步入队从源码看三个接口都通过OP_TYPE_REGISTER宏完成算子类型注册见 src/nnopbase/composite_op/aclnn_engine/z_framework_op.cpp并纳入 opbase 的 DFX数据采集与诊断框架接口入口处统一调用L0_DFX(CopyToNpu, src)等埋点进行调用监控。三个接口的核心定位可概括为接口数据方向同步/异步是否入 executor 任务队列返回CopyToNpuhost → device异步是device 侧 aclTensor 指针CopyToNpuSynchost → device同步阻塞否device 侧 aclTensor 指针CopyNpuToNpudevice → device异步是aclnnStatus 状态码CopyToNpu异步 host 到 device 拷贝功能说明CopyToNpu创建一个 host 侧到 device 侧的数据拷贝任务并放入 executor 的任务队列中由后续的 executor 调度如Run()统一异步执行。函数原型与参数const aclTensor *CopyToNpu(const aclTensor *src, aclOpExecutor *executor)参数输入/输出说明src输入需要拷贝到 device 侧的 host 侧数据aclTensor。executor输入L2 接口中一阶段接口声明的算子执行器对象。返回值说明返回拷贝到 device 侧后、指向 device 侧数据的aclTensor任务创建失败则返回nullptr。约束说明入参指针不能为空src的 placement 必须为 host 侧kOnHost源码中通过src-GetPlacement() op::TensorPlacement::kOnHost强校验见 z_framework_op.cpp不满足则返回nullptr。调用示例// 初始化一个host侧tensor并拷贝到dstdst为一个device侧tensor void Func(aclOpExecutor *executor) { int64_t myArray[10]; auto src executor-ConvertToTensor(myArray, 10, DT_INT64); auto dst CopyToNpu(src, executor); }源码实现要点从源码看z_framework_op.cppCopyToNpu的完整调用链为DFX 埋点L0_DFX(CopyToNpu, src)记录调用信息placement 校验确认src位于 host 侧申请 device 内存通过executor-AllocTensor(...)以src的存储 shape、原始 shape、数据类型、存储格式创建目标dsttensor构造拷贝 Launcher创建CopyToNpuKernelLaunchercoreType为op::NO_CALC即纯数据搬运、无需算子计算核并封装为OpArg参数列表入任务队列调用executor-AddToKernelLauncherListCopyTask(...)将拷贝任务挂到 executor 的拷贝任务链表上同时建立输入输出 tensor 的依赖关系。实际的异步拷贝动作发生在CopyToNpuKernelLauncher::Launch()中z_framework_op.cpp先调用CalcTensorNBytes计算源与目的 tensor 的字节数校验目的端字节数不小于源端随后调用aclrtMemcpyAsync并指定拷贝方向为ACL_MEMCPY_HOST_TO_BUF_TO_DEVICE在executor-GetStream()对应的流上异步执行最终由 executor 的Run()统一触发。需要特别注意的是由于 RTS 层的 memcpy 任务无法被缓存复用CopyToNpu内部会放弃 executor 的算子缓存逻辑源码注释明确标注 because rts memcpy cannot be cached, so abandon cache when use memcpy因此该类拷贝任务每次都会真实下发执行。CopyToNpuSync同步 host 到 device 拷贝功能说明CopyToNpuSync同步完成 host 侧到 device 侧的数据拷贝会阻塞等待拷贝动作完成且不会再放入 executor 任务队列。该接口中申请的 device 内存需要调用方负责释放。函数原型与参数const aclTensor *CopyToNpuSync(const aclTensor *src, aclOpExecutor *executor)参数输入/输出说明src输入需要拷贝到 device 侧的 host 数据。executor输入L2 接口中一阶段接口声明的算子执行器对象。返回值说明返回拷贝到 device 侧后、指向 device 侧数据的aclTensor任务创建失败则返回nullptr。约束说明入参指针不能为空与CopyToNpu相同src的 placement 必须为kOnHost内存所有权该接口自行申请的 device 内存不会被 executor 管理回收调用方在使用完毕后必须手动释放测试中通过delete[] static_castchar*(dstTensor-GetData())释放见 tests/nnopbase/st/composite_op/test_framework_op.cpp。调用示例// 初始化一个host侧tensor并拷贝到dstdst为一个device侧tensor void Func(aclOpExecutor *executor) { int64_t myArray[10]; auto src executor-ConvertToTensor(myArray, 10, DT_INT64); auto dst CopyToNpuSync(src, executor); }源码实现要点CopyToNpuSync的同步实现z_framework_op.cpp与CopyToNpu走完全不同的路径放弃缓存入口即调用executor-AbandonCache(true)禁用 executor 的缓存/复用能力保证同步语义DFX 埋点与 placement 校验同CopyToNpu申请 device 内存executor-AllocTensor(...)创建dst显式 device 内存分配通过aclrtMallocWithCfg分配ACL_MEM_TYPE_HIGH_BAND_WIDTH类型的高带宽内存并携带ACL_RT_MEM_ATTR_MODULE_ID属性kModelId 36对应 AICPU 模块内存大小由CalcTensorNBytes计算得到同步拷贝调用aclrtMemcpy同步版本方向ACL_MEMCPY_HOST_TO_DEVICE完成 host → device 数据搬移阻塞直至完成失败时自动aclrtFree释放已分配内存回填存储地址通过dst-SetFromWorkspace(false)标记非 workspace 内存并用dst-SetStorageAddr(deviceMem)将分配到的 device 地址挂到dst上随后返回。该接口适合在组合算子中需要先拿到 device 侧数据再继续后续逻辑的同步场景例如把 host 侧的常量表、索引表提前搬运到 device 供后续算子读取。CopyNpuToNpu异步 device 到 device 拷贝功能说明CopyNpuToNpu创建一个 device 侧到 device 侧的数据拷贝任务并放入 executor 的任务队列中由 executor 统一异步执行。函数原型与参数aclnnStatus CopyNpuToNpu(const aclTensor *src, const aclTensor *dst, aclOpExecutor *executor)参数输入/输出说明src输入拷贝的源 tensordevice 侧。dst输入拷贝的目的 tensordevice 侧。executor输入L2 接口中一阶段接口声明的算子执行器对象。与前面两个接口不同CopyNpuToNpu的dst由调用方自行创建并传入而不是由接口内部AllocTensor生成因此返回的是aclnnStatus状态码而非 tensor 指针。返回值说明创建拷贝任务成功返回ACLNN_SUCCESS状态码值 0否则返回其他 aclnn 错误码。约束说明入参指针不能为空src与dst的 placement 都必须是 device 侧 HBMkOnDeviceHbm源码中分别强校验z_framework_op.cpp目的 tensor 的字节数不能小于源 tensor 字节数。调用示例// 创建src到dst的拷贝任务 如果不成功则返回 void Func(const aclTensor *src, const aclTensor *dst, aclOpExecutor *executor) { if (CopyNpuToNpu(src, dst, executor) ! ACLNN_SUCCESS) { return; } }源码实现要点CopyNpuToNpuz_framework_op.cpp与CopyToNpu的任务编排结构一致同样先做 DFX 埋点与 placement 校验然后构造CopyNpuToNpuKernelLaunchercoreType为op::NO_CALC并调用AddToKernelLauncherListCopyTask入队。两者的核心差异在于Launch()阶段的实际拷贝方向CopyNpuToNpuKernelLauncher::Launch()使用ACL_MEMCPY_DEVICE_TO_DEVICE方向调用aclrtMemcpyAsyncz_framework_op.cpp在 device 显存之间完成数据搬移适用于算子输出复用、中间结果流转等 device 内存内的数据拷贝场景。底层机制CalcTensorNBytes 与任务编排三个接口共同依赖两个底层机制字节数计算。CalcTensorNBytesz_framework_op.cpp根据 tensor 的数据类型和存储 shape 计算实际需搬运的字节数并内置溢出防护使用ge::MulOverflow做乘法溢出检测若发生溢出返回ACLNN_ERR_INNER错误对于小于 1 字节的类型如比特位类型通过位运算BYTE_BITS_MINUS_ONE 7、LOG_2_BYTE_BITS 3向上取整到整数字节。拷贝任务入队。AddToKernelLauncherListCopyTask实现于 src/nnopbase/composite_op/aclnn_engine/op_executor.cpp将CopyToNpuKernelLauncher/CopyNpuToNpuKernelLauncher挂入 executor 的拷贝任务链表同时登记输入输出OpArg关系供后续 workspace 计算与执行依赖分析使用。两个 Launcher 的GetBin()均返回nullptrCheckRepeatable()均返回false说明该类数据搬运任务不携带算子二进制、不可被 executor 缓存复用——这也解释了三个接口或相关场景中 executor 缓存被主动放弃的原因。测试用例验证仓库提供了两个层次的测试验证接口行为tests/nnopbase/st/composite_op/test_framework_op.cppCopyToNpuUtTest通过CREATE_EXECUTOR()创建执行器AllocFloatArrayConvertToTensor构造 host 侧 tensor调用op::CopyToNpu后断言dstTensor非空随后executorPtr-Run()执行任务并断言返回ACLNN_SUCCESSCopyNpuToNpuUtTest用AllocTensor创建 shape 为{5, 10}的DT_FLOAT、FORMAT_ND源/目的 tensor断言CopyNpuToNpu返回ACLNN_SUCCESSRun()后同样返回成功CopyToNpuSyncTest构造约 75MB 的 float 数组调用CopyToNpuSync后立即用memcmp校验 host 数据与 device 数据逐字节一致同步语义保证拷贝完成并手动释放 device 内存。这三个用例从工程角度印证了接口的正确用法host 侧 tensor 经ConvertToTensor构造、device 侧 tensor 经AllocTensor构造、异步接口需Run()触发、同步接口返回即可读取数据、同步接口内存需调用方释放。使用建议与注意事项按需选择同步/异步若后续逻辑不依赖拷贝结果且希望流水化执行优先使用异步的CopyToNpu/CopyNpuToNpu入队后统一Run()若必须立即读取 device 数据使用CopyToNpuSync。内存管理职责分明CopyToNpuSync内部aclrtMallocWithCfg申请的高带宽内存归调用方所有用完后务必释放避免显存泄漏CopyToNpu返回的dst内存由 executor 统一管理。placement 匹配host 拷贝类接口要求源 tensor 为kOnHostdevice 间拷贝要求源、目的均为kOnDeviceHbm传错 placement 会直接返回失败。字节数约束目的 tensor 字节数不得小于源 tensor否则两个 Launcher 的Launch()均会返回ACLNN_ERR_INNER。与 L2 接口的配合所有接口的executor均来自 L2 接口一阶段声明创建的aclOpExecutor对象属于框架预留的算子组合能力建议在组合算子composite op开发流程中按上述调用示例接入。延伸阅读接口声明与注册include/nnopbase/opdev/framework_op.h、src/nnopbase/composite_op/aclnn_engine/z_framework_op.cpp执行器对象aclOpExecutor相关接口docs/zh/api/nnopbase/opdev/op_executor/op_executor.md、docs/zh/api/nnopbase/opdev/op_executor/ConvertToTensor.md英文版文档docs/en/api/nnopbase/opdev/framework_op/framework_op.md测试用例tests/nnopbase/st/composite_op/test_framework_op.cpp、tests/nnopbase/ut/composite_op/test_framework_op.cpp【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
