- 人工智能
- 深度学习
- 算子库
- CANN
- Ascend
【免费下载链接】asc-devkit
本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。
在昇腾 AI 处理器的算子开发中,同一个算子在不通的 AI 处理器型号(SoC)上往往需要支持不同的输入原型。本文以 asc-devkit 开源仓库中的原型注册与管理组件为背景,系统讲解OpAICoreConfig::Input(const char *name)接口的定位、函数原型、链式配置方法以及如何通过AddConfig与REGISTER_OP_AICORE_CONFIG将差异化输入绑定到具体芯片型号。读完本文,你将掌握在OpDef算子原型中为不同 SoC 分别声明输入名称、参数类型、数据类型与数据格式的完整实战方案。
一、接口定位:为什么需要按 AI 处理器型号注册差异化输入
在 算子原型定义 中,算子原型通过一个继承自OpDef的 C++ 类承载,类内使用Input、Output、Attr等接口声明算子对外暴露的接口,并用OP_ADD宏完成注册,从而让框架能够正确识别、编译和执行该算子。
然而在实际场景中,同一个算子在不同的 AI 处理器型号上,其支持的原型输入可能不同。例如某型号只接受DT_FLOAT16输入,而另一型号支持DT_FLOAT或DT_INT32;又或者某个输入在 A 型号上是必选(REQUIRED),在 B 型号上是可选(OPTIONAL)。如果把这些差异全部堆在通用原型里,会导致原型定义臃肿且无法精确表达每个型号的真实能力。
OpAICoreConfig::Input就是为此设计的:它允许开发者在OpAICoreConfig这一按 SoC 维度组织的配置结构中,针对不同的 AI 处理器型号注册差异化的算子原型输入,进而结合AddConfig把该配置与具体芯片型号关联起来。
继承与覆盖规则
从 算子原型定义 的说明可以确认两条核心规则:
OpAICoreConfig中未配置的输入输出,继承OpDef中的通用定义;- 重新配置的同名输入输出,覆盖通用定义。
也就是说,Input("x")既可以在通用OpDef构造函数中使用(声明算子默认输入),也可以在OpAICoreConfig中使用(为特定型号覆盖x的定义)。两种用法返回的都是OpParamDef引用,用于继续链式配置。
二、函数原型与参数说明
OpAICoreConfig::Input的函数原型如下:
OpParamDef &Input(const char *name)| 参数 | 输入/输出 | 说明 |
|---|---|---|
| name | 输入 | 算子输入名称。 |
参数含义与OpDef层的同名接口一致:传入算子输入的名称字符串,用于声明该输入在原型中的身份标识。声明顺序会影响 aclnn 接口参数顺序和核函数(Kernel)入口参数顺序,因此在OpAICoreConfig中重新配置同名输入时,名称必须与通用原型中的输入名称保持一致,否则无法形成覆盖关系。
三、返回值:OpParamDef 及链式配置方法
调用Input后返回一个OpParamDef结构的引用,后续可以通过该结构继续配置算子的输入信息。OpParamDef是算子参数定义的通用载体,完整成员可参考 OpParamDef,其中在输入配置中最常用的是下面三个链式方法。
ParamType:声明参数类型
OpParamDef &ParamType(Option param_type)Option取值为:
OPTIONAL:可选输入,部分场景可以省略;REQUIRED:必选输入,每次调用都必须提供;DYNAMIC:动态输入,数量随调用变化。
DataType:声明参数数据类型
OpParamDef &DataType(std::vector<ge::DataType> types)传入ge::DataType枚举的列表,声明该输入支持的数据类型。多个数据类型按列表顺序与Format列表一一对应。
Format:声明参数数据格式
OpParamDef &Format(std::vector<ge::Format> formats)传入ge::Format枚举的列表,声明该输入支持的数据格式。例如FORMAT_ND、FORMAT_NCHW、FORMAT_NCL等。DataType与Format两个列表按位置一一对应,例如DataType({ge::DT_FLOAT16, ge::DT_FLOAT})配合Format({ge::FORMAT_ND, ge::FORMAT_ND})表示支持 "DT_FLOAT16 + FORMAT_ND" 与 "DT_FLOAT + FORMAT_ND" 两组组合。
三个方法都返回OpParamDef &,因此可以连续链式调用:
aicConfig1.Input("x") .ParamType(OPTIONAL) .DataType({ ge::DT_FLOAT }) .Format({ ge::FORMAT_ND });四、完整调用示例与逐行解析
原文档给出的调用示例完整展示了如何在OpDef构造函数内为两个不同 AI 处理器型号注册差异化输入:
class AddCustom : public OpDef { public: AddCustom(const char* name) : OpDef(name) { this->Input("x").DataType({ ge::DT_FLOAT16 }).ParamType(OPTIONAL); this->Output("y").DataType({ ge::DT_FLOAT16 }); OpAICoreConfig aicConfig1; OpAICoreConfig aicConfig2; aicConfig1.Input("x") .ParamType(OPTIONAL) .DataType({ ge::DT_FLOAT }) .Format({ ge::FORMAT_ND }); aicConfig2.Input("x") .ParamType(REQUIRED) .DataType({ ge::DT_INT32 }) .Format({ ge::FORMAT_ND }); this->AICore().AddConfig("ascendxxx1", aicConfig1); this->AICore().AddConfig("ascendxxx2", aicConfig2); } };逐段说明:
- 通用原型定义:
this->Input("x")声明算子通用输入x,默认支持DT_FLOAT16且为OPTIONAL;this->Output("y")声明输出y。这是所有型号共享的基线定义。 - 构造两个型号的差异化配置:
OpAICoreConfig aicConfig1;与OpAICoreConfig aicConfig2;使用无参构造函数创建。 - 型号 1 覆盖输入
x:aicConfig1.Input("x")将输入x重新定义为OPTIONAL、DT_FLOAT、FORMAT_ND。 - 型号 2 覆盖输入
x:aicConfig2.Input("x")将输入x重新定义为REQUIRED、DT_INT32、FORMAT_ND。 - 绑定型号:
this->AICore().AddConfig("ascendxxx1", aicConfig1)与AddConfig("ascendxxx2", aicConfig2)分别把两份差异化配置注册到两个 AI 处理器型号。示例中的ascendxxx1、ascendxxx2是占位符,实际应替换为真实的芯片型号。
这个模式与 OpAICoreConfig::Output 完全对称——输出差异化配置使用Output("y"),写法一致。
五、将差异化配置绑定到具体 AI 处理器:AddConfig
Input只负责声明输入,真正把配置和芯片型号绑定起来的是AICore().AddConfig(...),其接口定义参见 AddConfig:
void AddConfig(const char *soc) void AddConfig(const char *soc, OpAICoreConfig &aicore_config)| 参数 | 输入/输出 | 说明 |
|---|---|---|
| soc | 输入 | 支持的 AI 处理器型号。填写规则请参考算子工程目录下编译配置项文件CMakePresets.json中的ASCEND_COMPUTE_UNIT字段,该字段取值在使用 msOpGen 创建工程时自动生成。 |
| aicore_config | 输入 | AI Core 配置信息,即OpAICoreConfig定义。 |
使用注意事项:
- 不传
aicore_config时,OpAICoreConfig结构中的部分参数会配置为默认值,具体如下表(详见 AddConfig):
| 配置参数 | 说明 | 默认值 |
|---|---|---|
| DynamicCompileStaticFlag | 标识该算子实现是否支持入图时的静态 Shape 编译 | true |
| DynamicFormatFlag | 标识是否根据SetOpSelectFormat设置的函数自动推导算子输入输出支持的 dtype 和 format | true |
| DynamicRankSupportFlag | 标识算子是否支持 dynamicRank(动态维度) | true |
| DynamicShapeSupportFlag | 标识该算子是否支持入图时的动态 Shape 场景 | true |
| NeedCheckSupportFlag | 标识是否在算子融合阶段调用算子参数校验函数进行 data type 与 Shape 的校验 | false |
| PrecisionReduceFlag | 用于 ATC 模型转换或网络调测时控制算子的精度模式 | true |
- 传入
aicore_config时,除了输入输出原型外,还可以通过 DynamicShapeSupportFlag(bool flag) 这类方法逐个覆盖上述开关。例如在测试代码中可以看到链式写法:
aicConfig.DynamicCompileStaticFlag(true) .DynamicFormatFlag(true) .DynamicRankSupportFlag(true) .DynamicShapeSupportFlag(true) .NeedCheckSupportFlag(true) .PrecisionReduceFlag(true); this->AICore().AddConfig("ascend310p"); this->AICore().AddConfig("ascend910", aicConfig); this->AICore().AddConfig("ascendxxx", aicConfig);上述代码来自仓库测试用例 test_ops.cpp,它演示了"部分型号使用默认配置、部分型号使用差异化配置"的混合注册方式。
六、OpAICoreConfig 构造函数与初始化行为
OpAICoreConfig共有三个构造函数,详见 OpAICoreConfig 构造函数:
OpAICoreConfig() OpAICoreConfig(const char *soc) OpAICoreConfig(const OpAICoreConfig &aicore_config)| 参数名 | 输入/输出 | 描述 |
|---|---|---|
| soc | 输入 | AI 处理器型号。 |
| aicore_config | 输入 | 已构造完成的 OpAICoreConfig,用于拷贝构造 OpAICoreConfig。 |
需要特别注意的是三种构造函数的行为差异:
- 传入
soc的构造函数会对OpAICoreConfig结构中的部分参数进行初始化:DynamicCompileStaticFlag、DynamicFormatFlag、DynamicRankSupportFlag、DynamicShapeSupportFlag、PrecisionReduceFlag初始化为true,NeedCheckSupportFlag初始化为false; - 无入参的默认构造函数不会初始化上述参数;
- 拷贝构造函数会复制被拷贝对象中已配置的输入、输出和配置项信息,适合在已有配置基础上派生新型号配置。
构造函数同样支持在REGISTER_OP_AICORE_CONFIG独立注册场景中使用(见下一节)。
七、独立文件注册:REGISTER_OP_AICORE_CONFIG
当差异化配置较多、希望按芯片或模块拆分维护时,可以不修改原有原型注册文件,而是通过REGISTER_OP_AICORE_CONFIG宏在独立文件中注册,详见 REGISTER_OP_AICORE_CONFIG。该宏"在不改变原有注册的基础上,允许单独新增文件来注册算子在不同硬件形态上的差异化信息"。
使用该注册宏需要包含头文件:
#include "register/op_config_registry.h"宏原型:
REGISTER_OP_AICORE_CONFIG(opType, socVersion, opFunc)| 参数 | 输入/输出 | 说明 |
|---|---|---|
| opType | 输入 | 算子类型。 |
| socVersion | 输入 | 支持的 AI 处理器型号。 |
| opFunc | 输入 | 返回 OpAICoreConfig 的回调函数指针,回调函数原型为OpAICoreConfig (*)()。 |
调用示例:假设已有原型注册文件op_host/add_custom.cpp实现了AddCustom并配置了型号ascendxxx1,可以新增文件op_host/add_custom_xxx.cpp,使用该宏单独注册型号ascendxxx2:
#include "register/op_config_registry.h" namespace ops { REGISTER_OP_AICORE_CONFIG(AddCustom, ascendxxx2, []() { ops::OpAICoreConfig config("ascendxxx2"); return config; }); }回调函数体内可以像在构造函数中一样调用config.Input("x").ParamType(...).DataType(...).Format(...)完成输入原型配置后返回。
优先级约束:若算子同时使用AddConfig注册算子支持的 AI 处理器型号及OpAICoreConfig信息,且 AI 处理器型号相同时,通过AddConfig方式注册的配置优先级更高,会覆盖REGISTER_OP_AICORE_CONFIG宏注册的OpAICoreConfig信息。这一规则在 REGISTER_OP_AICORE_CONFIG 与 算子原型定义 中均有明确说明。
八、仓库中的真实应用佐证
1. 测试用例中的多型号差异化输入
在 test_ops.cpp 中,AddTik2算子即为一个完整的差异化原型示例:通用原型定义了x1、x2两个输入(分别为OPTIONAL与DYNAMIC),随后构造aicConfig为x1、x2分别配置了不同的ParamType、DataType、Format以及(废弃的)UnknownShapeFormat,最后对ascend310p使用默认配置、对ascend910与ascendxxx使用差异化配置注册。这印证了"一个算子、多份配置、按型号分发"的标准用法。
2. 样例工程中的实际注册
在 add_custom_host.cpp 中,AddCustom算子的原型定义展示了最简注册路径:
namespace ops { class AddCustom : public OpDef { public: explicit AddCustom(const char* name) : OpDef(name) { this->Input("x").ParamType(REQUIRED).DataType({ge::DT_FLOAT16}).Format({ge::FORMAT_ND}); this->Input("y").ParamType(REQUIRED).DataType({ge::DT_FLOAT16}).Format({ge::FORMAT_ND}); this->Output("z").ParamType(REQUIRED).DataType({ge::DT_FLOAT16}).Format({ge::FORMAT_ND}); this->SetInferShape(ge::InferShape).SetInferDataType(ge::InferDataType); this->AICore().SetTiling(optiling::TilingFunc).AddConfig("ascend910b"); } }; OP_ADD(AddCustom); } // namespace ops这里AICore().AddConfig("ascend910b")使用无aicore_config的重载,即为型号ascend910b采用默认配置注册。开发者可在此基础上按第四节示例扩展为多型号差异化输入注册。
九、使用约束与最佳实践
- 名称必须一致:
OpAICoreConfig中Input("x")的名称要与OpDef通用原型中的输入名称一致,才能形成"同名覆盖";未在OpAICoreConfig中声明的输入继承通用定义。 - 关注声明顺序:输入注册顺序会影响 aclnn 接口参数顺序和核函数(Kernel)入口参数顺序,差异化覆盖时不宜打乱原有顺序。
- dtype 与 format 按位置对应:
DataType与Format均为列表且按位置一一对应,多组组合要逐项对齐;若希望某输入与已定义输入完全一致,可考虑使用 Follow 简化表达。 - 型号命名以工程配置为准:
AddConfig的soc参数应参考算子工程CMakePresets.json中ASCEND_COMPUTE_UNIT字段(msOpGen 创建工程时自动生成),不要随意捏造型号名。 - 两种注册方式按需选择:在构造函数内使用
AICore().AddConfig(...)适合配置较少、内聚性强的场景;REGISTER_OP_AICORE_CONFIG独立文件注册适合按芯片或模块拆分维护,但同型号下前者优先级更高。 - 配置项覆盖要谨慎:
OpAICoreConfig的六个 Flag 控制静态编译、动态 Shape、动态 Rank、format 推导、融合校验与精度模式等编译期行为,仅在确实需要差异时才显式覆盖,避免与算子实际能力不符导致入图或编译失败。
通过OpAICoreConfig::Input与AddConfig/REGISTER_OP_AICORE_CONFIG的组合,开发者可以用一套算子代码精确描述多芯片形态下的输入契约,这也是 CANN 算子原型注册与管理体系(参见 原型注册与管理)的核心能力之一。
- 人工智能
- 深度学习
- 算子库
- CANN
- Ascend
【免费下载链接】asc-devkit
本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。
相关推荐
asc-devkit 算子原型注册:OpAICoreConfig::Output 按 AI 处理器型号差异化注册算子输出
asc devkit 算子原型注册:OpAICoreConfig::Output 按 AI 处理器型号差异化注册算子输出 本文面向在 asc devkit(CA
人工智能深度学习算子库CANNAscendasc-devkit 算子原型注册详解:OpAICoreConfig 分 AI 处理器型号配置算子原型与动态编译能力
asc devkit 算子原型注册详解:OpAICoreConfig 分 AI 处理器型号配置算子原型与动态编译能力 在 CANN asc devkit 算子开
人工智能深度学习算子库CANNAscendasc-devkit 算子原型注册:OpAICoreConfig 构造函数详解与差异化配置实践
asc devkit 算子原型注册:OpAICoreConfig 构造函数详解与差异化配置实践 导读 本文深入解析 CANN asc devkit 算子程序开发
人工智能深度学习算子库CANNAscend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考