Felix: Optimizing Tensor Programs with Gradient Descent

Felix:使用梯度下降优化张量程序

作者/机构:
Yifan Zhao, Hashim Sharif, Vikram Adve, Sasa Misailovic
University of Illinois Urbana-Champaign, USA


A1 主要贡献

核心问题:在各种硬件上为深度神经网络(DNNs)等张量程序获得高性能实现是一项挑战。现有的基于搜索的张量程序优化器虽然能自动找到高性能程序,但由于搜索空间巨大,其搜索过程效率低下,通常需要数小时甚至数天才能发现好的程序。

研究目标:本文旨在解决现有搜索方法效率低下的问题,提出一种新颖的、基于梯度下降的编译器优化框架,以更高效地找到高性能的程序调度(schedules)。

创新点与主要贡献:
本文提出了Felix,一个新颖的基于梯度的张量程序编译器优化框架。其核心思想是将离散的程序调度搜索问题转化为一个可微的优化问题,从而利用梯度下降方法进行高效搜索。

  1. 可微程序空间的构建:Felix通过对程序空间应用连续松弛(continuous relaxation)技术,并创建了程序延迟的可微估计器,从而构建了一个适合梯度下降搜索的可微张量程序空间。这与传统方法在离散搜索空间上对不可微目标函数进行搜索形成鲜明对比。

  2. 自动生成可微程序特征:Felix能够自动生成与调度变量(如分块因子、循环展开因子)相关的可微程序特征。具体做法是:

    • 创建包含调度变量的符号化调度 (symbolic schedules)
    • 将符号化调度应用于子图,生成符号化程序 (symbolic programs)
    • 分析符号化程序,提取出程序特征的数学表达式(公式)
    • 通过平滑核(smoothing kernels)将这些公式中不可微的操作符(如 min, max)转化为可微的近似函数。
    • 这种方法使得Felix能够应用于广泛的张量算子和编译器变换,因为预训练的DNN成本模型可以一次性训练,然后应用于各种张量程序。
  3. 高效的梯度下降搜索:Felix利用梯度下降来优化调度变量,快速发现高性能的调度方案。梯度下降搜索产生的是连续空间中的候选调度,Felix通过离散化(如四舍五入)并验证其合法性,最终在目标硬件上进行少量实证评估,选出最终的优化调度。

  4. 显著的性能和效率提升:实验结果表明,Felix在平均7分钟的搜索时间内,其优化后的网络性能超过了PyTorch、TensorFlow和TensorRT等现成的推理框架。与最先进的基于搜索的优化器TVM Ansor相比,Felix达到95%最佳性能的速度平均快3.4倍,达到99%最佳性能的速度平均快2.8倍。这表明Felix在时间受限的调优场景或资源受限的边缘设备上尤其有效。

  5. 通用性与实现:Felix在Apache TVM张量编译器及其Ansor优化器之上实现。其优化方法是通用的,可以被其他基于张量的编译器采用。


A3 问题陈述

基于搜索的张量编译器典型工作流程:图1展示了如Ansor、Halide自动调度器和Tiramisu自动调度器等典型基于搜索的张量程序编译器的工作流程。用户首先通过API或声明式语言定义计算(步骤1),编译器将其直接翻译成中间表示,生成一个性能通常较低的初始程序$p_0$。优化器的目标是找到一个程序变换序列$s$(称为调度),生成一个优化后的程序$T(p_0, s)$。
图1. 基于搜索的张量编译器的典型工作流程。
优化问题可以表示为:
$$\max _{s \in S\left(p_0\right)} \operatorname{Performance}\left(\mathcal{T}\left(p_0, s\right)\right)$$
其中,$S(p_0)$是适用于初始程序$p_0$的合法调度搜索空间,$T(p_0, s)$是通过应用调度$s$到$p_0$上生成的优化程序,性能是在目标机器上测量的执行时间。

搜索空间定义:为了获得比手动调优更好的性能,张量编译器需要应用多种优化,如循环分块、循环展开、向量化、并行化以及硬件特定的变换(如GPU上的CUDA块/线程划分)。每种变换都包含可调参数(通常是布尔或整数值),这导致了巨大的搜索空间。为解决这个问题,现有编译器通常使用调度模板或顺序构建并剪枝调度,以限制任意的变换序列。搜索空间依赖于输入程序$p_0$,并且包含离散参数,因为被调优的编译器参数是整数值(如分块大小、SIMD并行因子)。

成本预测模型和程序特征:由于调度的搜索空间是离散的,许多经典的离散空间搜索技术(如遗传算法和束搜索)被用于此优化问题。为了减少对大量候选程序进行实证评估的开销,现有编译器通常使用成本模型(如前馈网络、LSTM、决策树)来预测程序的性能。这些成本模型不直接以调度$s$为输入,而是以从程序$p := T(p_0, s)$中提取的$K$个程序特征向量为输入:
$\text{Feat}(p) = (\text{Feat}_1(p), \ldots, \text{Feat}_k(p), \ldots, \text{Feat}_K(p))$
这些程序特征,例如程序中浮点加/乘操作的数量和循环中缓冲区访问的重用距离,通常由编译器设计者手动选择并通过静态分析提取。

现有目标函数的不可微性:如图1所示,估计一个调度的性能主要有3个步骤(步骤3-5):生成程序$T(p_0, s)$,提取程序特征$Feat(T(p_0, s))$,并将其输入成本模型。使用这种性能估计器时,优化问题变为(与公式1对比):
$$\max_{s \in S(p_0)} \text{CostModel} (\textbf{Feat}(\mathcal{T}(p_0, s)))$$
这个目标函数是不可微的,因为传统上应用调度和提取程序特征的过程都是不可微的程序。为了解决这些问题,Felix通过生成符号化调度、符号化变换的程序以及程序特征的公式,使这个过程变得可微。


