Graph Lowering实现
Graph Lowering遍历图上的算子,依次调用算子的Lowering函数,如果算子没有Lowering函数,则会执行默认的Lowering函数。
算子Lowering函数原型示例如下,输入一个GE图上的算子节点,返回Lowering是否成功,并在内部完成节点的Scalar表达,将结果存储在属性上。
1 | graphStatus(const NodePtr &node); |
Graph Lowering整体分为三步:
- 依次调用每个节点的Lowering函数,获得其对应的Loop表达。
- 在Loop表达上进行index和range的推导,从而将中间过程中的View类表达到输入数据的搬运方式上。
- 结合Loop表达推导出的index和range信息,生成一套轴的AscGraph。
调用完节点Lowering函数后,并不会立即将节点转换为AscBackend融合节点类型,而是将每个节点的每个输出Tensor关联一个KernelBox对象:KernelBox用于表达一个算子输出Tensor的计算类过程。其核心部分定义如下:
1 2 3 4 5 6 7 8 | class KernelBox { public: bool IsExternKernel() const; // 是否是Extern类型(即需要走原始的GE IR实现),取值为true,表示走原始GE IR实现 // 处理从该KernelBox中加载一个元素时的行为,对于Extern或者Realize后的KernelBox,等同于直接从其输出Anchor上Load数据。而对于能继续融合的KernelBox,则会返回其内部的计算过程 LoopVar Load() const; // 进行Realize,即需要为该KernelBox生成AscBackend节点,如果为true,则表示可融合 void Realize(bool persistent = true); }; |
单输出多引用
对于单输出多引用的算子,可能出现多个计算对其输出的加载方式不一致的情况。GraphLowering会基于重计算进行Lowering,即对于一个可以继续融合的节点,如果其输出被多个节点使用,例如下面例子中的第一个Abs,那么在使用该输出的每个节点对应的KernelBox中,均会包含第一个Abs的计算。通过重计算,能够处理同一份数据按照不同方式进行搬运的场景。
图中CSE(Common Subexpression Elimination,公共表达式消除) 是编译器优化、代码生成以及数学计算中常用的一种优化技术,其核心目标是识别并复用表达式中重复出现的子表达式,以减少冗余计算,提升效率。

融合与终止融合策略
在执行Lowering时,如何决定是否进行融合以及终止融合,本节给出当前的融合范围控制策略:
- KernelBox的类型为Reduction类型。
- KernelBox中的Loop节点总数超过阈值,当前阈值64。
- KernelBox中的Load节点数量超过阈值,当前阈值8。
- KernelBox作为一个无法Lowering的节点的输入(在默认Lowering函数中触发)。
- KernelBox归属的节点包含输入或输出控制边(跨节点的Fuse会丢失准确的控制信息)。
- KernelBox所属的节点的stream label信息与使用KernelBox作为输入的节点的stream label不一致。(如果任意一方未标记,则认为一致)。
- 遇到Exp算子会立即Realize。
- 控核scope不同核的算子不会融合到同一个融合算子。
- 没有使用到的输入会被提前Realize。例如Zeroslike。
- 如果算子带有_super_kernel_scope属性,会直接跳过。
- 带有_disable_autofuse_scope会跳过lowering。
- 实际属性和算子IR不一致。
- 算子的dtype校验如果不支持会跳过。
- 纯scalar图会跳过融合(以单个Ascbackend为限)。
对于不支持Lowering的算子,会调用默认的Lowering实现,默认的Lowering函数会完成两个动作:
- 将所有输入Anchor对应的KernelBox进行Realize,终止融合。
- 将自身节点输出Anchor对应的KernelBox设置为ExternKernel类型的KernelBox。
当前使用默认Lowering实现的场景有三类,处理方式完全一致:
- 节点无法进行Lowering表达。
- 节点可以使用Lowering表达,但是未实现。
- 节点可以使用Lowering表达,且已经实现,但是由于无符号化推导结果,导致表达结果无效。
为节点编写Lowering实现时,无需考虑是否有符号化结果,因为会在Loop函数中进行判断。无论具体情况如何,最终Lowering后的KernelBox都是一致的,均为ExternKernel类型的KernelBox。
融合回滚策略
回滚是指在Lowering阶段,由于不感知具体的CanFuse/Schedule/Codegen能力,导致生成的AscBackend融合节点性能低于原始节点。此时,应该如何将该AscBackend节点回滚至原始节点去执行。当前的回滚策略是:
如果AscBackend融合节点对应的原始GE IR节点数少于阈值(当前阈值为2),则回滚为原始节点执行。