CANN/ge格式建模解析

发布时间:2026/7/4 7:21:30
CANN/ge格式建模解析 GE 中的 Format 建模与接口语义解析【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge1. 为什么格式Format会成为性能问题在深度学习模型中用户在构造计算图时关注的通常是计算语义本身张量的维度、算子的数学含义以及算子之间的依赖关系等等。在这一层面数据“长什么样”往往被认为是显而易见的并不需要额外关心。然而当模型进入实际执行阶段这种“理所当然”的假设往往不再成立。1.1 用户语义与实际执行之间的差距从用户视角看一个张量只是一组有序的多维数据而从执行视角看这些数据需要以某种具体的内存布局存放才能被硬件高效访问。格式Format在 GE 中表示的正是这种内存布局。在业界的图编译器中这一概念通常也被称为 Layout。例如NCHW 与 NHWC 在 GE 中即属于两种不同的格式。不同的计算算子对格式往往有着不同的偏好例如对于 Conv2D 算子亲和的图片输入格式是 NC1HWC0亲和的 filter 输入格式是 FZ对于 MatMul 算子亲和的权重输入格式是 NZ这些差异并非来自算法本身而是源于底层硬件架构的实现特点。1.2 数据重排的代价并不“免费”当算子存在亲和格式时GE 通常需要插入额外的数据重排操作将输入转换为该算子更适合计算的格式。然而这类数据重排在性能上并不廉价会引入额外的计算开销与内存带宽消耗在复杂模型中可能被多次触发更重要的是这些数据重排往往不会出现在用户显式构造的计算图中却会直接影响模型的实际执行效率。1.3 这是一个系统性问题一个直观的想法是既然某些算子对数据布局更敏感那就由算子各自处理好输入输出的格式即可。但在工程实践中这并不是最优解。以一个多层卷积网络为例如果算子“各自为政”Conv2D 会在内部将输入从 NCHW 转换为 NC1HWC0完成计算后再将输出转换回 NCHW。此时实际的执行过程将演化为对于单个 Conv2D 算子而言通过数据重排使用亲和格式可以获得更优的计算性能但从整个网络来看TransData 在每一层反复出现显著增加了整体执行开销。因此格式相关的问题本质上是一个整网级别的系统优化问题在保证计算语义正确的前提下需要为不同算子选择合适的数据布局并尽量减少不必要的数据转换。也正是在这样的背景下GE 引入了一套统一的格式建模与优化机制以系统性地处理用户语义与实际执行之间的差距。2. Origin 与 StorageGE 中的两套表示体系为了解决上一章中提到的数据布局与性能问题GE 在内部对张量的表示进行了明确区分引入了两套相互关联、但职责不同的表示体系Origin与Storage。这两套表示分别用于刻画用户的原始语义以及算子实际执行时的数据形态是 GE 进行格式建模与优化的基础。2.1 Origin用户语义的表达与传播Origin 用于描述用户在构造计算图时所表达的原始语义包括OriginFormat张量在语义层面的格式描述例如 NCHWOriginShape张量在语义层面的维度信息例如 [8, 3, 224, 224]Origin 的来源通常是前端框架或用户显式给出的模型定义其核心特点是直接反映用户意图不包含任何针对具体硬件或实现的假设不以性能为目标进行调整当 GE 接受到一张计算图时Origin 通常由图的输入以及部分关键算子的属性显式给出如 Conv2D 通过属性标注自己的输入、输出格式。GE 会尽可能将 Origin 在整个计算图中传播其目的并非用于性能优化而是为了在整个编译过程中始终保留对用户原始计算语义的完整理解。这种传播机制为后续的优化提供了一个明确的语义边界使得任何形式的格式调整或执行优化都必须建立在不破坏 Origin 语义的前提下进行。2.2 Storage实际计算与存储的表示与 Origin 不同Storage 用于描述张量在实际执行阶段所采用的表示形式包括StorageFormat张量在内存中的具体布局方式例如 NCHWC0将 C 轴拆分为 C0、C1StorageShape张量在内存中的实际形态例如 Shape 为 [8, 3, 224, 224] 的 NCHW 格式转换为 NC1HWC0 后Shape 为 [8, 1, 224, 224, 16]Storage 并非由用户指定而是由 GE 在编译过程中根据多种因素综合推导得到例如算子的能力与限制不同算子对格式的亲和性全图范围内的数据流关系由于并非所有算子都支持所有格式Storage 的推导过程天然受到约束。例如某些格式可能仅对特定算子或算子的特定输入如权重有效。因此Storage 表示的是一种面向执行的工程选择其目标是在满足算子能力约束的前提下尽量少插入格式转换TransData以获得整体更优的执行效率。2.3 小结Origin 与 Storage 的分工配合关系在 GE 中Origin 与 Storage 的分工配合关系可以概括为Origin 正确定义用户语义不参与性能取舍Storage 面向执行性能优化但必须服从 Origin 语义这种分工使 GE 能够在保证用户语义正确的前提下对执行层面的 Format 进行灵活调整优化性能。3. Format 优化的基本原理在明确了 Origin 与 Storage 两套表示体系之后GE 所要解决的问题可以归结为两点如何在整张计算图中尽可能准确地理解用户对格式的原始语义在此基础上如何为算子选择合适的执行格式以获得整体更优的执行效率围绕这两个问题GE 的 Format 优化遵循一条清晰的原理路径先理解语义再优化执行。3.1 基于 Origin 的整图格式语义推导Format 优化的第一步并不直接涉及性能而是尽可能还原并理解整张计算图中的格式语义。GE 会以计算图的输入格式以及对格式敏感的算子例如 Conv2D在计算时必须明确输入格式作为锚点在计算图中向前、向后进行扩散尝试推导出各个算子输入与输出的 OriginFormat。这一过程的目标是尽可能扩大对用户原始格式语义的认知范围为后续的优化提供可靠的语义基础。3.2 格式语义推导的中断与不确定性在实际计算图中并非所有算子都能够保持格式语义的连续传播。当遇到改变张量维度语义的算子例如 Reshape时原有的格式语义往往不再成立。此时GE 会认为格式语义在该位置发生中断并将 Reshape 对端的格式标记为未知通常以 ND 表示。这种“中断”并不是失败而是对语义边界的主动标记避免在不具备充分信息的情况下对格式做出错误推断。3.3 基于算子能力的 StorageFormat 选择与扩散在完成整图范围内的 OriginFormat 推导之后GE 才进入执行层面的格式选择阶段。此时Format 优化的关注点从“语义是否正确”转向“如何获得更优的执行效率”。StorageFormat 的选择并不是对 OriginFormat 的直接映射而是需要综合考虑以下因素算子对执行格式的支持能力不同算子对特定格式的亲和性全图范围内的整体执行效率在这一阶段GE 始终遵循一个前提不破坏已经确认的 Origin 语义。由于不同算子对整体性能的影响并不均衡GE 会优先关注计算开销较大的算子例如卷积、矩阵乘法尽量为这些算子选择其更亲和的 StorageFormat。在关键算子确定执行格式后GE 再以其为中心结合上下游相邻算子的能力与约束对 StorageFormat 进行扩散与协调避免在关键路径上引入不必要的格式转换。以第 1.3 节中的计算图为例在完成 OriginFormat 推导后Format 优化会以计算开销较大的算子 Conv2D 为锚点选择其亲和的 StorageFormatNC1HWC0。由于其后的 ReLU 算子同样支持 NC1HWC0格式可以沿计算路径向后扩散并保持一致最终得到如下的执行格式布局3.4 Shape 与 Format 在推导过程中的分工在 GE 中Shape 与 Format 的推导承担着不同的角色。OriginShape 的推导遵循图编译器中常见的 InferShape 过程 它以计算图的输入 Shape即用户理解的 Shape为起点按照算子语义自前向后逐层推导直至图的输出。与之不同StorageShape 并不是独立推导的结果。 当 OriginShape、OriginFormat 与 StorageFormat 均已确定后StorageShape 可以自然地根据 StorageFormat 所对应的内存排布方式计算得到。这种分工将 Shape 的语义推导与执行层的 Tensor Format 解耦使格式优化能够在不干扰语义推导的前提下独立进行。4. 从 GE 对外 API 视角理解 Format/Shape 的接口与类型本章从 GE 对外 API 的视角说明 Format / Shape 在接口与类型层面如何表达并解释“概念 ↔ 类名”之间可能产生的理解偏差。4.1 接口层GetShape / GetOriginShape / GetStorageShape在对外接口中Shape / Format 通常会提供三类访问接口GetOriginShape()/GetOriginFormat()GetStorageShape()/GetStorageFormat()GetShape()/GetFormat()其中GetOrigin*()明确返回Origin视角的信息用于表达用户语义。GetStorage*()明确返回Storage视角的信息用于描述实际执行相关的信息。Get*()不带 Origin / Storage 前缀不显式指定视角因此返回的是“同时包含 Origin 与 Storage 两部分的信息”。换句话说GetShape()的含义并不是“只返回某一种 Shape”而是返回一个能够同时表达 Origin 与 Storage 的对象Format 相关接口同理。这种接口设计的价值在于需要语义信息时调用方可以显式使用GetOrigin*()需要执行信息时调用方可以显式使用GetStorage*()若调用方希望一次性获得完整描述则使用Get*()4.2 类型class层Shape / StorageShape / StorageFormat 的职责边界4.2.1 Shape纯数据结构不绑定语义Shape是一个单纯的数据结构类只负责表达“一个 shape”。 因此Shape可以用来承载 OriginShapeShape也可以用来承载 StorageShape是否属于 Origin 还是 Storage取决于其使用语境以及由哪个接口返回而不是Shape类型本身的属性。4.2.2 StorageShape / StorageFormat携带 Origin 与 Storage 的描述体从概念上看“StorageShape”“StorageFormat”很容易被理解为仅描述执行阶段的信息但在 class 体系中这两类对象实际承担的是更强的职责——它们都是同时携带 Origin 与 Storage 两部分信息的复合描述体。之所以需要将 Origin 与 Storage 绑定在同一个类型中根本原因在于 Storage 本身的复杂性。Storage 可能引入补维、对齐等规则从而使得仅有“执行阶段的 shape / format”不足以准确描述其与用户语义之间的对应关系。以 NC1HWC0 格式为例当看到一个形如[8, 1, 224, 224, 16]的 Tensor 时其 StorageFormat 为 NC1HWC0其 OriginFormat 既可能是 NCHW也可能是 NHWC其 OriginShape 中的 C 维度可能是 116 之间的任意值仅从执行阶段的 StorageShape 或 StorageFormat无法唯一还原其对应的语义含义。只有将 Origin 与 Storage 两部分信息同时绑定才能形成一个可解释、可稳定使用的完整描述。因此在对外 API 的语境中StorageShape与StorageFormat更接近于描述体Descriptor它们通过GetOrigin*()/GetStorage*()提供对不同视角的显式访问类型本身承担的是“绑定与封装”的职责而非对单一概念的直接映射4.3 关于类名歧义的说明与建议确实会有人将class StorageShape类型名与“StorageShape 概念”执行 shape混淆。这种混淆来自命名上的天然缺陷但从建模角度看该类实际承担的是“同时携带 Origin 与 Storage 的完整描述”。在实践中建议总是通过接口来判断获得了什么而不是通过 class。未来在不破坏现有接口兼容性的前提下也可以通过更明确的类型命名来降低理解成本例如class ShapeDescriptor {...}; using StorageShape ShapeDescriptor; // Deprecated 容易混淆不再推荐使用 class FormatDescriptor {...}; using StorageFormat FormatDescriptor; // Deprecated: 容易混淆不再推荐使用附录 A同一个 Tensor 在 Origin / Storage 视角下的对照示例下表以一个具体示例说明同一个 Tensor在不同视角下所表达的信息是如何不同的以及为何需要“描述体”类型来同时承载这些信息。视角接口示例内容说明OriginGetOriginFormat()NCHW用户语义上的格式OriginGetOriginShape()[8, 3, 224, 224]用户理解的 ShapeStorageGetStorageFormat()NC1HWC0实际执行使用的格式StorageGetStorageShape()[8, 1, 224, 224, 16]执行阶段的内存形态含补维复合GetFormat(){OriginNCHW, StorageNC1HWC0}同时携带语义与执行信息复合GetShape(){Origin[8,3,224,224], Storage[8,1,224,224,16]}完整的 Shape 描述体【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考