A2 方法细节

图2展示了Felix编译器的工作流程,其核心方法如下:
- 计算图分区 (§3.1):Felix首先将输入程序的计算图划分为多个子图,每个子图代表一个或多个张量算子,优化在子图内部进行,子图之间不进行交互优化。
- 符号化调度与程序生成 (§3.2):对于每个子图,Felix生成多个符号化调度$s_i^*$,每个调度包含一组调度变量$x_i$。这些符号化调度可以生成许多具体的调度。
- 可微性能估计器 (§3.3):对于每个符号化调度$s_i^*$,Felix创建一个可微的性能估计器$EstmPerf_{i}$。该估计器由两部分组成:
1. 通过分析符号化程序$p_i^*$(由$s_i^*$变换而来),提取出作为调度变量$x_i$的可微函数的一组特征公式$Feat_{p_i^*}(x_i)$。
2. 一个预训练的基于DNN的成本模型 $C$,它将程序特征值映射到预测的执行时间。
- 梯度下降优化 (§3.4):Felix使用梯度下降法最小化所有符号化调度的目标函数$O(x_i)$,以发现每个子图的高性能调度。
- 整合与生成 (§3.5):最后,Felix将优化后的子图组合起来,生成一个优化的完整张量程序。

图2. Felix 高层工作流。
图2. Felix 高层工作流。

3.1 计算图分区

3.2 符号化调度与符号化程序生成

图3. 在Dense-Add图上生成符号化调度的示例,展示了2个生成的符号化调度及其对应的程序。为了简洁,调度中的一些变换步骤和程序中的长表达式被省略。Felix引入到调度中的变量以粗体显示。
图3. 在Dense-Add图上生成符号化调度的示例,展示了2个生成的符号化调度及其对应的程序。为了简洁,调度中的一些变换步骤和程序中的长表达式被省略。Felix引入到调度中的变量以粗体显示。

3.3 特征公式提取与重写

3.4 使用梯度下降优化调度

3.5 完整图调优

3.6 Felix编程接口

4. 实现


A4 实验环境


A4 实验结果

6.1 Felix vs. 现成推理框架

图6. Felix与深度学习框架(PyTorch, TensorFlow)在三个硬件平台上的DNN推理性能。y轴是某一框架的性能相对于该网络所有框架中最佳性能的归一化值。
表1. Felix在不同DNN和硬件设备上超越性能最佳的手动库所需调优时间(秒)。

6.2 Felix vs. Ansor-TenSet 基于搜索的编译器

图7. Felix和Ansor-TenSet在调度搜索过程中,最佳性能(显示为网络推理延迟)随搜索时间的变化。
表2. Felix相对于Ansor的调优加速比,通过比较Felix和Ansor达到最佳Ansor性能的90%、95%和99%所需的时间来衡量。(a) 批量大小 = 1
图8. Felix和Ansor中候选调度群体的预测性能随搜索进度的变化。顶行显示调优会话开始时的早期迭代,底行显示接近收敛时的后期迭代(两个工具的线重叠)。

6.3 单个算子性能比较

图9. Felix、Ansor和手动优化库(PyTorch、TensorFlow)在RTX A5000上的单个算子性能。y轴是某一框架的性能相对于该算子所有框架中最佳性能的归一化值。
图9. Felix、Ansor和手动优化库(PyTorch、TensorFlow)在RTX A5000上的单个算子性能。y轴是某一框架的性能相对于该算子所有框架中最佳性能的归一化值。

6.4 不同批量大小下的性能

表2b. 批量大小 = 16
图10. Felix和Ansor-TenSet的调度搜索过程中最佳性能(网络推理延迟)随搜索时间的变化。网络在RTX A5000上针对输入批量大小为16进行优化。


A7 相关工作


A5 结论

本文介绍了Felix,一个新颖的、基于梯度的张量程序编译器优化框架。我们展示了如何创建程序转换调度的可微空间,并利用梯度下降来寻找高性能的调度。Felix优化算法的新颖之处在于对调度空间进行了审慎的连续松弛,并生成了一个适合梯度下降优化的可微成本函数。

评估结果表明,Felix中高效的基于梯度的搜索能够比PyTorch、TensorFlow和TensorRT提供的程序更快地找到更优的张量程序。此外,我们展示了Felix相对于最先进的进化搜索的优势:它生成高性能程序的速度明显快于TVM Ansor。我们的评估证明,Felix可以广泛应用于从服务器、桌面到资源受限的边缘设备的各种商用GPU。

我们相信Felix背后的核心思想——通过变量松弛和数值优化来优化计算图——是通用的,并且可以基于其他操作计算图的张量编译器(如Halide【28, Halide: A language and compiler for optimizing parallelism, locality, and recomputation in image processing pipelines, 2013, PLDI】、MLIR【20, MLIR: A compiler infrastructure for the end of moore’s law, 2020, CoRR】、TACO【18, The tensor algebra compiler, 2017, OOPSLA】、Tiramisu【4, Tiramisu: A polyhedral compiler for expressing fast and portable code, 2019, CGO】)来实现。我们视此方法为将基于梯度的优化推广到通用编程语言中具有更复杂成本特征和控制流的更广泛程序类别的第一步。


方法细节中的引用汇总