asc-devkit 算子原型定义中的 Version 接口:为 aclnn 单算子 API 提供版本化兼容管理
人工智能深度学习算子库CANNAscend【免费下载链接】asc-devkit本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言原生支持C和C标准规范主要由类库和语言扩展层构成提供多层级API满足多维场景算子开发诉求。项目地址https://gitcode.com/cann/asc-devkit点击查看免费下载导读在 CANN asc-devkit 中算子原型通过OpDef完成注册后编译器会自动为算子生成单算子 APIaclnnxxx接口。当算子后续演进、需要新增可选输入或可选属性时直接修改原型会导致已发布的aclnnxxx接口签名变化破坏既有调用方的兼容性。OpParamDef::Version(uint32_t version)正是为解决这一问题而设计通过在可选参数上标注版本号让自动生成的aclnnxxx接口按版本演进新版本接口完整包含低版本接口的全部参数旧调用方则继续使用低版本接口。读完本文你将掌握 Version 接口的函数原型、参数含义、编号规则并通过仓库中的真实原型定义与生成代码理解从算子原型到多版本 aclnn 接口的完整生成链路与约束条件。Version 接口解决什么问题在 asc-devkit 的算子开发流程中算子原型注册完成后编译器会自动为算子生成单算子 APIaclnnxxx接口。这一点在 Version 官方文档 的开篇即有明确说明算子编译部署后会自动生成单算子 APIaclnnxxx接口接口中的输入输出参数和算子原型定义中保持一致。也就是说aclnnxxx接口的参数列表并不是手工维护的而是由算子原型定义OpDef直接推导出来的。原型中的每个输入、输出、属性最终都会以对应类型出现在生成的aclnnxxx接口签名中。这里隐含一个工程难题算子上线后往往需要迭代演进例如为算子新增一个可选输入如偏置、缩放因子、额外权重等。如果直接修改原型定义那么重新编译后aclnnxxx接口的参数列表就会变化所有调用旧接口的存量代码将面临编译失败或行为不兼容的风险。Version接口正是为化解这一矛盾而生新增可选输入时通过 Version 接口为参数指定版本号让自动生成的 aclnn 接口携带版本信息实现接口的向后兼容演进。函数原型与参数说明Version是OpParamDef类的成员函数用于为算子参数输入或属性指定版本号。函数原型如下OpParamDef Version(uint32_t version)参数输入/输出说明version输入指定的版本号类型为uint32_t无符号 32 位整数。函数返回值为OpParamDef即算子参数定义对象本身的引用因此可以与其他配置接口如ParamType、DataType、Format等链式调用。关于OpParamDef类的完整成员函数集合可参考 OpParamDef 文档。版本号编号规则文档中对版本号的编排提出了两条明确要求版本号需要从 1 开始配置版本号应该连续配置且与可选属性统一编号。所谓和可选属性统一编号是指同一个算子原型中可选输入Input与可选属性Attr共享同一套版本号序列。这一规则在 OpAttrDef_functions 文档 中有完全一致的表述——OpAttrDef同样提供了OpAttrDef Version(uint32_t version)成员函数用于为新增的可选属性配置版本号两者必须放在同一个编号序列中统一管理。配置后自动生成的 aclnn 接口会携带版本号并且高版本号的接口会包含低版本号接口的所有参数。这意味着版本号本质上是参数的出现层级Version(2)的参数只在 V2 及以上的接口中出现而Version(1)的参数在 V1 和 V2 接口中都会出现。完整示例给算子新增可选输入算子原型定义以下示例来自 Version 官方文档演示了如何在算子原型中为新增的可选输入配置版本号class AddCustom : public OpDef { public: explicit AddCustom(const char* name) : OpDef(name) { this-Input(x) .ParamType(DYNAMIC) .DataType({ge::DT_FLOAT16, ge::DT_FLOAT}) .Format({ge::FORMAT_ND, ge::FORMAT_ND}); this-Input(x1) .ParamType(OPTIONAL) .Version(1) .DataType({ge::DT_FLOAT16, ge::DT_FLOAT}) .Format({ge::FORMAT_ND, ge::FORMAT_ND}); this-Input(x2) .ParamType(OPTIONAL) .Version(2) .DataType({ge::DT_FLOAT16, ge::DT_FLOAT}) .Format({ge::FORMAT_ND, ge::FORMAT_ND}); this-Output(y) .ParamType(DYNAMIC) .DataType({ge::DT_FLOAT16, ge::DT_FLOAT}) .Format({ge::FORMAT_ND, ge::FORMAT_ND}); this-AICore().AddConfig(xxx); } }; OP_ADD(AddCustom);原型定义的关键点输入x、输出y的ParamType为DYNAMIC动态输入它们属于所有版本接口的公共部分新增的可选输入x1通过ParamType(OPTIONAL)标记为可选并通过.Version(1)归属到 V1 版本后续新增的可选输入x2通过.Version(2)归属到 V2 版本ParamType的取值包括OPTIONAL可选、REQUIRED必选、DYNAMIC动态输入详见 ParamType 文档。自动生成的三个版本接口基于上述原型编译器会自动生成 3 个版本的 aclnn 接口。其中不带版本后缀的接口可视为 V0只包含动态输入/输出参数aclnnStatus aclnnAddCustomGetWorkspaceSize( const aclTensorList *x, const aclTensorList *out, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnAddCustom( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream); aclnnStatus aclnnAddCustomV1GetWorkspaceSize( const aclTensorList *x, const aclTensor *x1Optional, const aclTensorList *out, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnAddCustomV1( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream); aclnnStatus aclnnAddCustomV2GetWorkspaceSize( const aclTensorList *x, const aclTensor *x1Optional, const aclTensor *x2Optional, const aclTensorList *out, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnAddCustomV2( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream);对比三个版本可以看到版本演进的核心规律V1 接口在 V0 基础上增加了x1Optional参数对应原型中Version(1)的输入x1V2 接口在 V1 基础上再增加x2Optional参数对应原型中Version(2)的输入x2每个版本都保留aclnnxxxGetWorkspaceSize计算 workspace 大小并创建 executor与aclnnxxx执行算子成对出现的两个接口可选参数的命名遵循参数名 Optional后缀的约定如x1Optional、x2Optional便于调用方识别。这套接口的演进完全符合文档中的承诺——高版本号的接口包含低版本号接口的所有参数因此使用 V0 接口的存量代码无需任何修改即可继续工作。仓库中的真实版本化原型VersionTest在仓库的测试工程中存在一个与上述示例完全同构的真实用例。test_ops.cpp 中定义了VersionTest算子原型同时为可选输入和可选属性配置了版本号class VersionTest : public OpDef { public: VersionTest(const char* name) : OpDef(name) { this-Input(x1).DataType({ge::DT_FLOAT}); this-Input(x2).DataType({ge::DT_INT64}); this-Input(x3).ParamType(OPTIONAL).DataType({ge::DT_INT64}).Version(1); this-Input(x4).ParamType(OPTIONAL).DataType({ge::DT_INT64}).Version(2); this-Output(y).DataType({ge::DT_FLOAT}); this-Output(x1).DataType({ge::DT_FLOAT}); this-Attr(bias1).AttrType(OPTIONAL).Int(1).Version(1); this-Attr(bias2).AttrType(OPTIONAL).Float(0.0).Version(2); this-Attr(bias3).AttrType(OPTIONAL).Bool(true).Version(2); this-Attr(bias4).AttrType(OPTIONAL).ListBool({true, false}).Version(1); this-Attr(bias5).AttrType(OPTIONAL).ListFloat({0.1, 0.2}).Version(1); this-Attr(bias6).AttrType(OPTIONAL).ListInt({1, 2}).Version(2); this-Attr(bias7).AttrType(OPTIONAL).String(ssss).Version(2); OpAICoreConfig aicConfig; aicConfig.ExtendCfgInfo(aclnnSupport.value, aclnn_only); this-AICore().AddConfig(ascend910, aicConfig); this-EnableFallBack(); } }; OP_ADD(VersionTest);这个用例直观地展示了两个重要事实可选输入与可选属性共用版本序列输入x3/x4与属性bias1bias7都使用从 1 开始、连续递增的版本号Version(1)、Version(2)二者处于同一编号体系印证了官方文档中和可选属性统一编号的规则Version 可用于各种属性数据类型Int、Float、Bool、ListBool、ListFloat、ListInt、String等属性均可通过AttrType(OPTIONAL)Version(n)组合参与版本化演进且可选属性配置默认值时如Int(1)、Float(0.0)在低版本接口中会被自动填充。自动生成的 V1、V2 接口源码佐证仓库中保留了该原型自动生成的接口源码文件可以对照查看版本间差异aclnn_version_test_v2.h 与 aclnn_version_test_v2.cpp.txtV2 接口包含全部参数即x3Optional、x4Optional以及bias1bias7全部属性aclnn_version_test_v1.h 与 aclnn_version_test_v1.cpp.txtV1 接口只包含Version(1)及更低版本的参数x3Optional、bias1、bias4Optional、bias5Optional不包含Version(2)的参数。值得关注的是 aclnn_version_test_v1.cpp.txt 中低版本接口的实现方式——它并不是重新实现一套逻辑而是直接调用高版本接口并填充默认值aclnnStatus aclnnVersionTestV1GetWorkspaceSize( aclTensor *x1Ref, const aclTensor *x2, const aclTensor *x3Optional, int64_t bias1, const aclBoolArray *bias4Optional, const aclFloatArray *bias5Optional, const aclTensor *yOut, uint64_t *workspaceSize, aclOpExecutor **executor) { static float bias2Def 0; static bool bias3Def true; static int64_t bias6OptionalDef[] {1, 2}; static aclIntArray *bias6OptionalArray aclCreateIntArray(bias6OptionalDef, 2); static char *bias7OptionalDef ssss; aclnnStatus ret aclnnVersionTestV2GetWorkspaceSize( x1Ref, x2, x3Optional, NULL, bias1, bias2Def, bias3Def, bias4Optional, bias5Optional, bias6OptionalArray, bias7OptionalDef, yOut, workspaceSize, executor); if (ret ! ACLNN_SUCCESS) { return ret; } if (NnopbaseDisableOptionalInput ! NULL executor ! nullptr) { NnopbaseDisableOptionalInput(*executor, 3); } return ret; }从这段生成代码可以观察到版本兼容机制的底层实现策略对于 V1 中没有的Version(2)属性参数bias2、bias3、bias6Optional、bias7Optional调用 V2 接口时使用原型定义中声明的默认值如bias2Def 0、bias3Def true填充对于 V1 中没有的Version(2)可选输入x4直接传入NULL再通过NnopbaseDisableOptionalInput(*executor, 3)在 executor 中禁用对应的可选输入槽位执行接口aclnnVersionTestV1则直接透传给aclnnVersionTestV2完成算子执行。也就是说多版本 aclnn 接口在运行时共享同一套 V2 实现低版本接口只是裁剪参数 补齐默认值的薄封装这正是版本化方案既能保证接口兼容、又不引入重复实现的根本原因。版本号使用的边界约束哪些参数可以配置 Version虽然文档中约束说明标注为无但从仓库测试用例可以推断出 Version 接口实际存在明确的适用范围。在 test_opbuild.cpp 中有三个针对性测试用例专门验证非法配置场景用例 1必选输入配置版本号会被拒绝。对必选输入调用Version(1)时生成器报错VersionFail only optional input can configured version.用例 2必选属性配置版本号会被拒绝。对AttrType(REQUIRED)的属性调用Version(1)时生成器报错VersionFail only optional attr can configured version.用例 3输出参数配置版本号不被支持。对Output调用Version(1)时生成器报错The output version of op VersionFail does not support.由此可以总结 Version 接口的使用边界参数类别是否允许配置 Version可选输入ParamType(OPTIONAL)的 Input允许可选属性AttrType(OPTIONAL)的 Attr允许必选输入 / 必选属性REQUIRED不允许配置时报错输出Output不允许配置时报错其内在逻辑是合理的必选参数在所有版本中都必须出现版本号对它没有意义输出参数的形态由算子语义决定不应随版本变化只有新增的可选参数才需要版本号来界定从哪个接口版本开始引入从而实现向后兼容。版本号与可选属性接口的配套使用Version接口并不局限于OpParamDef。当算子新增可选属性时OpAttrDef提供了同名接口OpAttrDef Version(uint32_t version)二者必须统一编号。官方 OpAttrDef_functions 文档 对 Version 的说明为新增可选属性时为了保持原有单算子 APIaclnnxxx接口的兼容性可以通过 Version 接口配置 aclnn 接口的版本号版本号需要从 1 开始配且应该连续配置和可选输入统一编号。配置后自动生成的 aclnn 接口会携带版本号。高版本号的接口会包含低版本号接口的所有参数。这一表述与 Version 官方文档 中的规则完全一致只是将可选输入与可选属性互换了主语进一步确认了二者必须纳入同一版本序列的约定。小结OpParamDef::Version(uint32_t version)是 asc-devkit 算子原型注册体系中实现 aclnn 单算子 API 向后兼容的关键接口规则版本号从 1 开始、连续配置可选输入与可选属性统一编号效果自动生成多版本aclnnxxx接口高版本接口包含低版本全部参数无版本号的接口保持原有签名不变边界仅可选输入与可选属性允许配置版本号必选参数与输出禁止配置实现低版本接口通过补齐默认值、禁用可选输入槽位的方式透传调用高版本接口运行期共享同一套执行逻辑。在算子迭代开发中建议遵循如下实践任何可能破坏aclnnxxx接口签名的新增可选参数都应在原型定义中显式标注 Version并保证编号连续、统一从而让接口演进可预期、可维护。更多相关接口可继续查阅 OpParamDef 文档、OpAttrDef 文档 与 原型注册与管理总览。赞分享人工智能深度学习算子库CANNAscend【免费下载链接】asc-devkit本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言原生支持C和C标准规范主要由类库和语言扩展层构成提供多层级API满足多维场景算子开发诉求。项目地址https://gitcode.com/cann/asc-devkit点击查看免费下载相关推荐asc-devkit 自定义算子开发实战aclnn 单算子 API 调用全解析asc devkit 自定义算子开发实战aclnn 单算子 API 调用全解析 本文基于 asc devkit 仓库中的 aclnn_invocation 样人工智能深度学习算子库CANNAscendasc-devkit 中 AICPU 自定义算子原型注册之 OpkernelLib 接口详解asc devkit 中 AICPU 自定义算子原型注册之 OpkernelLib 接口详解 导读 本文围绕 asc devkit 算子原型注册框架中的 Opk人工智能深度学习算子库CANNAscendasc-devkit 算子原型注册OpAICPUDef::UserDefined 接口详解——如何声明 AICPU 自定义算子asc devkit 算子原型注册OpAICPUDef::UserDefined 接口详解——如何声明 AICPU 自定义算子 导读 在 CANN 昇腾 AI人工智能深度学习算子库CANNAscend上一篇八大网盘直链下载终极指南LinkSwift开源工具完全教程下一篇Douzy 教程4 类内容无水印批量下载创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考