
OP_OUTPUT 宏详解CANN opbase 算子输出封装与执行器对接指南【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase导读OP_OUTPUT是 CANN opbase 算子库中用于声明并封装算子输出参数的核心宏它将一个或多个aclTensor/aclTensorList打包成统一的OpArgContext参数描述供执行器aclOpExecutor在构建算子图、调度 Kernel 时直接消费。本文将以 OP_OUTPUT 官方文档 为主线结合 opbase 仓库中的宏定义、参数上下文初始化逻辑与实际工程调用样例完整讲解其原型、参数语义、约束条件、底层实现机制与真实使用场景帮助算子开发者正确声明输出并在图构建链路中理解其流转方式。宏功能概述OP_OUTPUT的作用非常聚焦把算子的一个或多个输出张量封装为框架可识别的输出参数描述。其封装对象包括两类aclTensor单个输出张量aclTensorList输出张量列表例如算子输出为动态数量张量时的场景。它与同族的OP_INPUT、OP_ATTR、OP_WORKSPACE、OP_OUTSHAPE、OP_OPTION、OP_MODE、OP_EMPTY_ARG共同构成 opbase 的“算子参数声明宏族”开发者通过一组宏即可把算子的输入、输出、属性、工作空间、输出形状等参数一次性声明清楚参见 op_arg_def.h 中的宏族定义。宏原型OP_OUTPUT(x...)x...是可变参数variadic可以一次传入一个或多个输出对象宏内部会将其打包为std::tuple后再构造输出参数对象。参数说明参数输入/输出说明x...输入算子的输出aclTensor与aclTensorList支持同时传入多个。值得说明的是与OP_ATTR属性值或OP_WORKSPACE工作空间不同OP_OUTPUT的参数在语义上虽然是“算子的输出”但在宏参数传递层面它仍然是作为调用方传入的入参出现在宏调用中——即开发者把预先创建好的输出张量对象作为实参填入宏。约束说明原文档给出了一条关键约束如果算子的输出既不是aclTensor也不是aclTensorList需要先调用aclOpExecutor::ConvertToTensor将该参数转换为aclTensor后再使用OP_OUTPUT封装。这条约束的工程背景是算子框架的图构建与 Kernel 调度统一按aclTensor语义处理输出因此一切非张量形态的输出如标量、整型数组、浮点数组、bool 数组、fp16/bf16 数组等都必须先“张量化”。在 op_executor.h 中可以看到ConvertToTensor为各类数据类型提供了重载const aclTensor* ConvertToTensor(const aclIntArray* value, op::DataType dataType); const aclTensor* ConvertToTensor(const aclBoolArray* value, op::DataType dataType); const aclTensor* ConvertToTensor(const aclFloatArray* value, op::DataType dataType); const aclTensor* ConvertToTensor(const aclFp16Array* value, op::DataType dataType); const aclTensor* ConvertToTensor(const aclBf16Array* value, op::DataType dataType); const aclTensor* ConvertToTensor(const aclScalar* value, op::DataType dataType); template typename T const aclTensor* ConvertToTensor(const T* value, uint64_t size, op::DataType dataType);即标量aclScalar、各类数组aclIntArray/aclBoolArray/aclFloatArray/aclFp16Array/aclBf16Array以及裸指针 长度的组合均可在指定op::DataType后转换为aclTensor从而满足OP_OUTPUT的封装前提。调用示例原文档示例封装算子的 1 个输出参数addOut。// 封装算子的1个输出参数addOut OP_OUTPUT(addOut);多输出场景下可以直接在可变参数中罗列多个输出例如// 封装算子的多个输出参数 OP_OUTPUT(out0, out1);底层实现原理宏如何变成参数上下文OP_OUTPUT并非黑魔法其定义在 op_arg_def.h 中只有一行#define OP_OUTPUT(x...) op::OpOutput(std::make_tuple(x))结合同一文件的 宏定义基础设施OP_OUTPUT的完整推导链路如下构造OpOutput对象op::OpOutput由DEFINE_OP_ARG(OpOutput, OP_OUTPUT_ARG)生成op_arg_def.h它继承自OpArgBaseT...内部持有std::tupleT...并通过静态成员value OP_OUTPUT_ARG标识自己的参数类别。参数类别编号OpArgDef枚举为每一类参数分配了编号op_arg_def.h枚举数值语义OP_INPUT_ARG0输入OP_OUTPUT_ARG1输出本宏OP_ATTR_ARG2属性OP_WORKSPACE_ARG3工作空间OP_OUTSHAPE_ARG4输出形状OP_OPTION_ARG5可选项OP_EXEC_MODE_ARG6执行模式上下文装载OpArgContextInitop_arg_def.h会遍历传入的各个OpArgBase派生对象通过t.VisitByNoReturn(...)逐个访问 tuple 中的元素并调用AppendOpArg将其转换成统一的OpArg描述然后按类别累加计数写入ctx.argLists[opArgType]。也就是说OP_OUTPUT(addOut)最终会在OpArgContext中登记一个类别为OP_OUTPUT_ARG、内容为addOut张量的OpArg。上下文创建GetOpArgContext/MakeOpArgContextop_arg_def.h负责一次性分配OpArgContext与参数数组的连续内存并将上面登记的参数装载进去得到后续执行器可直接使用的OpArgContext*。工程中的真实使用样例样例一memsetV2 算子参数声明在 memset_op.cpp 中MemsetV2ArgContext将输入、输出、工作空间和属性一次性组装进参数上下文const aclTensor* memsetV2WsTensor memsetInputs_[0]; memsetV2OpArgCtx_ GetOpArgContext(OP_INPUT(memsetTensors_), OP_OUTPUT(memsetTensors_), OP_WORKSPACE(memsetV2WsTensor), OP_ATTR(intAttrArray_, floatAttrArray_)); CHECK_COND(memsetV2OpArgCtx_ ! nullptr, ACLNN_ERR_INNER_NULLPTR, Create memsetv2 arg ctx failed);这里OP_OUTPUT(memsetTensors_)封装的是通过executor-AllocTensorList(...)分配的aclTensorList与OP_INPUT使用同一个张量列表——memset 场景下输入输出共享同一块显存直观体现了OP_OUTPUT对aclTensorList的封装能力。样例二NonFiniteCheck 算子参数声明在 non_finite_check_op.h 中PrepareNonFiniteCheckOpArgs使用OpArgContextInit手工初始化上下文OpArgContextInit(nonFiniteCheckOpArgCtx, nonFiniteCheckOpArg, OP_INPUT(inputTensor), OP_OUTPUT(outputTensor), OP_WORKSPACE(workspaceTensor));这里OP_OUTPUT(outputTensor)封装的是OpArgType::OPARG_ACLTENSOR类型的单个输出张量见同文件上方outputArg.type OpArgType::OPARG_ACLTENSOR的构造逻辑。执行器侧输出参数如何被消费OP_OUTPUT声明的参数最终会被aclOpExecutor从OpArgContext中取出并用于构建算子图。在 op_executor.cpp 中不同调度路径都会调用args-GetOpArg(op::OP_OUTPUT_ARG)取出输出参数列表aclnnStatus aclOpExecutor::AddToKernelLauncherListDvpp(uint32_t opType, op::KernelLauncher* obj, op::OpArgContext* args) { kernelLaunchObjList_.emplace_back(obj); auto ret op::internal::BuildGraph(impl_-GetGraph(), opType, *args-GetOpArg(op::OP_INPUT_ARG), *args-GetOpArg(op::OP_OUTPUT_ARG), *args-GetOpArg(op::OP_WORKSPACE_ARG)); return ret; } aclnnStatus aclOpExecutor::AddToKernelLauncherListAiCpu(int32_t opType, op::KernelLauncher* obj, op::OpArgContext* args) { impl_-AbandonCache(true); kernelLaunchObjList_.emplace_back(obj); auto ret op::internal::BuildGraph(impl_-GetGraph(), opType, *args-GetOpArg(op::OP_INPUT_ARG), *args-GetOpArg(op::OP_OUTPUT_ARG), *args-GetOpArg(op::OP_WORKSPACE_ARG)); return ret; }由此可见OP_OUTPUT封装的输出参数是BuildGraph图构建的三大核心输入输入、输出、工作空间之一。此外在非有限数检查NonFiniteCheck流程中输出参数还会被进一步取出并反序列化为真实张量使用见 non_finite_check_op.h 中nonFiniteCheckOpArgCtx.GetOpArg(OpArgDef::OP_OUTPUT_ARG)的取用方式。常见错误与使用建议输出类型不合规若输出参数不是aclTensor/aclTensorList例如aclScalar、裸指针数组直接使用OP_OUTPUT会导致参数无法被框架按张量语义消费必须先调用aclOpExecutor::ConvertToTensor转换这正是 op_executor.h 提供多重重载的原因。声明遗漏所有需要参与图构建与 Kernel 调度的输出都必须显式通过OP_OUTPUT声明遗漏声明会导致执行器侧GetOpArg(op::OP_OUTPUT_ARG)取到的参数列表不完整进而影响BuildGraph的图结构。顺序一致性OP_OUTPUT中输出的排列顺序应与算子原型定义、Tiling 侧解析顺序保持一致避免输出张量错位。与OP_OUTSHAPE区分OP_OUTSHAPEop_arg_def.h用于声明输出形状信息与OP_OUTPUT的输出张量本体是不同维度的参数使用时注意区分。总结OP_OUTPUT是 opbase 算子参数声明体系中最基础也最重要的宏之一它在语法层面将算子输出封装为统一的OpArg在语义层面把输出张量aclTensor/aclTensorList与执行器的图构建、Kernel 调度链路对接起来。理解它的原型、约束与底层实现宏定义 →OpOutput对象 →OpArgContext→BuildGraph是写出可正确运行的自定义算子的第一步。配合 OP_INPUT、OP_WORKSPACE、OP_OUTSHAPE 等同类宏文档可完整掌握 opbase 的参数声明全景。【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考