CANN/ge:历史原型库设计文档(ES 场景) 历史原型库设计文档ES 场景【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge0. 阅读指引本文聚焦 ES 场景gen_esb如何消费历史原型数据并生成 ES API重点是第 4 章重载生成与二义性处理。历史原型库的协议性内容定位/目录/格式/生成与发布/演进规则见协议文档。0.1 术语最小集合历史原型库跨版本归档算子 IR 原型的结构化数据与协议。结构化数据index.json/metadata.json/operators.json三类文件及其目录组织。生产者生成结构化数据的工具当前固定为gen_esb --extract-history。消费者读取结构化数据的任意工具当前主要是gen_esb未来可扩展到其他工具链。一年窗口/兼容周期消费者依据版本元信息选择纳入对比/生成的版本范围。Ops run 包Ops 安装后的运行包形态下文若出现“run 包”均指 Ops run 包。1. 概述1.1 背景在 ES (Eager Style) 构图中为了保证 C ES API 的向后 API 兼容性生成工具需要掌握不同商发版本的 IR 原型演进情况。历史原型库负责沉淀并提供这些历史原型数据供gen_esb在生成阶段对比差异、生成重载接口。需要说明gen_esb是 ES 场景的工具当前既是历史原型数据的主要消费者也是结构化数据的生成工具--extract-history。短期内不提供新的生成工具。历史原型库协议层不绑定 ES未来可被其他消费者复用。本文只描述 ES 场景的消费与生成策略。2. 模块架构与职责划分2.1 模块划分历史原型库系统由两个核心模块组成历史原型库模块Ops组件 Ops包负责存储和管理历史原型数据消费、生成工具模块gen_esb GE仓 GE组件 GE包消费者角色负责读取历史数据生成 C 重载接口生产者角色负责生成历史原型库数据复用gen_esb --extract-history2.2 历史原型库模块职责一句话历史原型库模块的职责是存储并以文件系统形式提供历史原型结构化数据。边界只提供数据与元信息版本索引、算子原型等不提供业务逻辑输出不做二义性检测/兼容性判定/重载生成/代码生成协议细节目录/文件/字段见协议文档。2.3 gen_esb 工具职责gen_esb 工具负责所有与代码生成相关的逻辑。2.3.1 消费者角色2.3.1.1 数据读取从历史原型库读取兼容周期内的历史版本 IR 定义并解析 JSON 格式的历史原型数据2.3.1.2 版本对比分析对比当前版本当前版本直接从原型 .so 得来与历史版本的 IR 定义差异识别新增的可选输入、属性等默认算子原型的变化都是兼容的不进行兼容性检查GE的流程中已经有标准的兼容性检查流程并且算子原型变化兼容本身就是一个基本要求因此不做额外检查2.3.1.3 重载生成根据版本差异生成合法的重载签名同时添加必要的防呆接口详见章节2.3.1.4 代码输出生成 C 接口头文件包含多个重载版本生成 C 接口不重载生成当前版本对应的原型生成 Python 接口不重载生成当前版本对应的原型2.3.2 生产者角色2.3.2.1 历史原型库数据生成复用 gen_esb 工具在商发版本发布前使用gen_esb工具生成当前版本的原型数据从当前版本的原型 .so 文件中提取 IR 定义生成 JSON 格式的历史原型数据输出到历史原型库目录结构中2.4 模块间接口2.4.1 历史原型库提供的接口历史原型库模块通过文件系统接口提供数据2.4.1.1 分包的文件目录结构协议与目录/数据格式详见history_op_registry_protocol.md。本文仅使用其文件系统接口与关键文件名index.json版本列表含发布日期等registry/ver/operators.json该版本的算子原型定义registry/ver/metadata.json版本元信息例如发布日期、分支名2.4.2 gen_esb 工具的使用方式消费历史原型用于生成 ES API 代码gen_esb \ --history-registry /path/to/registry/math \ # 历史原型库分包目录 --output-dir /path/to/output \ # 输出目录 --release-version 8.0.RC2 # 当前版本号,用于查询兼容周期历史原型库结构化数据的生成与发布属于协议侧内容见协议文档 的“生成与发布”与“附录 JSON 示例”。2.4.3 generate_es_package 函数的使用方式generate_es_package是对gen_esb封装后的cmake函数当前参数如下:参数必需性说明示例ES_LINKABLE_AND_ALL_TARGET必需对外暴露的库 target 名称es_math,es_nn,es_cvOPP_PROTO_TARGET必需算子原型库的 CMake target 名称opgraph_math,opgraph_nnOUTPUT_PATH必需产物输出的根目录${CMAKE_BINARY_DIR}/outputEXCLUDE_OPS可选需要排除生成的算子Add,Conv2D简化思路工程组通过定义一些全局的cmake变量, generate_es_package函数内部直接获取然后进行处理无需generate_es_package新增一些参数这样的好处是 ops组件少一些感知结合当前ops的分仓分包的背景在generate_es_package函数内部处理可以避免各个分仓都要适配备选方案新增如下的参数参数必需性说明示例HISTORY_REGISTRY可选构建环境上的历史原型结构化数据目录/path/to/registry/mathEXTRACT_HISTORY可选生成历史原型结构化数据 默认不生成ONRELEASE_VERSION可选当前版本号8.0.RC2调用方Ops传递对应的参数生成 ES API 代码 额外传递HISTORY_REGISTRY可选如果有历史原型的话、RELEASE_VERSION可选 如果有历史原型的话生成ES API 历史原型库数据 额外传递HISTORY_REGISTRY可选如果有历史原型的话、RELEASE_VERSION必选 当前历史原型数据对应的版本号、EXTRACT_HISTORY必选 告知当前使能了历史原型结构化数据生成模式推荐使用简化思路2.4.4 其他需要的信息以下信息如果需要显示传递的话需要在gen_esb和generate_es_package内部处理对应的参数release_date商发时刻branch_name分支名2.5 职责边界总结功能历史原型库模块gen_esb 工具存储历史原型数据负责不负责提供数据查询接口负责文件系统形式不负责生成历史原型库数据不负责负责读取历史数据不负责负责版本对比分析不负责负责兼容性检测不负责不负责默认兼容二义性检测不负责负责重载生成不负责负责代码生成不负责负责核心原则历史原型库 数据存储 数据提供纯数据层作为 ops run 包的一部分gen_esb 数据生成 数据读取 业务逻辑 代码生成业务逻辑层3. 数据流图构建 Ops 包的“历史原型数据生成/合并/打包发布”流程属于协议侧内容见协议文档 的“生成与发布”。各组件配合流程图ES 场景工程组算子GE的配合如下具体来说商发版本初次构建时工程组传递信息编译宏的方式告诉ge完成 12 两部分逻辑根据历史原型的结构化数据和当前原型 .so 中的原型生成算子粒度的 C 重载接口历史原型库结构化数据生成(为了简化第三步ops的处理逻辑 此步骤生成当前版本历史原型库结构化数据之后会把已有的历史原型库结构化数据合并一起输出到指定目录)ops组件对1和2的产物统一打包完成ops包的构建非商发的构建或者商发版本日常构建时完成上述商发流程中的13两部分逻辑4. 重载生成与二义性处理4.1 问题根源C 要求默认参数必须在参数列表末尾基本类型的可选参数会存在重载二义性EsTensorLike支持从标量类型int64_t、float隐式转换使得标量值可以同时匹配EsTensorLike参数和int64_t参数产生二义性。4.2 解决方案4.2.1 设计约束用户侧语义约定本章节目标在不引入“版本命名空间”的前提下让历史重载接口可编译、无二义性并尽量保持用户调用习惯稳定。核心事实来自EsTensorLike定义EsTensorLike(const std::nullptr_t)可用nullptr表达“该输入未连边”可选输入语义EsTensorLike(const int64_t/float/vector...)标量/向量会被视为常量输入隐式构造核心约束用户侧语义约定面向“保留 v1、生成多版本重载”的策略当新增可选输入xo2时不使用 xo2未连边固定使用 v1 形态例如Foo(x, xo1[, attr...])保证最前向兼容使用 xo2连边使用 v2 形态例如Foo(x, xo1, xo2[, attr...])为避免Foo(x, xo1)在 v1/v2 同时存在时产生二义性v2 的xo2必须是必选参数不得提供nullptr默认值。同时生成防呆对“v2 的xo2位置传nullptr”给出 deprecateddelete 的明确诊断提示用户“未连边请用 v1”。当算子同时存在“可被标量字面量占位的属性参数”如int64_t a0时此时Foo(x, xo1, 0)固定解释为属性如a0不允许把0当作新增输入xo2若要把标量/向量作为xo2传入必须先构造成Const/Scalar/Vector如EsGraphBuilder::CreateScalar/CreateVector/CreateConst再传入4.2.2 方案A同命名空间、多版本重载推荐方案A的核心目标在同一命名空间内通过一组可编译且无二义性的重载集合兼顾后向兼容与前向兼容。4.2.2.1 A1新增输入仍用EsTensorLike但 v2 中xo2必选 nullptr防呆适用场景新增可选输入xo2不会与现有参数产生“标量字面量歧义”例如属性是const char*/std::string等不会与EsTensorLike(int64_t)冲突。做法保留 v1用于表达xo2未连边最前向兼容的调用形态v2新增xo2但把xo2生成为必选参数无默认值用于表达xo2已连边防呆额外生成std::nullptr_t的 deprecateddelete 重载提示“xo2 未连边请用 v1”示例const char*默认属性场景// v1xo2 未连边 FORCE_INLINE EsTensorLike Foo(const EsTensorLike x, const EsTensorLike xo1, const char *mode xx); // v2xo2 已连边xo2 必选避免 Foo(x, xo1) 二义性 FORCE_INLINE EsTensorLike Foo(const EsTensorLike x, const EsTensorLike xo1, const EsTensorLike xo2, const char *mode xx); // 防呆若 xo2 未连边请直接调用 v1 [[deprecated(xo2 未连边请调用 v1: Foo(x, xo1[, mode])若 xo2 需连边请传入有效 Tensor/Const。)]] FORCE_INLINE EsTensorLike Foo(const EsTensorLike x, const EsTensorLike xo1, std::nullptr_t /*xo2*/, const char *mode xx) delete;4.2.2.2 A2标量字面量歧义时切换TensorHolder兜底消歧触发条件典型新增可选输入xo2与“标量属性默认参数”共存会导致Foo(x, xo1, 0)这类调用出现歧义风险0既可能被当作xo2的标量常量也可能被当作属性。做法将新增输入的参数类型改为TensorHolder且无默认值用户若需要把标量/向量作为xo2必须先CreateScalar/CreateVector/CreateConst。// v1 版本保留用于后向兼容 FORCE_INLINE EsTensorLike Foo(const EsTensorLike x, const EsTensorLike xo1, int64_t a0); // v2 版本兜底新增输入 xo2 变为必选 TensorHolder 消歧 FORCE_INLINE EsTensorLike Foo(const EsTensorLike x, const EsTensorLike xo1, const TensorHolder xo2, int64_t a0, int64_t b0);4.2.3 方案B版本命名空间备选方案B把不同版本的 API 放到不同命名空间例如ge::es与ge::es::v2从而避免同名重载集合膨胀与二义性// v1 版本 namespace ge::es { FORCE_INLINE EsTensorLike Foo(const EsTensorLike x, const EsTensorLike xo1, const char *mode xx); } // v2 版本新命名空间 namespace ge::es::v2 { FORCE_INLINE EsTensorLike Foo(const EsTensorLike x, const EsTensorLike xo1, const EsTensorLike xo2 nullptr, const char *mode xx); }4.2.4 方案对比与取舍维度方案A同命名空间多版本重载方案B版本命名空间重载二义性风险低A1 默认 A2 必要时 TensorHolder 兜底消歧低命名空间隔离用户调用习惯稳定性高Foo(x, xo1[, attr...])始终代表“未连边”中调用方需显式选择命名空间前向兼容引导可用 deprecateddelete 强制用户“未连边走 v1”主要依赖文档规范Runnable DumpC低成本dump 只需判断“是否连边”选择 v1/v2 形态高成本dump 需要确定命名空间/版本并处理依赖动态库 ABI仍无法保证命名空间不影响底层 C ABI仍无法保证综合取舍本设计选择方案A。4.2.5 重载生成策略方案AA1/A2本小节给出可落地的生成规则用于gen_esb目标是在“一年窗口”的版本链上生成一组最小且无二义性的 C 接口集合。版本链说明版本链按版本元信息中的发布时间例如metadata.json.release_date排序得到V0 ... Vn一年窗口的筛选策略由gen_esb根据当前版本号与发布日期计算得到。术语定义抽取存在性对某个版本的算子原型判断某个输入/属性在 IR 原型中是否“存在”以 IR 原型是否声明该 name 为准可选/动态输入同样视为存在。新增点在相邻两版本对比时满足“新版本存在、旧版本不存在”的输入/属性。首次出现在版本序列从旧到新中某个输入/属性第一次从“不存在”变为“存在”的版本。备注新增点 vs 首次出现若 IR 原型在一年窗口内只发生“单调兼容变化”仅新增可选输入/带默认属性则相邻版本的“新增点”等价于版本链上的“首次出现”。但我们还需覆盖未来可能出现的兼容变化例如必选→可选这类变化不一定体现为 exists 从 0→1因此实现上仍保留 “新增点exists 变化”与“首次触发生成可能来自 exists/required/default 的变化”的区分。生成方法每个算子独立执行用于指导gen_esb实现输入一年窗口版本链V0 V1 ... Vn的 IR 原型inputs/attrs 及默认值。输出一组最小、无二义性、带防呆的 C 接口签名集合含 Runnable Dump 生成约束。阶段0抽取存在性矩阵见第 8 章附录《存在性矩阵与二义性检测示例》对每个版本Vi抽取exists_input[Vi][name]、exists_attr[Vi][name]。对相邻版本对(Vi-1, Vi)计算新增点new_inputs(Vi)、new_attrs(Vi)。阶段1候选签名生成先生成、后判定阶段2 失败则回到这里收敛不在这里穷举 case仅新增可选属性有默认值不新增重载把默认属性追加到最新接口参数末尾即可。新增可选输入先判定是否可安全合并为单签名A0否则进入“保留 v1 的收敛链尝试0→A1→A2”安全合并A0判定同时满足时可合并且不再保留 v1新增输入追加到末尾且用 nullptr能准确表达“未连边”语义不存在“标量字面量可能被误当作新增输入”的风险典型触发同时存在int64_t a0这类标量默认属性位不需要用“保留 v1 防呆”来约束 dump/用户写法例如不需要禁止Foo(..., nullptr)占位通过后生成单签名Foo(..., xo2nullptr, ...)不再生成 v1该场景不涉及重载二义性判定阶段2可跳过。不满足安全合并进入收敛链保留 v1尝试0语义优先生成 v1同时让 v2 的新增输入xo2保持可选xo2 nullptr若阶段2判定 v1/v2 存在潜在二义性则放弃并升级到 A1。A1收敛1保留 v1 表达“未连边”v2 新增输入xo2采用EsTensorLike且必选无默认值表达“已连边”并生成nullptr防呆见 4.3。A2收敛2/兜底若 A1 仍可能与标量字面量发生重载歧义则把 v2 的xo2切换为TensorHolder必选并要求 dump/用户显式CreateScalar/CreateVector/CreateConst。阶段2二义性判定生成侧必须执行失败则回到阶段1收敛Gate1候选对筛选对任意两条候选接口f/g计算可调用实参个数区间Rf[req_f, tot_f]、Rg[req_g, tot_g]req必选参数数量。若Rf ∩ Rg为空则不可能二义性否则进入 Gate2。Gate2典型实参集合检查保守判定在k ∈ (Rf ∩ Rg)的调用里检查是否存在某个典型实参能在同一位置同时匹配f/g的形参类型。建议至少覆盖两类可隐式转换为EsTensorLikenullptr、0、0.0对应EsTensorLike(std::nullptr_t/int64_t/float)。不可隐式转换为EsTensorLikexxconst char*用于验证“属性字符串”不会与输入位竞争。 若存在可同时匹配的典型实参则判定为潜在二义性禁止该组合回到阶段1升级策略尝试0→A1→A2或调整签名集合。说明该检查是工程上的保守近似不追求完全等价于 C 的重载决议规则。阶段3防呆与导出约束落地若最终集合包含“v2 新增输入必选”的重载A1/A2则自动生成std::nullptr_t的 deprecateddelete 防呆重载见 4.3并要求 Runnable Dump 不输出(..., nullptr, ...)占位形态。A0单签名且新增输入默认 nullptr不生成上述nullptr防呆Runnable Dump 可省略该输入以利用默认值。最小示例两种场景对比A) 仅新增可选输入且无标量字面量歧义默认方案v2 令 xo2 必选 防呆v1Foo(x, xo1[, mode])xo2 未连边v2新增可选输入xo2生成保留 v1 生成 v2xo2 必选 生成 nullptr 防呆Foo(x, xo1[, mode])Foo(x, xo1, xo2[, mode])Foo(x, xo1, nullptr[, mode]) deletedeprecated 提示用 v1B) 新增可选输入 存在标量属性默认参数默认方案会与字面量冲突触发兜底方案Av1Foo(x, xo1, a0)v2新增可选输入xo2生成保留 v1 新增 v2 重载TensorHolder消歧Foo(x, xo1, a0)Foo(x, xo1, TensorHolder xo2, a0, b0)4.3 防呆机制方案A防呆的目标主要用于阻止“破坏向前兼容性”的占位写法并给出明确迁移提示。说明只要新增输入在 v2 中是必选参数无论类型是EsTensorLike还是TensorHolder就建议生成std::nullptr_t的 deprecateddelete 防呆重载用于拦截用户误把nullptr当成“未连边”的写法并提示回落到 v1。 补充A0 场景合并为单签名且新增输入默认 nullptr不需要生成nullptr防呆重载。4.3.1nullptr占位防呆对“v2 新增输入必选”的重载生效触发条件当某条 v2 重载因为“新增可选输入”而引入了新的输入形参xo2且该参数在 v2 中为必选参数无默认值则额外生成一条同名防呆重载。签名形态将该新增输入位置替换为std::nullptr_t并 delete必要时叠加[[deprecated(...)]]给出更友好的诊断。示例// 正常重载 FORCE_INLINE EsTensorLike Foo(const EsTensorLike x, const EsTensorLike xo1, const TensorHolder xo2, int64_t a0, int64_t b0); // 防呆重载禁止 nullptr 作为占位避免误以为 nullptr 表示“未连边” [[deprecated(此算子新增输入 xo2 采用 TensorHolder 兜底消歧若不使用 xo2 请调用旧重载 Foo(x, xo1[, a])若 xo2 为标量/向量常量请使用 EsGraphBuilder::CreateScalar/CreateVector/CreateConst 显式构造。)]] FORCE_INLINE EsTensorLike Foo(const EsTensorLike x, const EsTensorLike xo1, std::nullptr_t /*xo2*/, int64_t a0, int64_t b0) delete;4.3.2 Runnable Dump 规则与两种方案对应A0单签名新增输入默认 nullptrxo2 未连边dump 可直接省略 xo2利用默认值xo2 已连边dump 直接传入 xo2默认方案EsTensorLike作为新增输入类型v2 中 xo2 必选xo2 未连边dump 输出 v1 形态Foo(x, xo1[, attr...])不输出Foo(x, xo1, nullptr, ...)xo2 已连边dump 输出 v2 形态Foo(x, xo1, xo2[, attr...])兜底方案ATensorHolder xo2xo2 若为标量/向量常量dump 必须先输出builder.CreateScalar/CreateVector/CreateConst再把产物作为xo2传入禁止 dump 用Foo(x, xo1, 0)这类标量字面量形态表达xo2该形态固定用于属性表达如a05. ES 集成要点最小历史原型库的目录/格式/归档流程属于协议内容见协议文档。ES 场景下的主要集成点是generate_es_package对gen_esb的封装参数传递与构建流程以及第 4 章定义的“重载生成与二义性处理规则”。6. 特殊情况处理6.1 首次启用历史原型库场景在某个版本如 8.0.RC1首次启用历史原型库之前的版本未归档处理策略将当前版本作为基线版本仅生成当前版本的 API不生成重载在后续版本中以基线版本为起点生成重载具体来说2026/3/30版本规划为首次启用历史原型库的版本但是2025/12/30是es构图的首次启动版本因此330商发之前需要先生成2025/12/30历史原型数据然后生成重载版本的c接口同时归档330的历史原型数据6.2 算子在历史版本中不存在场景当前版本新增的算子在历史版本中不存在处理策略仅生成当前版本的 API不生成重载因为没有历史版本C 接口与 C 接口一一对应7. 总结历史原型库是 ES 构图系统中支持 C API 兼容性的关键基础设施核心作用存储历史版本的 IR 定义供gen_esb生成 C 重载接口关键原则C 接口单一版本包含所有参数C 接口多个重载版本往前一年内的所有版本都要生成Python 接口单一版本使用关键字参数所有接口最终都调用同一个 C 实现数据格式采用 JSON 格式作为 ops run 包的一部分打包发布重载生成基于历史版本差异为可选输入的演进生成重载二义性处理检测并规避重载二义性保证生成的代码可编译维护流程商发前使用gen_esb工具生成数据打包到 ops run 包中兼容性默认算子原型的变化都是兼容的不进行兼容性检查兼容性保证由 IR 语义运行时兼容处理机制负责通过历史原型库ES 构图系统能够在保证易用性的同时提供可靠的 API/ABI 兼容性保障。8. 附录存在性矩阵与二义性检测示例A.1 存在性矩阵输入/属性一年窗口内版本为V1 V2 V3该算子在各版本出现过的输入名并集为{x, xo1, xo2}属性名并集为{a, b}。输入存在性矩阵1表示该版本 IR 中存在该输入定义versionxxo1xo2V1110V2111V3111属性存在性矩阵versionabV110V210V311由此可得新增点new_inputs(V2){xo2}new_attrs(V3){b}首次出现xo2首次出现在V2b首次出现在V3A.2 Gate1/Gate2 的“区间交集”与二义性示例两条候选重载f1:Foo(x, xo1, int64_t a0)可调用范围R1[2,3]f2:Foo(x, xo1, TensorHolder xo2, int64_t a0)可调用范围R2[3,4]Gate1交集R1 ∩ R2 [2,3] ∩ [3,4] [3,3]说明只有当调用传入 3 个实参时两者才可能同时成为候选。Gate2实参可同时匹配调用Foo(x, xo1, 0)对 f10可转int64_t可匹配对 f20不能转TensorHolder不可匹配因此最终不二义性且固定解释为a0若把 f2 的xo2类型改为EsTensorLike且存在EsTensorLike(int64_t)隐式构造则Foo(x, xo1, 0)会同时可匹配 f1 与 f2从而发生二义性这就是“冲突时方案ATensorHolder 兜底”的触发原因。A.3 新增可选输入EsTensorLike 必选 nullptr 防呆当 v1/v2 需要同时存在时v2 不能把新增输入写成xo2nullptr否则Foo(x, xo1)会二义性。推荐形态是v1表达xo2未连边v2xo2必选表达xo2已连边防呆std::nullptr_t重载提示用户回落到 v1示例v1:Foo(x, xo1, const char* modexx)v2:Foo(x, xo1, EsTensorLike xo2, const char* modexx)防呆: [deprecated(描述信息)]Foo(x, xo1, nullptr, const char* modexx) delete【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考