---
title: Graph Lowering实现
description: "Graph Lowering遍历图上的算子，依次调用算子的Lowering函数，如果算子没有Lowering函数，则会执行默认的Lowering函数。"
url: https://www.hiascend.com/document/detail/zh/canncommercial/latest/programug/graphdevg/autofuse_1_0014.html
sourcePath: /source/zh/canncommercial/900/programug/graphdevg/autofuse_1_0014.html
indexId: 69cfbfb963fab2c1fcd9a6651e13a134c5bc958975d430c48ef550d01ae47b6370
---
# Graph Lowering实现

Graph Lowering遍历图上的算子，依次调用算子的Lowering函数，如果算子没有Lowering函数，则会执行默认的Lowering函数。

算子Lowering函数原型示例如下，输入一个GE图上的算子节点，返回Lowering是否成功，并在内部完成节点的Scalar表达，将结果存储在属性上。

```
ge::graphStatus(const NodePtr &node);
```

Graph Lowering整体分为三步：

1. 依次调用每个节点的Lowering函数，获得其对应的Loop表达。
2. 在Loop表达上进行index和range的推导，从而将中间过程中的View类表达到输入数据的搬运方式上。
3. 结合Loop表达推导出的index和range信息，生成一套轴的AscGraph。

调用完节点Lowering函数后，并不会立即将节点转换为AscBackend融合节点类型，而是将每个节点的每个输出Tensor关联一个KernelBox对象：KernelBox用于表达一个算子输出Tensor的计算过程。其核心部分定义如下：

```
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，公共表达式消除） 是编译器优化、代码生成以及数学计算中常用的一种优化技术，其核心目标是识别并复用表达式中重复出现的子表达式，以减少冗余计算，提升效率。

图1 单输出多引用流程


#### 融合与终止融合策略

在执行Lowering时，如何决定是否进行融合以及终止融合，本节给出当前的融合范围控制策略：

1. KernelBox的类型为Reduction类型。
2. KernelBox中的Loop节点总数超过阈值，当前阈值64。
3. KernelBox中的Load节点数量超过阈值，当前阈值4。
4. KernelBox作为一个无法Lowering的节点的输入（在默认Lowering函数中触发）。
5. KernelBox所属的节点包含输入或输出控制边（跨节点的Fuse会丢失准确的控制信息）。
6. KernelBox所属的节点的stream label信息与使用KernelBox作为输入的节点的stream label不一致。（如果任意一方未标记，则认为一致）。
7. 遇到Exp算子会立即Realize。
8. 控核scope不同核的算子不会融合到同一个融合算子。
9. 没有使用到的输入会被提前Realize。例如Zeroslike。
10. 如果算子带有_super_kernel_scope属性，会直接跳过。
11. 带有_disable_autofuse_scope会跳过Lowering。
12. 实际属性和算子IR不一致。
13. 算子的dtype校验如果不支持会跳过。
14. 纯scalar图会跳过融合（以单个Ascbackend为限）。

对于不支持Lowering的算子，会调用默认的Lowering实现，默认的Lowering函数会完成两个动作：

1. 将所有输入Anchor对应的KernelBox进行Realize，终止融合。
2. 将自身节点输出Anchor对应的KernelBox设置为ExternKernel类型的KernelBox。

当前使用默认Lowering实现的场景有三类，处理方式完全一致：

- 节点无法进行Lowering表达。
- 节点可以使用Lowering表达，但是未实现。
- 节点可以使用Lowering表达，且已经实现，但是由于无符号化推导结果，导致表达结果无效。

为节点编写Lowering实现时，无需考虑是否有符号化结果，因为会在Loop函数中进行判断。无论具体情况如何，最终Lowering后的KernelBox都是一致的，均为ExternKernel类型的KernelBox。


#### 融合回滚策略

回滚是指在Lowering阶段，由于不感知具体的CanFuse/Schedule/Codegen能力，导致生成的AscBackend融合节点性能低于原始节点时，应该如何将该AscBackend节点回滚至原始节点去执行。当前的回滚策略包括：


如果AscBackend融合节点对应的原始GE IR节点数少于阈值（当前阈值为2），则回滚为原始节点执行。
