LADDER: Enabling Efficient Low-Precision Deep Learning Computing through Hardware-aware Tensor Transformation

文章标题:LADDER: 通过硬件感知的张量变换实现高效的低精度深度学习计算
作者/机构:Lei Wang†⋄∗, Lingxiao Ma⋄, Shijie Cao⋄, Quanlu Zhang⋄, Jilong Xue⋄, Yining Shi‡⋄∗, Ningxin Zheng⋄, Ziming Miao⋄, Fan Yang⋄, Ting Cao⋄, Yuqing Yang⋄, Mao Yang⋄ (中国科学院大学†, 北京大学‡, 微软研究院⋄)


A1 主要贡献

核心问题
随着深度学习模型对性能需求的不断增长,利用低精度计算已成为一种趋势。然而,现有的硬件和软件对新兴的自定义低精度数据类型的支持不足且效率低下。硬件(如GPU)只能集成有限的几种标准数据类型计算单元,难以跟上算法需求的快速演进。软件方面,优化细粒度的低比特数据访问以适应粗粒度的内存系统(如NVIDIA GPU的4字节共享内存库宽)非常复杂,容易导致带宽浪费,需要大量非平凡的优化工作。这种软硬件支持的不足和低效严重阻碍了模型和加速器的创新。

研究目标
本文旨在弥合不断演进的自定义数据类型与当前硬件支持的固定精度格式之间的鸿沟。目标是在不修改现有硬件的条件下,通过一种新颖的编译器设计,系统性地支持通用低比特精度自定义数据类型,并在现代加速器上实现高效的深度学习计算。

创新点
1. 分离存储与计算的通用方法:本文观察到,硬件的内存系统可以存储任意数据类型,而计算单元支持的标准数据类型可以无损地表示大多数自定义数据类型。基于此,LADDER提出了一种通用方法:以自定义低精度数据类型存储和传输张量,通过类型转换以硬件支持的标准数据类型进行计算。这能够有效利用低比特数据类型在节省内存流量和占用方面的优势。
2. tType通用类型系统和扩展张量表达式:为了方便地实现快速演进的自定义数据类型(如块状数据类型MXFP),LADDER引入了一个名为tType的通用类型系统。tType是一个分块(tile-wise)的数据类型,通过显式指定类型宽度、元素形状和类型转换函数来定义所有常见的自定义类型。基于此,LADDER扩展了现有的张量表达式,使其能够原生支持为每个张量标注tType,从而系统地将带有自定义数据类型的DNN计算转换为标准的计算流水线。
3. 张量调度原语:为了优化涉及自定义数据存储、访问和类型转换的计算流水线,LADDER引入了一套新的张量调度原语,包括slice(切片)、map(映射)、pad(填充)和convert(转换)。这些原语能够将默认的计算流水线转换为经过优化的、性能更佳的等价形式,以适应不同的内存布局和硬件特性。
4. 硬件感知的优化策略:为了在复杂的变换空间中找到最优解,LADDER采用了一种分层的、硬件感知的优化策略。该策略将DNN计算建模为分块级别的数据流图,然后使用一种粒度感知的调度策略进行优化:较低层内存提供首选的数据访问粒度作为提示(hint),上层根据此提示决定最优的计算粒度,并通过张量变换进行对齐。
5. 系统实现与开源:LADDER基于TVM、Roller和Welder实现,并且已经开源。其核心DNN操作编译功能已作为BitBLAS库发布,可集成到现有的DNN和LLM框架中,为深度学习生态系统提供高效的低精度计算能力。


A3 背景知识/关键Observation/设计原则

2.1 深度学习中的精度要求

2.2 GPU中不充分的精度支持

2.3 低精度计算的低效性

2.4 我们的洞见


A2 方法细节

3. LADDER 设计

系统概述:针对第2节中的观察,LADDER被设计为一个将数据类型视为一等公民的DNN编译器,它引入了张量变换来支持自定义数据类型上的高效DNN计算。图3展示了其系统架构。

图3:LADDER的系统概览
图3:LADDER的系统概览

核心抽象 tTile:LADDER的核心是TypedTile(tTile)抽象,它在基于分块(tile)的张量抽象上增加了数据类型信息(即tType,见§3.1)。算法设计者可以使用常用数据类型(如FP16)或定义自定义数据类型(如MXFP8, NF4)作为tType,并在此数据类型上定义DNN计算。随后,LADDER将输入的DNN模型转换为一个基于tTile的数据流图(tTile-graph),其中算子被定义为基于tTile的计算任务(tTile-operator)(见§3.1)。

