图引擎(GE)是 CANN 异构计算栈中的编译核心。它负责将上层 AI 框架定义的、逻辑层次的计算图,转化为底层硬件能够高效执行的、面向物理特性的执行序列(OM 文件)。GE 的优化工作集中于静态分析,目标是最大化硬件利用率,并以前所未有的精度管理显存资源。
CANN 组织链接 : https://atomgit.com/cann
GE 仓库链接 : https://atomgit.com/cann/ge
1. 图结构的统一化与静态分析管线
GE 的首要任务是将来自不同框架(如 ONNX/TF/PyTorch)的表示,统一到一个内部的中间表示(IR)中,并在此基础上进行深度静态分析。
1.1 IR 抽象层与依赖图构建
- 统一张量描述 :GE 定义了统一的张量描述符,明确了输入输出的维度、数据类型以及内存布局格式(如 NC1HWC0 或 Block Format)。
- 数据依赖图 (DFG) 拓扑分析:GE 精确构建 DFG,识别所有算子间的依赖关系。这是所有后续优化(融合、内存复用、并行化)的严格约束条件。
- 控制流的边界确定:GE 必须将计算图中的控制流(如条件分支、循环结构)与数据流区分开来。由于 NPU 擅长数据并行,控制流的拆解和后处理是优化过程中的关键点。
1.2 编译期常量折叠与图简化
在执行之前,GE 会尽可能地"编译掉"图中的静态计算部分。
- 常量传播与折叠:任何输入为常量的运算(如权重初始化、固定的激活函数参数)都会被 GE 在编译时直接计算出结果,并替换掉原始的计算节点,生成新的常量张量。
- 死代码与冗余操作消除:通过对 DFG 的遍历,GE 会识别出那些不影响最终输出的中间计算(Dead Code),并将其及其依赖的内存分配从最终的执行序列中移除,实现图的瘦身。
2. 内存空间的静态规划:高效率的显存复用策略
对于大型模型,GE 最大的工程贡献在于其对设备显存的静态、全局规划能力,这直接解决了运行时内存碎片化和峰值占用过高的问题。
2.1 张量生命周期分析(Liveness Analysis)
GE 采用经典编译器技术来优化内存生命周期。
- 生命周期区间确定 :GE 分析每个中间张量 T i T_i Ti 的完整生命周期,确定其首次写入时间点 和最后一次读取时间点。
- 内存复用调度(Aliasing) :这是 GE 内存优化的核心。GE 调度器会搜索时间轴上完全不重叠的两个中间张量 T A T_A TA 和 T B T_B TB,并将它们分配到相同的物理显存地址 。这种策略使得模型的总显存占用趋近于最大活性张量的占用,而不是所有中间张量之和。
2.2 布局优化与 TransData 算子的最小化
GE 负责将数据格式转化为 NPU 单元能最高效处理的形式。
- 格式传播机制:GE 尝试将 NPU 推荐的 Block Format(如 NC1HWC0)沿着数据流尽可能地传播。
- 冗余转换的消除 :只有当数据从一种格式流向一个不支持该格式的下一层算子 时,GE 才会插入
TransData算子。优化目标是使整个图中的TransData节点数量最少化,因为每个转换节点都代表了额外的计算开销和显存读写操作。
3. 算子融合的深度语义控制与多单元协同生成
算子融合是 GE 降低访存延迟的主要手段。GE 的融合是基于对算子语义的深入理解,而非简单的串联。
3.1 结构感知融合模板的应用
GE 内置了针对主流网络结构的融合规则集。
- Transformer 块融合 :针对 LLM,GE 能识别并融合注意力机制中的 Q K T → Softmax → Attention ⋅ V Q K^T \rightarrow \text{Softmax} \rightarrow \text{Attention} \cdot V QKT→Softmax→Attention⋅V 序列,以及 MLP 块中的
LayerNorm -> Linear -> Activation序列。 - 数值一致性验证:在融合过程中,GE 必须确保浮点数的精度保持一致。例如,在融合中需要确保累加器(Accumulator)的精度被提升(如从 FP16 提升到 FP32),以避免精度漂移。
3.2 融合核函数的内部执行模型
一个深度融合的核函数实际上是一个复杂的任务调度器。
- Cube 与 Vector 单元的协同:例如,一个融合的 MLP 算子,内部的线性层(GEMM)会由 Cube Unit 执行,而激活函数(如 GELU)的非线性运算则会由 Vector Unit 执行。GE 生成的指令流必须精确控制这两个单元的数据交换和时序,使其在片上完成所有步骤。
4. 执行序列的生成与异步并发控制
GE 的最终输出是固化在 OM 文件中的一系列硬件任务(Task)序列,Runtime 负责执行这些序列。
4.1 任务模型的定义与序列化
- Task 粒度的封装:GE 将优化后的图分解为粒度最小的执行单元(Task)。这些 Task 明确了要执行的算子、输入输出的物理地址(Static Address)以及所需的硬件资源。
- 异步指令流生成:GE 生成的任务序列是异步的,它包含了所有必要的同步点,用于指导 Runtime 调度器在 Device 侧管理并发流。
4.2 并行化与同步点的精确注入
- Stream 划分:GE 分析图的拓扑结构,将可并行计算的部分分配到不同的逻辑 Stream 中。
- Event 依赖的硬编码 :GE 在任务序列中显式注入 Event Wait/Set 操作。这些操作是数据依赖在硬件层面的精确表示,确保了不同 Stream 之间在数据就绪时才进行同步,最大限度地实现计算与通信的重叠。
5. 动态形状模型的编译策略与部署兼容性
GE 必须支持动态输入,同时保留静态编译带来的性能优势。
5.1 形状分档(Bucketing)机制
- 多档位预编译:对于支持动态形状的算子,GE 允许用户定义多个输入形状的"档位"。GE 会针对每一个档位独立编译一套优化后的 Tiling 策略和内存布局。
- 运行时快速匹配:部署时,Runtime 通过检查输入数据的实际维度,快速查找并加载最匹配的预编译 Task 序列,避免了运行时 JIT 编译的开销。
5.2 OM 文件:自包含的执行蓝图
GE 编译的 OM 文件是部署的唯一载体。
- 所有信息固化:OM 文件内嵌了优化的权重数据、静态内存分配地址、Task 序列描述以及动态形状档位的映射表。这使得 Runtime 能够快速加载和执行,无需依赖外部资源。
CANN 组织链接 : https://atomgit.com/cann
GE 仓库链接 : https://gitcode.com/cann/ge