尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

CANN opbase 中 AI CPU 算子执行任务注册宏 ADD_TO_LAUNCHER_LIST_AICPU 完全指南

CANN opbase 中 AI CPU 算子执行任务注册宏 ADD_TO_LAUNCHER_LIST_AICPU 完全指南 CANN opbase 中 AI CPU 算子执行任务注册宏 ADD_TO_LAUNCHER_LIST_AICPU 完全指南【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase导读ADD_TO_LAUNCHER_LIST_AICPU是 CANN opbase 算子库为 AI CPUAICpu算子提供的一阶段接口aclnnXxx注册宏它负责为指定算子创建一个执行任务task并挂入aclOpExecutor的执行队列任务真正在二阶段接口aclnnXxx_调用时才会在指定 stream 上执行。本文围绕该宏展开从参数语义、调用顺序约束、底层展开实现到缓存与任务空间机制逐层剖析帮助算子开发者正确编写 AI CPU 算子的单算子调用代码并理解任务从注册到执行的完整链路。本文对应的官方英文文档为 ADD_TO_LAUNCHER_LIST_AICPU中文版见 docs/zh/api/nnopbase/opdev/common_macros_and_classes/ADD_TO_LAUNCHER_LIST_AICPU.md该宏属于 opdev 公共宏与类体系完整清单见 common_macros_and_classes。宏功能为 AI CPU 算子创建执行任务两阶段接口模型中的位置在 opbase 的单算子调用框架中一个算子对外暴露为两阶段接口一阶段接口aclnnXxx负责构建aclOpExecutor执行器、完成算子参数校验与任务登记此时算子尚未真正下发执行。二阶段接口aclnnXxx_接收 executor 与aclrtStream此时才真正触发执行队列中的任务在设备上运行。ADD_TO_LAUNCHER_LIST_AICPU正是嵌入在一阶段接口实现中的关键一步它创建某个 AI CPU 算子的执行任务并将任务置入aclOpExecutor的执行队列任务在二阶段接口aclnnXxx_被调用时执行。宏原型ADD_TO_LAUNCHER_LIST_AICPU(KERNEL_NAME, attrNames, opArgs...)参数说明参数输入/输出说明KERNEL_NAME输入算子名例如Add。该名字必须与算子在 op_type 注册表中的注册名一致宏内部会拼接生成KERNEL_NAME##OpTypeId()来获取算子类型 ID。attrNames输入算子的属性名类型为OP_ATTR_NAMES封装的属性名列表详见 OP_ATTR_NAMES。无属性时传OP_ATTR_NAMES()空FVector。opArgs...输入算子的参数由 OP_INPUT、OP_OUTPUT、OP_ATTR 等宏按顺序组合而成。宏功能与两阶段执行的关系从语义上讲该宏完成了两件事创建任务在AicpuTaskSpace中查找或创建对应的AicpuTask详见下文源码级实现任务携带算子类型、输入输出张量、属性以及动态 shape 处理方式等上下文。挂入执行队列将任务封装为AiCpuKernelLauncher并调用executor-AddToKernelLauncherList()追加到aclOpExecutor的执行队列当二阶段aclnnXxx_被调用时框架按队列顺序依次下发执行。约束说明使用该宏必须遵守以下约束与 INFER_SHAPE 的先后顺序如果算子需要INFER_SHAPE那么此宏必须在 INFER_SHAPE之后调用。原因在于动态 shape 算子必须先完成输出 shape 推导才能为输出张量确定正确的内存布局随后创建的执行任务才能携带有效的 shape 信息。关联接口宏定义内部会调用如下接口完成参数解析与任务构建OP_ATTR_NAMES OP_INPUT(x...) OP_OUTPUT(x...) OP_ATTR(x...)其中OP_ATTR_NAMES用于声明参与任务构建的 AI CPU 算子属性名列表OP_INPUT/OP_OUTPUT/OP_ATTR分别用于描述算子输入、输出与属性参数供OpArgContext统一管理。关联接口速览接口作用详细说明OP_ATTR_NAMES({name1, name2, ...})封装 AI CPU 算子的属性名类型为::op::FVectorstd::string默认值为空FVector即OP_ATTR_NAMES()。详见 OP_ATTR_NAMESOP_INPUT(x...)封装算子输入的aclTensor/aclTensorList若算子含非aclTensor/aclTensorList输入需先调用aclOpExecutor::ConvertToTensor转换。详见 OP_INPUTOP_OUTPUT(x...)封装算子输出详见 OP_OUTPUTOP_ATTR(x...)封装算子属性参数属性参数指算子原型中定义的属性。详见 OP_ATTRINFER_SHAPE(KERNEL_NAME, op_args...)运行算子 InferShape 推导输出 shape需在ADD_TO_LAUNCHER_LIST_AICORE/ADD_TO_LAUNCHER_LIST_AICPU之前调用。详见 INFER_SHAPE源码级实现宏展开与调用链宏定义与展开该宏定义于头文件 include/nnopbase/opdev/aicpu/aicpu_task.h#define OP_ATTR_NAMES ::op::FVectorstd::string aclnnStatus CreatAicpuKernelLauncher(uint32_t opType, op::internal::AicpuTaskSpace space, aclOpExecutor* executor, const FVectorstd::string attrNames, op::OpArgContext* args); #define ADD_TO_LAUNCHER_LIST_AICPU(KERNEL_NAME, attrNames, opArgs...) \ ({ \ aclnnStatus addToLaunchRet; \ do { \ op::OpArgContext* opArgCtx GetOpArgContext(opArgs); \ addToLaunchRet CreatAicpuKernelLauncher(KERNEL_NAME##OpTypeId(), space, executor, attrNames, opArgCtx); \ } while (0); \ addToLaunchRet; \ })宏展开后的关键动作可以拆解为三步GetOpArgContext(opArgs)将OP_INPUT/OP_OUTPUT/OP_ATTR等宏包装的参数统一构建为op::OpArgContext内部按输入、输出、属性等类别管理参数。KERNEL_NAME##OpTypeId()通过字符串拼接生成取算子类型 ID 的函数调用得到uint32_t类型的 opType。CreatAicpuKernelLauncher(opType, space, executor, attrNames, opArgCtx)真正创建并登记执行任务。注意宏中引用的spaceAicpuTaskSpace与executoraclOpExecutor*是在宏外部、由算子实现代码预先声明好的变量这也解释了为何该宏只能在特定上下文算子一阶段接口实现内部中使用。CreatAicpuKernelLauncher 的任务构建逻辑CreatAicpuKernelLauncher实现在 src/nnopbase/aicpu/task_handler/aicpu_task.cppaclnnStatus CreatAicpuKernelLauncher(uint32_t opType, op::internal::AicpuTaskSpace space, aclOpExecutor* executor, const FVectorstd::string attrNames, op::OpArgContext* args) { CHECK_RET(args ! nullptr, ACLNN_ERR_PARAM_INVALID); auto task space.GetOrCreateTask(executor, attrNames, args); CHECK_RET(task ! nullptr, ACLNN_ERR_INNER); op::internal::ProfilingInfoId profilingInfoId; auto overflowTask TakePendingOverflowTask(task); op::KernelLauncher* launcher nullptr; if (overflowTask ! nullptr) { executor-AbandonCache(true); launcher new AiCpuOneShotKernelLauncher{ opType, op::AI_CPU, executor, profilingInfoId, std::move(overflowTask), args}; } else { executor-AbandonCache(); launcher new op::internal::AiCpuKernelLauncher{opType, op::AI_CPU, executor, profilingInfoId, task, args}; } executor-AddToKernelLauncherList(launcher); op::internal::BuildGraph(executor-GetGraph(), opType, *args-GetOpArg(op::OP_INPUT_ARG), *args-GetOpArg(op::OP_OUTPUT_ARG), *args-GetOpArg(op::OP_WORKSPACE_ARG)); return ACLNN_SUCCESS; }核心流程校验args为空时返回ACLNN_ERR_PARAM_INVALID。获取或创建任务space.GetOrCreateTask(executor, attrNames, args)是任务复用与缓存的关键见下一节。构造 launcher若存在挂起的 overflow 任务则构造AiCpuOneShotKernelLauncher并调用executor-AbandonCache(true)放弃缓存否则构造常规的AiCpuKernelLauncher。登记executor-AddToKernelLauncherList(launcher)将 launcher 追加进执行队列——这正是文档所述置入 aclOpExecutor 的执行队列。构图BuildGraph依据输入、输出、workspace 参数构建算子执行图graph为二阶段执行提供拓扑信息。AicpuTaskSpace 与任务缓存机制任务空间类AicpuTaskSpace同样定义在 include/nnopbase/opdev/aicpu/aicpu_task.h 中其核心职责包括维护算子的任务缓存内部持有std::unordered_mapsize_t, std::vectorstd::unique_ptrAicpuTask hashMap_以任务 key 为索引缓存已创建的任务对象避免同一算子重复创建。计算任务 keyGenTaskKey结合属性名attrNames与算子输入张量信息生成哈希 keyGenHashBinary使用kHashSeed 0x9e3779b9U作为哈希种子。记录引用关系SetRef/IsRef记录输入输出的引用ref索引用于内存复用语义。动态 shape 分类构造函数接受ge::UnknowShapeOpType unknownType默认DEPEND_IN_SHAPE与isTf标志区分自研CC动态 shape 算子与 TF 动态 shape 算子。GetOrCreateTask实现在 src/nnopbase/aicpu/task_handler/aicpu_task_base.cpp 附近负责按 key 命中缓存或创建新的任务实例。任务类层次AicpuTask / AicpuTfTask / AicpuCCTask任务基类AicpuTask与两个子类定义于 include/nnopbase/opdev/aicpu/aicpu_task.h类用途关键成员AicpuTask任务基类opType_算子类型、unknownType_动态 shape 类型、inputs_/outputs_输入输出张量、inputKey_/keyLen_任务 key 缓冲长度上限kAicpuKeyBufLen 1024字节、argsHandle_AicpuArgsHandler、extInfoHandle_AicpuExtInfoHandlerAicpuTfTaskTF 类型动态 shape 算子任务额外持有tfBinHandle_、funcHandle_、useNewLaunchInterface_通过LaunchKernelByNewInterface走新接口下发AicpuCCTask自研算子任务额外持有aicpuBinHandle_、funcHandle_、isCustomTask_默认内核函数名为kDefaultFunctionName RunCpuKernel通过GetKernelNameAndSoName解析内核 so 与函数名任务接口Init(inputs, outputs, attrs)初始化任务将输入输出与属性AicpuAttrs绑定到任务。Run(executor, stream)在二阶段执行时被调用负责下发 AI CPU 内核。AicpuTask基类还通过inputKey_缓冲与GenTaskKey配合把输入张量及属性序列化为任务 key 字节流供AicpuTaskSpace做缓存命中判断——不同输入 shape / 属性组合会生成不同 key从而复用或重建任务。属性序列化与参数转换细节在 include/nnopbase/opdev/aicpu/aicpu_task.h 中还可以看到任务构建所需的辅助逻辑AppendTensor系列重载将aclTensor、aclScalar、aclIntArray、aclTensorList统一转换为aclTensor加入列表其中aclScalar会先通过executor-ConvertToTensor转为张量。CreateTensorListImpl按OpArgTypeOPARG_ACLTENSOR、OPARG_ACLSCALAR、OPARG_INT_LIST、OPARG_ACLTENSOR_LIST分发处理。AppendAttrForKey系列将标量、字符串、std::vector、aclIntArray/aclFloatArray/aclBoolArray等属性值逐字节写入 key 缓冲用于任务缓存 key 的生成缓冲上限kAicpuKeyBufLen 1024字节超出即停止追加。AddAicpuAttr系列将数组类属性aclIntArray/aclFloatArray/aclBoolArray转换为std::vectorint64_t/std::vectorfloat/std::vectorbool后以AnyValue形式存入AicpuAttrs。这些细节说明attrNames中声明的属性名不仅用于任务 key 计算还决定了哪些属性会被序列化进AicpuAttrs供内核侧解析因此属性名必须与算子原型定义一致。调用示例官方示例IndexPut 算子// 调用ADD_TO_LAUNCHER_LIST_AICPU创建IndexPut算子的执行任务 // 其中IndexPut是算子名accumulate是算子的属性名 // selfRef、values、masks、indices是算子输入参数 // out是算子输出参数accumulate是算子的属性参数 ADD_TO_LAUNCHER_LIST_AICPU(IndexPut, OP_ATTR_NAMES({accumulate}), OP_INPUT(selfRef, values, masks, indices), OP_OUTPUT(out), OP_ATTR(accumulate));解读算子名IndexPut会被拼接为IndexPutOpTypeId()以获取类型 ID。OP_ATTR_NAMES({accumulate})声明算子的属性名为accumulate布尔型表示是否原地累加。OP_INPUT(selfRef, values, masks, indices)声明 4 个输入张量。OP_OUTPUT(out)声明 1 个输出张量。OP_ATTR(accumulate)将属性变量绑定到属性声明。典型的一阶段接口代码骨架将上述宏放入一阶段接口实现中完整的模式如下aclnnStatus aclnnIndexPut(aclOpExecutor* executor, aclTensor* selfRef, aclTensor* values, aclTensor* masks, aclTensor* indices, aclTensor* out, bool accumulate) { // 需要动态shape推导时先调用INFER_SHAPE INFER_SHAPE(IndexPut, OP_INPUT(selfRef, values, masks, indices), OP_OUTPUT(out), OP_ATTR(accumulate)); // 静态task空间按算子名与动态shape类型创建 static op::internal::AicpuTaskSpace space(IndexPut, ge::DEPEND_IN_SHAPE, false); // 注册执行任务到executor队列二阶段aclnnIndexPut_调用时执行 ADD_TO_LAUNCHER_LIST_AICPU(IndexPut, OP_ATTR_NAMES({accumulate}), OP_INPUT(selfRef, values, masks, indices), OP_OUTPUT(out), OP_ATTR(accumulate)); return ACLNN_SUCCESS; }说明space与executor为宏展开所依赖的上下文变量其具体创建方式随算子在框架中的接入位置而定。单元测试中的用法佐证在 tests/nnopbase/ut/composite_op/test_op_cache.cpp 中可以看到该宏与AicpuTaskSpace配合的测试用例AICPUNoCache用例用于验证 AI CPU 任务禁止走 PTA 缓存其代码形态为static op::internal::AicpuTaskSpace space(Gelu, ge::DEPEND_IN_SHAPE, false); ADD_TO_LAUNCHER_LIST_AICPU(Gelu, OP_ATTR_NAMES(), OP_INPUT(self.get()), OP_OUTPUT(out.get()));该用例同时验证了 AI CPU 任务执行后executor-GetOpExecCache()-GetHash()为 0、缓存 key 为空等行为说明 AI CPU 算子任务默认不参与一阶段 executor 的 cache 复用流程——这与CreatAicpuKernelLauncher中调用executor-AbandonCache()的实现相互印证。常见问题与最佳实践ADD_TO_LAUNCHER_LIST_AICPU与ADD_TO_LAUNCHER_LIST_AICORE的区别前者面向 AI CPU 算子经AicpuTaskSpace管理任务、运行时加载 AI CPU 内核 so后者面向 AI Core 算子经AiCoreKernelLauncher下发 AI Core 内核。二者都通过executor-AddToKernelLauncherList将任务挂入同一执行队列因此一个算子内部不得同时混用两者重复登记同一任务。属性名必须精确匹配attrNames中列出的属性名将参与任务 key 生成与AicpuAttrs序列化若与算子原型中的属性名不一致将导致任务无法正确命中缓存或内核侧解析失败。有INFER_SHAPE需求时注意调用顺序务必先INFER_SHAPE后ADD_TO_LAUNCHER_LIST_AICPU否则输出 shape 未推导任务携带的张量信息不完整。无属性算子使用空OP_ATTR_NAMES()即ADD_TO_LAUNCHER_LIST_AICPU(OpName, OP_ATTR_NAMES(), OP_INPUT(...), OP_OUTPUT(...))如上述 Gelu 测试用例所示。输入参数类型输入须为aclTensor或aclTensorList若算子含标量、数组等非张量输入需先用ConvertToTensor转换参见 OP_INPUT 的约束说明。深入阅读宏定义与任务类体系include/nnopbase/opdev/aicpu/aicpu_task.h任务构建实现src/nnopbase/aicpu/task_handler/aicpu_task.cpp任务空间缓存实现src/nnopbase/aicpu/task_handler/aicpu_task_base.cpp单元测试tests/nnopbase/ut/composite_op/test_op_cache.cpp关联宏文档OP_ATTR_NAMES、INFER_SHAPE、OP_INPUT、OP_OUTPUT、OP_ATTR、宏与类总览【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表