硬件抽象 tTile-device:此外,LADDER将硬件加速器抽象为一个多层层次结构,每一层的需求都由一个tTiletTile-device,见§3.1)来表示。tTile-device显式地描述了每一层的要求,例如支持的数据类型、事务大小等。通过将tTile-graph中的tTiletTile-device对齐,以tTile-graph表示的DNN计算便可以在硬件加速器上执行。

调度机制与策略:给定初始的tTile-graph和硬件规格,LADDER会将DNN模型编译成在加速器上的高效执行计划。为了在tTile-device上调度tTile-graph并满足硬件层次结构的要求,LADDER将其调度机制与策略分离。在机制方面,LADDER提出了四种tTile变换原语:slicemappadconvert,它们能够将一个tTile变换为另一个等价的tTile(见§3.2)。

优化空间与策略:调度器会将初始的tTile-graph调度为一个能够精细控制tTile配置、变换以及在硬件层次结构上布局的tTile-graphtTile抽象扩大了DNN计算的调度空间,并在内存占用效率和延迟效率之间开启了新的权衡。在策略方面,LADDER基于观察采用启发式方法,并提供了一种硬件感知的、分层的策略来优化延迟效率(见§3.3)。

代码生成:最后,编译好的、由tTile-graph表示的计划将被生成为给定硬件加速器的可执行代码。

3.1 tTile 抽象

图4:tType、tTile和tTile-operator的定义
图4:tType、tTile和tTile-operator的定义
图5:FP16张量A和NF4张量B的矩阵乘法:(a) tType注解的张量表达式,(b) NVIDIA A100的tTile-device,(c) 计算流水线的伪代码,(d) 使用tTile变换原语的Transform-Load,(e) 张量B的变换
图5:FP16张量A和NF4张量B的矩阵乘法:(a) tType注解的张量表达式,(b) NVIDIA A100的tTile-device,(c) 计算流水线的伪代码,(d) 使用tTile变换原语的Transform-Load,(e) 张量B的变换

3.2 tTile 变换

图6:tTile变换原语
图6:tTile变换原语

3.3 硬件感知的 tTile-Graph 调度

Data: g: tTile-graph; D: tTile-device   
Result: gret : scheduled tTile-graph   
1 Function GetDeviceHint(g, D):   
2 D = SelectDeviceConfig(g, D);   
3 HintShape = None, HintGranularity = None;   
4 for layer ∈ D.layers do   
5 HintGranularity = LCM(HintGranularity, layer.tTile.type);   
6 for layer ∈ D.layers do   
7 layer.tTile = convert(layer.tTile, HintGranularity);   
8 HintShape = LCM(HintShape, layer.tTile.shape);   
9 for layer ∈ D.layers do   
10 layer.tTile.shape = HintShape;   
11 return D;   
12 Function ScheduleTransform(op,D,lid):   
13 tTileh = op.tTile[lid -1];   
14 tTilel = op.tTile[lid ];   
15 ScheduleSlice(tTilel , tTileh);   
16 if LCM(tTilel .shape, tTileh.shape) != tTilel .shape then   
17 SchedulePad(tTilel , tTileh, D);   
18 if tTilel .type != tTileh.type then   
19 ScheduleConvert(tTilel , tTileh, D);   
20 if nBits(tTileh.shape[-1]) != nBits(D.layers[lid ].shape[-1]) then   
21 ScheduleMap(tTilel , tTileh, D);   
22 return op.transform[lid -1];   
23 Function ScheduleConnectedGraph(g, D):   
24 D = GetDeviceHint(g, D);   
25 for lid in length(D.layers) do   
26 for op ∈ g[lid ] do   
27 op.tTile[lid ] = ScheduleTiling(op,D,lid );   
28 if lid > 0 then   
29 op.transform[lid ] = ScheduleTransform(op,D,lid );   
30 g = ProfileAndSelect(g);   
31 return g;   
32 Function Schedule(g,D):   
33 g = ExtractConnectedGraph(g, D);   
34 for gconn ∈ g do   
35 gconn = ScheduleConnectedGraph(gconn, D);   
36 return g;

