TVM图优化(以Op Fusion为例)

首先给出一个TVM 相关的介绍,这个是Tianqi Chen演讲在OSDI18上用的PPT https://files.cnblogs.com/files/jourluohua/Tianqi-Chen-TVM-Stack-Overview.rar

对于图优化来说,位于整个软件编译栈比较高的层次:

首先给出计算图的定义

Computational graphs: a common way to represent programs in deep learning frameworks

对于图优化来说,有很多种图优化手段:Operator Fusion
Constant Parameter Path Pre-Computation
Static Memory Reuse Analysis
Data Layout Transformation
AlterOpLayout
SimplifyInference

这里仅以Operator Fusion做例子介绍

Operator fusion : combine multiple operators together into a single kernel without saving the intermediate results back into global memory

也就说是说算子融合省掉了中间数据的store过程

在TVM中,有三种融合规则:

其中,算子属于哪一类是算子本身的特性(这个地方不是特别懂,这个属性有非常多的值),但是能融合的规则只有这三种。

但是这种store是如何减少的,在IR上有明确的体现。

下边的例子,我会使用tvm.relay来进行介绍,relay是TVM中实现的一种高级IR,可以简单理解为另一种计算图表示。其在TVM所处的位置如下图所示

比如,我们假设我们要完成一个y = exp(x+1.0)的计算图

给出测试代码(来自于源码中的test_pass_fuse_ops.py,有改动):

import tvm
from tvm import relay

def test_fuse_simple():
    """Simple testcase."""
    def before():
        x = relay.var("x", shape=(10, 20))
        y = relay.add(x, relay.const(1, "float32"))
        z = relay.exp(y)
        return relay.Function([x], z)

    def expected():
        x = relay.var("p", shape=(10, 20))
        y = relay.add(x, relay.const(1, "float32"))
        z = relay.exp(y)
        f1 = relay.Function([x], z)
        x = relay.var("x", shape=(10, 20))
        y = relay.Call(f1, [x])
        return relay.Function([x], y)

    z = before()
    z = relay.ir_pass.infer_type(z)
    # print(z.astext())
    zz = relay.ir_pass.fuse_ops(z, opt_level=2)
    print(zz.astext())
    zz = relay.ir_pass.infer_type(zz)
    zz = relay.ir_pass.fuse_ops(zz)
    zz = relay.ir_pass.infer_type(zz)
    after = relay.ir_pass.infer_type(expected())
    # print(after.astext())
    assert relay.ir_pass.alpha_equal(zz, after)

在融合前,其IR(方便用户看的一种形式,不是真正的IR)

fn (%x: Tensor[(10, 20), float32])
    -> Tensor[(10, 20), float32] {
  %0 = fn(%p0: Tensor[(10, 20), float32],
          %p1: float32)
          -> Tensor[(10, 20), float32] {
    %1 = add(%p0, %p1)
    %1
  }
  %2 = %0(%x, 1f)
  %3 = fn(%p01: Tensor[(10, 20), float32])
          -> Tensor[(10, 20), float32] {
    %4 = exp(%p01)
    %4
  }
  %5 = %3(%2)
  %5
}

 融合后:

fn (%x: Tensor[(10, 20), float32])
    -> Tensor[(10, 20), float32] {
  %0 = fn(%p0: Tensor[(10, 20), float32])
          -> Tensor[(10, 20), float32] {
    %1 = add(%p0, 1f)
    %2 = exp(%1)
    %2
  }
  %3 = %0(%x)
  %3
}

 可以很明显的发现,省掉了一次数据store过程

原文地址:https://www.cnblogs.com/jourluohua/p/10774399.html