TVM Learning (5)-Automatic Program Optimization
Transform a Primitive Tensor Function
之前已经讲过如何通过 tir.Schedule
对T.prim_func进行变换,仍以矩阵乘法为例
1 |
|
对其进行 split,
reorder
和 decompose_reduction
变换得到的TensorIR如下。
通过以上变换后,矩阵乘法的执行时间减少是由于:
- 循环拆分 (
sch.split
) :
- 将
j
循环拆分成了两个循环:j_0
和j_1
,其中j_1
的因子为4(内层循环)。 - 提高数据的局部性,因为较小的数据块会在更短的时间内被频繁访问,从而更好地利用缓存。
- 循环重排 (
sch.reorder
) :
- 将循环的顺序调整为
i, j_0, k, j_1
,意味着外层循环先遍历i
和j_0
,内层循环再遍历k
和j_1
。 - 优先考虑了数据在寄存器或缓存中的重用,尤其是在内层循环操作期间
A
矩阵中的元素。
- 分解归约 (
sch.decompose_reduction
) :
- 将对
k
的归约操作分解为初始化阶段和更新阶段,有助于将计算的两个阶段(即设置初始值和实际归约)分开。 - 提高并行化的机会,并且允许更好地利用向量化指令或其他硬件优化。
1 |
|
我们可以比较变换前后的计算用时
1 |
|
Transformation Trace
除了 sch.mod
字段,tir.Schedule
还提供了一个跟踪字段 sch.trace
,用于显示变换IRModule的步骤。
1 |
|
Stochastic Schedule Transformation
在之前的变换中,我们都是指定这些函数的输入参数。实际情况下,我们需要引入随机性,根据不同变换的输入参数得出的执行时间来选择性能最好的一个。
sample_perfect_tile
函数可以计算任务中的特定循环采样最优的切分策略。
输入参数:
loop
:要切分的循环。n
:要切分成几份。max_innermost_factor
:允许在最内层循环中采样的最大切分大小。此参数有助于控制平铺的粒度。decision
:一个可选的整数列表,表示预先确定的切分决策。如果提供,函数将使用此决策而不是采样。
下面函数 stochastic_schedule_mm
和 schedule_mm
唯一的区别是指定 j_factors
采用的是随机的策略。
1 |
|
可以发现,它是对原来的确定性变换的泛化版本,只是多了两个元素:
- 来自
sample_perfect_tile
的随机变量,以及我们在示例中没有涉及的其他采样操作。 - 根据随机变量采取行动的
schedule
操作。
j_factors
中的元素不是整数。相它们是符号变量,指的是正在采样的随机变量。我们可以将这些变量传递给转换 API,以指定factors. 调用 stochastic_schedule_mm
后的trace如下
1 |
|
Search Over Stochastic Transformations
stochastic_schedule_mm
实际上会根据每个采样步骤的实际决定,创建一个程序的搜索空间。
我们需要一种搜索算法能找到性能最好的变换。下面的函数使用最直接的搜索算法–随机搜索。它尝试重复运行 stochastic_schedule_mm
,得到一个转换后的IR module,运行benchmark,然后将性能最好的IR module记录下来。
1 |
|
实际情况下会使用更高级的算法。还需要提供额外的工具,例如在远程设备上进行基准测试等。TVM 的 meta_schedule
API 提供了这些功能。
meta_schedule
是一个命名空间,用于支持在可能的变换空间中进行搜索。
- 跨多个进程的并行基准测试。
- 使用 cost model,避免每次都进行基准测试。
- 在 trace 上进行进化搜索,而不是每次都随机取样。
tune_tir
API 仍使用随机变换来指定好程序的搜索空间并在搜索空间内找到优化的方案。
1 |
|
不知道为何Windows上运行clang会出错
1 |
|