最初,该策略将图调度为子图(第33行)。每个子图表示一个计算流水线,它将`tTile`从最低内存层加载到核心,然后将结果存储回最低内存层。一个子图可以是一个`tTile-operator`或一组可以融合的`tTile-operator`。`ExtractConnectedGraph`可以利用现有的DNN编译器工作【索引13,Tvm: end-to-end optimization stack for deep learning,2018,arXiv;索引43,Welder: Scheduling deep learning memory access via tile-graph,2023,OSDI】。对于一个子图,它首先从硬件中推断出提示。具体来说,它首先选择合适的硬件配置(例如,计算核心)(第2行),偏好硬件支持的比特位最接近的`tType`。因为位数更多的数值类型通常需要更多的晶体管来实现硬件指令,并且通常性能较低。例如,在NVIDIA A100 GPU中,NF4类型可以转换为FP16或FP32进行处理,LADDER将选择FP16核心(312 TFlops)而不是FP32(19.5 TFlops)。然后,它通过比特对齐为每个硬件层找到对齐的粒度和形状,并配置提示(第1-11行)。以NVIDIA A100为例(图5(b)),`HintGranularity`是`ldmatrix`要求的16B,`HintShape`是`[4,8]`,其中内维是128B,与全局内存的32B事务和共享内存的128B事务对齐。然后,策略从顶层(即核心)到底层(即DRAM)逐层调度此子图(第25-29行)。在每一层,策略首先通过`ScheduleTiling`调度`tTile-operator`的分块并带有提示(第27行),然后调度`tTile`变换(第29行)。如果`ScheduleTiling`(第27行)调度的算子分块是`[4,8]`和16B的倍数,后续的`ScheduleTransform`可以使此调度与`tTile-device`对齐。此外,`ScheduleTiling`可以利用现有的张量编译器【索引13,Tvm: end-to-end optimization stack for deep learning,2018,arXiv;索引52,Ansor: Generating high-performance tensor programs for deep learning,2020,OSDI;索引57,Roller: Fast and efficient tensor compilation for deep learning,2022,OSDI】。在`ScheduleTransform`中,策略将检查形状和类型与`tTile-device`的对齐情况,并调度相应的变换来对齐`tTile`(第12-22行)。调度后可能会有一些候选方案,将对其进行性能评测并返回最佳方案(第30行)。

4. 实现

代码与依赖:LADDER的实现包含约5000行代码,包括Python和C++,基于开源DNN编译器:TVM【索引13,Tvm: end-to-end optimization stack for deep learning,2018,arXiv】、Welder【索引43,Welder: Scheduling deep learning memory access via tile-graph,2023,OSDI】和Roller【索引57,Roller: Fast and efficient tensor compilation for deep learning,2022,OSDI】。LADDER修改了TVM以实现核函数调度和生成核函数代码,同时利用Roller来推断高效的tTile配置。Welder是目前最先进的能够全面优化DNN模型的DNN编译器,被用于端到端的图优化。

工作流程:LADDER的输入是一个PyTorch程序。对于PyTorch内置的数据类型,LADDER不需要对DNN模型程序做任何修改。此外,对于PyTorch不支持的新数据类型,LADDER通过自定义算子扩展了PyTorch,以表达用户定义数据类型上的张量表达式。给定PyTorch程序,LADDER将其导出为ONNX图。LADDER也扩展了ONNX以表示新数据类型上的计算,其中tType注解的张量表达式保存在ONNX图节点的属性中。有了导出的ONNX图和目标硬件加速器的基于tTile的规范文件,LADD-ER会自动将ONNX图转换为tTile-graph并执行调度。然后,LADDER为目标硬件加速器生成设备代码。

硬件支持:我们为NVIDIA GPU和AMD GPU实现了LADDER,因为它们是DNN最流行的加速器。在本节的其余部分,我们将详细描述在NVIDIA GPU上的LADDER实现,并简要描述在AMD GPU上的实现。此外,如果新的硬件指令(例如最新Hopper GPU中的FP8张量核心)和其他硬件加速器(例如Graphcore IPU)符合基于tTile的硬件抽象并提供在硬件层次结构上加载和存储数据的编程接口,LADDER也可以移植到它们上面。

4.1 LADDER on NVIDIA CUDA GPUs

4.1.1 tType 和 tTile
4.1.2 使用 PTX 指令优化代码生成

4.2 LADDER on AMD ROCm GPUs


A4 实验环境


A4 实验结果

5.2 NVIDIA GPU 评估

5.3 AMD GPU 评估 (图17)


A7 补充细节

6. 讨论


A5 结论

本文介绍了LADDER,这是首个旨在优化通用低精度计算在GPU等加速器上的深度学习编译器。LADDER通过暴露一个通用的类型系统(tType)和扩展的张量表达式,使用户能够轻松实现和表达深度学习中的新数据类型。它引入了一套新的张量调度原语,以促进计算流水线中张量存储、访问和类型转换等方面的优化。LADDER的分层、硬件感知的优化策略能够驾驭复杂的变换空间,展示了其系统性支持各种低比特精度自定义数据类型的能力。这在无需硬件修改的情况下,提升了现代加速器上DNN计算的性能。这项创新不仅赋能模型设计者探索数据类型优化,也为硬件供应商提供了一个灵活的解决方案来扩展对多样化精度格式的支持。