From bdf93f63bf1ae57a55fe650944baddb1df651072 Mon Sep 17 00:00:00 2001 From: Corleone Date: Sun, 20 Feb 2022 20:57:45 +0800 Subject: [PATCH] Update ch06 --- .../accelerator_architecture.md | 94 + .../accelerator_introduction.md | 18 + .../accelerator_programming.md | 162 + chapter_hardware_accelerator/index.md | 20 +- chapter_hardware_accelerator/summary.md | 10 + img/ch06/G2S.svg | 1233 + img/ch06/R2TC.svg | 2880 ++ img/ch06/S2R.svg | 1193 + img/ch06/SM.svg | 9571 +++++++ img/ch06/V100.svg | 22683 ++++++++++++++++ img/ch06/compute_unit.svg | 13867 ++++++++++ img/ch06/davinci_architecture.svg | 3715 +++ img/ch06/ptx.svg | 1265 + img/ch06/tensor_core.svg | 392 + 14 files changed, 57100 insertions(+), 3 deletions(-) create mode 100644 chapter_hardware_accelerator/accelerator_architecture.md create mode 100644 chapter_hardware_accelerator/accelerator_introduction.md create mode 100644 chapter_hardware_accelerator/accelerator_programming.md create mode 100644 chapter_hardware_accelerator/summary.md create mode 100644 img/ch06/G2S.svg create mode 100644 img/ch06/R2TC.svg create mode 100644 img/ch06/S2R.svg create mode 100644 img/ch06/SM.svg create mode 100644 img/ch06/V100.svg create mode 100644 img/ch06/compute_unit.svg create mode 100644 img/ch06/davinci_architecture.svg create mode 100644 img/ch06/ptx.svg create mode 100644 img/ch06/tensor_core.svg diff --git a/chapter_hardware_accelerator/accelerator_architecture.md b/chapter_hardware_accelerator/accelerator_architecture.md new file mode 100644 index 0000000..fad9dfe --- /dev/null +++ b/chapter_hardware_accelerator/accelerator_architecture.md @@ -0,0 +1,94 @@ +加速器基本组成原理 +------------------ + +上节主要介绍了加速器的意义以及设计思路,了解到加速器与通用处理器在设计上的区别,因此加速器的硬件结构与CPU的硬件结构有着根本的不同,通常都是由多种片上缓存以及多种运算单元组成。本章节主要通过GPU的Volta架构作为样例进行介绍。 + +### 硬件加速器的架构 + +现代GPU在十分有限的面积上实现了极强的计算能力和极高的储存器以及IO带宽。一块高端的GPU中,晶体管数量已经达到主流CPU的两倍,而且显存已经达到了16GB以上,工作频率也达到了1GHz。GPU的体系架构由两部分组成,分别是流处理阵列和存储器系统,两部分通过一个片上互联网络连接。流处理器阵列和存储器系统都可以单独扩展,规格可以根据产品的市场定位单独裁剪。如GV100的组成如 :numref:`gv100`所示: + +![Volta GV100 [@2017NVIDIA] []](../img/ch06/V100.svg) +:width:`800px` +:label:`gv100` + +- 6个GPU处理集群(GPU Processing Cluster,GPC), 每个GPC含有: + + - 7个纹理处理集群(Texture Processing Cluster, TPC) (每个TPC含有两个流多处理器(Streaming Multiprocessor, SM)) + + - 14个SM + +- 84个SM, 每个流多处理器含有: + + - 64个32位浮点运算单元 + + - 64个32位整数运算单元 + + - 32个64位浮点运算单元 + + - 8个张量核 + + - 4个纹理单元 + +- 8个512-bit内存控制器 + +一个完整的GV100 GPU含有84个SM,5376个32位浮点运算单元,5376个32位整型运算单元,2688个64位浮点运算单元,672个张量运算单元和336个纹理单元。一对内存控制器控制一个HBM2 DRAM堆栈。图 :numref:`gv100`中展示的为带有84个SM的GV100 GPU(不同的厂商可以使用不同的配置),Tesla V100则含有80个SM。 + +### 硬件加速器的存储单元 + +与传统的CPU模型相似,从一个计算机系统主内存DRAM中获取数据的速度相对于处理器的运算速度较慢。对于加速器而言,如果没有缓存进行快速存取,DRAM的带宽非常不足。如果无法快速的在DRAM上获取程序和数据,加速器将因空置而降低利用率。为了缓解DRAM的带宽问题,GPU提供了不同层次的若干区域供程序员存放数据,每块区域的内存都有自己的最大带宽以及延迟。 + +- **寄存器文件(Register File)**:片上最快的存储器,但与CPU不同,GPU的每个SM(流多处理器)有上万个寄存器。但当每个线程使用过多的寄存器时,SM中能够调度的线程块数量就会受到限制,可执行的线程总数量会因此受到限制,可执行的线程数量过少会造成硬件无法充分的利用,性能急剧下降。所以要根据算法的需求合理使用寄存器。 + +- **共享内存(Shared Memory)**:共享内存实际上是用户可控的一级缓存,每个SM(流多处理器)中有128KB的一级缓存, 开发者可根据应用程序需要配置最大96KB的一级缓存作为共享内存。共享内存的延迟极低,只有几十个时钟周期,几乎与寄存器相当。共享内存具有高达1.5TB/s的带宽,远远高于全局内存的峰值带宽900GB/s。所以说,共享内存的使用对于一个高性能计算工程师来说是一个必须要掌握的一个概念。 + +- **全局内存(Global Memory)**:全局内存之所以称为全局,是因为GPU与CPU都可以对它进行读写操作。全局内存对于GPU中的每个线程都是可见的,都可以直接对全局内存进行读写操作。CPU等其他设备可以通过PCI-E总线对其进行读写操作。全局内存也是GPU中容量最大的一块内存,可达16GB之多。同时也是延迟最大的内存, + +- **常量内存(Constant Memory)**:常量内存其实只是全局内存的一种虚拟地址形式,并没有真正的物理硬件内存块。常量内存有两个特性,一个高速缓存,另一个更重要的特性是它支持将某个单个值广播到线程束中的每个线程中。 + +- **纹理内存(Texture Memory)**:纹理内存是全局内存的一个特殊形态。当全局内存被绑定为纹理内存时,执行读写操作将通过专用的纹理缓存来加速。在早期的GPU上没有缓存,因此每个SM(流多处理器)上的纹理内存为设备提供了唯一真正缓存数据的方法。然而随着硬件的升级,一级缓存和二级缓存的出现,纹理缓存的这项优势已经荡然无存。纹理内存的另外一个特性,也是最有用的特性就是当访问存储单元时,允许GPU实现硬件相关的操作。比如说使用纹理内存,可以通过归一化的地址对数组进行访问,获取的数据可以通过硬件进行自动插值,从而达到快速处理数据的目的。此外对于二位数组和三维数组,支持硬件级的双线性插值与三线性插值。纹理内存另一个实用的特性是可以根据数组的索引自动处理边界条件,不需要对特殊边缘进行处理即可完成数组内元素操作,从而防止线程中分支的产生。 + +由于寄存器的高速读取特性,因此每次计算都离不开寄存器的参与。接着是一级缓存和共享内存,然后是常量内存、纹理内存、全局内存,最后则是主机端内存。根据不同存储器之间的存储速度的数量级的变化规律,选用适当类型的内存以及最大化地利用它们,从而发挥硬件的最大算力,减少计算时间。 + +### 硬件加速器的计算单元 + +为了支持不同的神经网络模型,加速器会提供以下几种计算单元,不同的网络层可以根据需要选择使用对应的计算单元。如图 :numref:`compute-unit`所示 + +- **标量计算单元**:与标准的精简指令运算集(Reduced Instruction Set Computer,RISC)相似,一次计算一个标量元素。 + +- **一维向量计算单元**:一次可以完成多个元素的计算,与传统的CPU和GPU架构中单指令多数据(SIMD)相似,已广泛应用于高性能计算(High Performance Computing,HPC)和信号处理中。 + +- **二维向量计算单元**:一次运算可以完成一个矩阵与向量的内积,或向量的外积。利用数据重复使用这一特性,降低数据通信成本与存储空间,更高效的提高矩阵乘法性能。 + +- **三维向量计算单元**:一次完成一个矩阵的乘法,专为神经网络应用设计的计算单元,更充分利用数据重复特性,隐藏数据通信带宽与数据计算的差距。 + +![多种计算单元[]](../img/ch06/compute_unit.svg) +:width:`800px` +:label:`compute-unit` + +GPU计算单元主要由标量计算单元和三维向量计算单元组成。如图 :numref:`SM`所示,对于每个SM,其中64个32位浮点运算单元、64个32位整数运算单元、32个64位浮点运算单元均为标量计算单元。而8个张量核则是专为神经网络应用设计的三维向量计算单元。 + +![Volta GV100 流多处理器(SM) [@2017NVIDIA] []](../img/ch06/SM.svg) +:width:`800px` +:label:`SM` + +张量核(Tensor Core)每个时钟周期完成一次4x4的矩阵乘累加计算,如图 :numref:`tensorcore`: + +``` + D = A * B + C +``` + +![Tensor Core 4x4矩阵乘累加计算[]](../img/ch06/tensor_core.svg) +:width:`800px` +:label:`tensorcore` + +其中A,B,C和D都是4x4的矩阵,矩阵乘累加的输入矩阵A和B是FP16的矩阵,累加矩阵C和D可以是FP16也可以是FP32。 V100的张量核是可编程的矩阵乘法和累加计算单元,可以提供多达125 Tensor TFLOPS(Tera Floating-point Operations Per Second)的训练和推理应用。相比于普通的FP32计算单元可以提速10倍以上。 + +### DSA芯片架构 + +为了满足飞速发展的深度神经网络对芯片算力的需求,业界也纷纷推出了特定领域架构DSA芯片设计。以华为公司昇腾系列AI处理器为例,本质上是一个片上系统(System on Chip,SoC),主要应用在图像、视频、语音、文字处理相关的场景。主要的架构组成部件包括特制的计算单元、大容量的存储单元和相应的控制单元。该芯片由以下几个部分构成:芯片系统控制CPU(Control CPU),AI计算引擎(包括AI Core和AI CPU),多层级的片上系统缓存(Cache)或缓冲区(Buffer),数字视觉预处理模块(Digital Vision Pre-Processing,DVPP)等。 + +![达芬奇架构设计](../img/ch06/davinci_architecture.svg) +:width:`800px` +:label:`davinci_architecture` + +昇腾AI芯片的计算核心主要由AI Core构成,负责执行标量、向量和张量相关的计算密集型算子。AI Core采用了达芬奇架构,基本结构如图 :numref:`davinci_architecture`所示,从控制上可以看成是一个相对简化的现代微处理器基本架构。它包括了三种基础计算单元:矩阵计算单元(Cube Unit)、向量计算单元(Vector Unit)和标量计算单元(Scalar Unit)。这三种计算单元分别对应了张量、向量和标量三种常见的计算模式,在实际的计算过程中各司其职,形成了三条独立的执行流水线,在系统软件的统一调度下互相配合达到优化计算效率的目的。 同GPU类似,在矩阵乘加速设计上,在AICore中也提供了矩阵计算单元作为昇腾AI芯片的核心计算模块,意图高效解决矩阵计算的瓶颈问题。矩阵计算单元提供强大的并行乘加计算能力,可以用一条指令完成两个16\*16矩阵的相乘运算,等同于在极短时间内进行了16\*16\*16=4096个乘加运算,并且可以实现FP16的运算精度。 diff --git a/chapter_hardware_accelerator/accelerator_introduction.md b/chapter_hardware_accelerator/accelerator_introduction.md new file mode 100644 index 0000000..6612a06 --- /dev/null +++ b/chapter_hardware_accelerator/accelerator_introduction.md @@ -0,0 +1,18 @@ +概述 +---- + +### 硬件加速器设计的意义 + +未来人工智能发展的三大核心要素是数据、算法和算力。目前,人工智能系统算力大都构建在CPU+GPU之上,主体多是GPU。随着神经网络的层数越多,模型体量越大,算法越复杂,CPU和GPU很难再满足新型网络对于算力的需求。例如,2015年谷歌的AlphaGo与樊麾对弈时,用了1202个CPU和176个GPU,每盘棋需要消耗上千美元的电费,而与之对应的是樊麾的功耗仅为20瓦。 + +虽然GPU在面向向量、矩阵以及张量的计算上,引入许多新颖的优化设计,但由于GPU需要支持的计算类型复杂,芯片规模大、能耗高,人们开始将更多的精力转移到深度学习硬件加速器的设计上来。和传统CPU和GPU芯片相比,新型深度学习加速器会有更高的性能,以及更低的能耗。未来随着人们真正进入智能时代,智能应用的普及会越来越广泛,到那时每台服务器、每台智能手机、每个智能摄像头,都需要使用加速器。 + +### 硬件加速器设计的思路 + +近些年来,计算机体系结构的研究热点之一就是深度学习硬件加速器的设计。在体系结构的研究中,能效和通用性是两个重要的衡量指标。能效关注单位能耗下基本计算的次数,通用性主要指芯片能够覆盖的任务种类。 + +以两类特殊的芯片为例:一种是我们较为熟悉的通用处理器(如CPU),该类芯片理论上可以完成各种计算任务,但是其能效较低大约只有0.1TOPS/W;另一种是专用集成电路(Application Specific Integrated Circuit, ASIC),其能效更高,但是支持的任务相对而言就比较单一。对于通用的处理器而言,为了提升能效,在芯片设计上有许多加速技术的引入,例如:超标量技术、单指令多数据(Single Instruction Multiple Data,SIMD)技术以及单指令多线程(Single Instruction Multiple Threads,SIMT)技术等。 + +对于不同的加速器设计方向,业界也有不同的硬件实现。针对架构的通用性,NVIDIA持续在其GPU芯片上发力,先后推出了Volta, Turing, Ampere架构,并推出用于加速矩阵计算的张量核(Tensor Core),以满足深度学习海量算力的需求。 + +对于偏定制化的硬件架构,面向深度学习计算任务,业界提出了特定领域架构(Domain Specific Architecture)。 Google公司推出了TPU芯片,专门用于加速深度学习计算任务,其使用脉动阵列(Systolic Array)来优化矩阵乘法和卷积运算,可以充分地利用数据局部性,降低对内存的访问次数。华为也推出了自研的昇腾AI处理器,旨在为用户提供更高能效的算力和易用的开发、部署体验,其中的CUBE运算单元,就用于加速矩阵乘法的计算。 diff --git a/chapter_hardware_accelerator/accelerator_programming.md b/chapter_hardware_accelerator/accelerator_programming.md new file mode 100644 index 0000000..73f2413 --- /dev/null +++ b/chapter_hardware_accelerator/accelerator_programming.md @@ -0,0 +1,162 @@ +加速器基本编程原理 +------------------ + +本章前两节主要介绍了这些硬件加速器设计的意义、思路以及基本组成原理。软硬件协同优化作为构建高效AI系统的一个重要指导思想,需要软件算法/软件栈和硬件架构在神经网络应用中互相影响、紧密耦合。为了最大限度地发挥加速器的优势,要求能够基于硬件系统架构设计出一套较为匹配的指令或编程(操纵)方法。因此,本节将以[1.2.3](#硬件加速器的计算单元){reference-type="ref" reference="硬件加速器的计算单元"}中介绍的Tensor Core为例,着重介绍加速器的可编程性,以及如何通过编程使能加速器,提升神经网络算子的计算效率。 + +### 硬件加速器的可编程性 + +[1.1.2](#硬件加速器设计的思路){reference-type="ref" reference="硬件加速器设计的思路"}节中列出的硬件加速器均具有一定的可编程性,程序员可以通过软件编程,有效的使能上述加速器进行计算加速。但出于计算效率和易用性等方面考虑,将编程使能方式分为不同等级,一般包括:算子库层级,编程原语层级,以及指令层级。为了更具象的解释上述层级的区别,仍以Volta架构的Tensor Core加速器为例,由高层至底层对比介绍这三种不同编程方式: + +- **算子库层级**:如cuBLAS基本矩阵与向量运算库,cuDNN深度学习加速库,均通过Host端调用算子库提供的核函数使能TensorCore; + +- **编程原语层级**:如基于CUDA的WMMA API编程接口。同算子库相比,需要用户显式调用计算各流程,如矩阵存取至TensorCore、TensorCore执行矩阵乘累加运算、TensorCore累加矩阵数据初始化操作等; + +- **指令层级**:如PTX ISA MMA指令集,提供更细粒度的mma指令,便于用户组成更多种形状的接口,通过CUDA Device端内联编程使能TensorCore。 + +矩阵乘法运算作为深度学习网络中占比最大的计算,对其进行优化是十分必要的。因此本节将统一以矩阵乘法$D[M, N] = C[M, N] + A[M, K] * B[K, N]$为实例,对比介绍如何通过不同编程方式使能加速器。 + +``` +$A, B$矩阵 $D$矩阵  $C[i][j] \gets 0$  +$C[i][j] \gets C[i][j] + A[i, k] \times B[k, j]$  +$D[i][j] \gets C[i][j]$  +``` + +### 硬件加速器的多样化编程方法 + +#### 算子库使能加速器 + +在上述三种层级的编程方式中,直接调用算子加速库使能加速器无疑是最快捷高效的方式。NVIDIA提供了cuBLAS/cuDNN两类算子计算库,cuBLAS提供了使能Tensor Core单元的接口,用以加速矩阵乘法(GEMM)运算,cuDNN提供了对应接口加速卷积(CONV)运算等。 + +以[1.3.1](#硬件加速器的可编程性){reference-type="ref" reference="硬件加速器的可编程性"}小节的GEMM运算为例,与常规CUDA调用cuBLAS算子库相似,通过cuBLAS加速库使能Tensor Core步骤包括: + +1. 创建cuBLAS对象句柄且设置对应数学计算模式 + +``` +cublasHandle_t handle; +cublasStatus_t cublasStat = cublasCreate(&handle); +cublasStat = cublasSetMathMode(handle, CUBLAS_TENSOR_OP_MATH); +``` + +2. 分配和初始化矩阵内存空间及内容元素 + +``` +size_t matrixSizeA = (size_t)M * K; +cublasStat = cudaMalloc(&devPtrA[0], matrixSizeA * sizeof(devPtrA[0][0])); +cublasStat = cublasSetMatrix(M, K, sizeof(A[0]), A, M, devPtrA[i], M); +``` + +3. 调用对应计算函数接口 + +``` +cublasStat = cublasGemmEx(handle, transa, transb, m, n, k, alpha, + A, CUDA_R_16F, lda, + B, CUDA_R_16F, ldb, + beta, C, CUDA_R_16F, ldc, CUDA_R_32F, algo); +``` + +4. 传回结果数据 + +``` +cublasStat = cublasGetMatrix(M, N, sizeof(D[0]), devPtrD[i], M, D, M); +``` + +5. 释放内存和对象句柄 + +``` +cudaFree(devPtrA); +cudaDestroy(handle); +``` + +当然,由于加速器一般会有矩阵形状、数据类型、排布方式等限制,因此在调用句柄和函数接口时要多加注意。如本例中,cuBLAS计算模式必须设置为$CUBLAS\_TENSOR\_OP\_MATH$,步长必须设置为8的倍数,输入数据类型必须为$CUDA\_R\_16F$等。按照如上方式即可通过cuBLAS算子库对[1.3.1](#硬件加速器的可编程性){reference-type="ref" reference="硬件加速器的可编程性"}实例使能Tensor Core加速器,通过NVIDIA官方数据可知,该方式对于不同矩阵乘法计算规模,平均有4~10倍的提升,且矩阵规模越大,加速器提升效果越明显。 + +该方式由于能够隐藏体系结构细节,易用性较好,且一般官方提供的算子库吞吐量较高。但与此同时,这种算子颗粒度的库也存在一些问题,如不足以应对复杂多变的网络模型导致的算子长尾问题(虽然常规形式算子占据绝大多数样本,但仍有源源不断的新增算子,因其出现机会较少,算子库未对其进行有效优化。),以及错失了较多神经网络框架优化(如算子融合)的机会。 + +#### 编程原语使能加速器 + +第二种加速器编程方式为编程原语使能加速器,如通过在Device端调用CUDA WMMA (Warp Matrix Multiply Accumulate) API接口。以线程束(即Warp,是调度的基本单位)为操纵对象,使能多个Tensor Core单元。该方式在CUDA 9.0中被公开,程序员可通过添加API头文件的引用和命名空间定义来使用上述API接口。基于软硬件协同设计的基本思想,该层级编程API的设计多与架构绑定,如WMMA操纵的总是$16x16$大小的矩阵块,并且操作一次跨两个TensorCore进行处理,本质是与TensorCore如何集成进SM中强相关的。针对Float16输入数据类型,NVIDIA官方提供了三种不同矩阵规模的WMMA乘累加计算接口,分别为$16x16x16$,$32x8x16$,$8x32x16$。 + +该API接口操纵的基本单位为Fragment,是一种指明了矩阵含义(乘法器/累加器)、矩阵形状($WMMA\_M, WMMA\_N, WMMA\_K$)、数据类型(Half/ Float)、排布方式($row\_major/ col\_major$)等信息的模板类型,包括如下: + +``` +wmma::fragment a_frag; +wmma::fragment b_frag; +wmma::fragment acc_frag; +wmma::fragment c_frag; +``` + +使用时,我们需要将待执行乘法操作矩阵块的数据,作为Fragment,由寄存器加载至TensorCore,在将累加Fragment初始化/清零操作后,通过TensorCore单元执行乘累加运算,最后将运算结果的Fragment存回寄存器或其他内存区域。与上述操作对应的,NVIDIA提供了$wmma.load\_matrix\_sync(), wmma.store\_matrix\_sync()$接口用于将参与计算的子矩阵块写入/载出Fragment片段;$wmma.fill\_fragment()$接口用于初始化对应Fragment的数据;$wmma.mma\_sync()$接口用于对Fragment进行乘累加运算。 + +#### 指令集编程使能加速器 + +在NVIDIA PTX ISA (Instruction Set Architecture)中提供了另一个编程接口,如Volta架构中的$mma.sync.m8n8k4$指令,它使用$M=8, N=8, K=4$的形状配置执行乘累加操作。具体地,它由线程组(黑色椭圆表示)或octet执行[@2018Modeling],如图 :numref:`PTX`显示了线程和数据的映射关系。每个线程组由四个连续的线程组成,使用不同颜色的圆圈表示。图中还指出了一个octet里面的线程在线程束内的分布,Float16乘法器A或B的四个连续元素(使用具有相同颜色的块表示),以及Float32累加器C或D的八个分散元素(同样使用相同颜色的块表示)。彩色块上的数字代表对应的线程ID。 + +![mma指令之线程与矩阵元素映射关系](../img/ch06/ptx.svg) +:width:`800px` +:label:`PTX` + +作为一个更细粒度的指令,mma可以组成更加多样化形状的Warp范围的WMMA API接口,可以控制线程束内线程与数据的映射关系,并允许AI编译器自动/手动显式地管理内存层次结构之间的矩阵分解,因此相比于直接应用NVCUDA::WMMA API具有更好的灵活性。 + +#### 算子编译器编程使能加速器 + +除了上述三种层级的编程方式,算子编译器也逐渐成为DSA加速器的关注热点。随着深度学习模型的迭代更新以及各类DSA加速器的层出不穷,手写算子或高性能算子库(如cuDNN/cuBLAS)等基于人工优化算子的方式给算子开发团队带来沉重的负担。因此,开发一种能够将High-level的算子表示编译成目标硬件可执行代码的算子编译器,成为了学术界、业界的共识。 + +近年来涌现出许多算子编译器/编译框架,如TVM,为不同的硬件后端提供了编译优化支持。在昇腾芯片上,基于TVM开发了TBE(Tensor Boost Engine),不仅提供了一个优化过的神经网络标准算子库,同时还提供了算子开发能力及融合能力。通过TBE提供的API和自定义算子编程开发界面可以完成相应神经网络算子的开发,帮助用户较容易的去使能硬件加速器上的AI\_CORE相关指令,以实现高性能的神经网络计算。此外,为了更好的支持复杂算子融合场景,还提供了基于polyhedral多面体编译技术的AKG(Auto kernel generator),提供算子的自动生成能力。 + +基于算子编译器使能加速器实现矩阵乘的流程则对用户更加友好,用户只需基于python定义矩阵乘的tensor信息(数据类型及形状等),调用对应TBE接口即可。如下所示: + +```python +a_shape = (1024, 256) +b_shape = (256, 512) +bias_shape = (512, ) +in_dtype = "float16" +dst_dtype = "float32" +tensor_a = tvm.placeholder(a_shape, name='tensor_a', dtype=in_dtype) +tensor_b = tvm.placeholder(b_shape, name='tensor_b', dtype=in_dtype) +tensor_bias = tvm.placeholder(bias_shape, name='tensor_bias', dtype=dst_dtype) +res = te.lang.cce.matmul(tensor_a, tensor_b, False, False, False, dst_dtype=dst_dtype, tensor_bias=tensor_bias) +``` + +### 硬件加速器高性能编程实例 + +本节[1.3](#加速器基本编程原理){reference-type="ref" reference="加速器基本编程原理"}前几个小节主要介绍了硬件加速器的不同层级的多样化编程方法。调用计算库的方式留给程序员的优化空间较少,合理利用硬件加速器不同层级的编程,可以实现更好的性能优化。 为了更好的让读者理解硬件加速器的使用,本节会继续[1.3.1](#硬件加速器的可编程性){reference-type="ref" reference="硬件加速器的可编程性"}节中的GEMM运算,仍以WMMA API使能Tensor Core加速单元为例,介绍如何通过矩阵分块、资源映射等方式更高效的利用硬件加速器。 + +[\[alg:TensorCore\]]{#alg:TensorCore label="alg:TensorCore"} + +``` +$A, B$矩阵 $D$矩阵   Mapping to Block.Idx  Mapping to Block.Idy  Mapping +to Block.Idz +$A_{Shared}[i_o][k_o] \gets A[i_o][k_o]$ $B_{Shared}[k_o][j_o] \gets B[k_o][j_o]$ $Syncthreads()$  + Mapping to Warp.Idx  Mapping to Warp.Idy +$A_{Register}[i_i][k_i] \gets A_{Shared}[i_i][k_i]$ $B_{Register}[k_i][j_i] \gets B_{Shared}[k_i][j_i]$ $pragma\ unroll$  +$wmma.load\_matrix\_sync(A_{Fragment}, A_{Register})$ $wmma.load\_matrix\_sync(B_{Fragment}, B_{Register})$  +$wmma.fill\_fragment(C_{Fragment}, 0)$  +$wmma.mma\_sync(D_{Fragment}, C_{Fragment}, A_{Fragment}, B_{Fragment})$  +$Syncthreads()$  $wmma.store\_matrix\_sync(D, D_{Fragment})$  +``` + +若要得到高性能CUDA程序,提高并行性、增大吞吐量、优化指令执行是至关重要的三个优化目标。针对该实例,具体地实现和优化方案列出如下,对应到具体实例伪代码如算法2所示: + +1. **优化内存结构------增大吞吐量**:将原始大规模矩阵根据不同阈值切分成不同层级的子矩阵块,使得子矩阵块能被如共享内存、寄存器等高性能体系结构存储下来,以此提高吞吐量。设置切分参数为$BlockTile[Ms, Ns, Ks]$和$WarpTile[Mw, Nw, Kw]$,对应的将BlockTile下的矩阵由全局内存搬移至共享内存,以提高全局内存合并访问和数据局部性,如图 :numref:`GEMM-BlockTile`所示;再将内层WarpTile下的矩阵由共享内存搬移至寄存器中,如图 :numref:`GEMM-WarpTile`所示,以备Tensor Core加速器数据存取。 + +![全局内存与共享内存数据交互](../img/ch06/G2S.svg) +:width:`800px` +:label:`GEMM-BlockTile` + +![共享内存与寄存器数据交互](../img/ch06/S2R.svg) +:width:`800px` +:label:`GEMM-WarpTile` + +2. **并行资源映射------提高并行性**:将多层级的并行资源(Block、Warp、Thread)与对应需要计算/搬移的数据建立映射关系,提高程序并行性。将可并行的计算/数据搬移操作映射到并行资源上,对于GEMM实例,M/N轴即为可并行轴,将数据搬移操作中的循环指令映射分配到Block层级(即算法3中的2-4行$For$循环),将内层循环指令映射分配到Warp层级(即算法3中的8-10行$For$循环)。(前文介绍,线程束Warp作为调度的基本单位,且是WMMA API操纵的基本层级,因此对Warp层级进行数据映射比Thread层级映射更为合适) + +3. **Warp统一的Tensor Core数据交互------增大吞吐量**:根据[1.3.2](#硬件加速器的多样化编程方法){reference-type="ref" reference="硬件加速器的多样化编程方法"}节中介绍的编程方法,除调用算子库外,均需要使用或将指令封装成WMMA接口形式统一进行Warp层级的数据存取和计算。如图 :numref:`GEMM-TensorCore`所示,Tensor Core加速器需要从局部内存/寄存器中读取数据,存于虚拟Fragment数据结构中,对应使用$wmma.load\_matrix\_sync()$接口,将累加Fragment $C$ 通过$wmma.fill\_fragment()$接口进行初始化后,使用$wmma.mma\_sync()$使能加速器进行乘累加运算,后将结果Fragment $D$通过调用$wmma.store\_matrix\_sync()$接口拷贝至目标内存地址。 + +![寄存器与硬件加速器交互](../img/ch06/R2TC.svg) +:width:`800px` +:label:`GEMM-TensorCore` + +4. **优化数据访存------提高并行性**:在进行内存结构变化(矩阵数据搬移)时,需要注意全局内存的合并访问、共享内存的存储体冲突等常见性能瓶颈点。 + +5. **资源负载均衡------增大吞吐量**:调整平衡每个线程处理的数据量、共享内存使用量、寄存器使用量,以获得更高的SM占用率。一般在实际程序中BlockTile和WarpTile的选取至关重要。 + +6. **优化指令执行**:使用\#unroll功能进行循环展开以避免分支冲突,如算法3中13行;使用向量化加载指令减少PTX指令执行次数以提高带宽等,对于GPU Volta架构,最大向量化加载指令为ldg128,即128比特带宽,对于算法3中5-6行数据由全局内存加载至共享内存时,即可采用Float4\*类型指针进行内存读取。 + diff --git a/chapter_hardware_accelerator/index.md b/chapter_hardware_accelerator/index.md index 07e5168..7baff90 100644 --- a/chapter_hardware_accelerator/index.md +++ b/chapter_hardware_accelerator/index.md @@ -1,10 +1,24 @@ # 硬件加速器 +上一章节,我们详细讨论了计算图的基本组成,生成和执行等关键设计。当前主流深度学习模型大多基于神经网络实现,无论是训练还是推理,都会产生海量的计算任务,尤其是涉及矩阵乘法这种高计算任务的算子。另一方面,通用处理器芯片如CPU,在执行这类算子时通常耗时较大,难以满足训练/推理任务的需求。因此工业界和学术界都将目光投向特定领域的加速器芯片设计,希望以此来解决算力资源不足的问题。 +本章将会着重介绍加速器的基本组成原理,并且以矩阵乘法为例,介绍在加速器上的编程方式及优化方法。最后,介绍由异构算子组成的异构计算图表达与执行方式。 + +本章的学习目标包括: + +- 掌握加速器的基本组成 + +- 掌握矩阵乘法的常见优化手段 + +- 理解编程API的设计理念 + +- 理解异构硬件加速的表达与执行 ```toc :maxdepth: 2 -:numbered: - -``` \ No newline at end of file +accelerator_introduction +accelerator_architecture +accelerator_programming +summary +``` diff --git a/chapter_hardware_accelerator/summary.md b/chapter_hardware_accelerator/summary.md new file mode 100644 index 0000000..c19cab6 --- /dev/null +++ b/chapter_hardware_accelerator/summary.md @@ -0,0 +1,10 @@ +总结 +---- + +- 面向深度学习计算任务,加速器通常都是由多种片上缓存以及多种运算单元组成来提升性能。 + +- 未来性能增长需要依赖架构上的改变,即需要利用可编程的硬件加速器来实现性能突破。 + +- 出于计算效率和易用性等原因,加速器一般会具有多个等级的编程方式,包括:算子库层级,编程原语层级和指令层级。 + +- 越底层的编程方式越能够灵活地制加速器,但同时对程序员的能力要求也越高。 diff --git a/img/ch06/G2S.svg b/img/ch06/G2S.svg new file mode 100644 index 0000000..58ef1e5 --- /dev/null +++ b/img/ch06/G2S.svg @@ -0,0 +1,1233 @@ + + + + + + + + + + diff --git a/img/ch06/R2TC.svg b/img/ch06/R2TC.svg new file mode 100644 index 0000000..3e6e759 --- /dev/null +++ b/img/ch06/R2TC.svg @@ -0,0 +1,2880 @@ + + + + + + + + + + diff --git a/img/ch06/S2R.svg b/img/ch06/S2R.svg new file mode 100644 index 0000000..9a50458 --- /dev/null +++ b/img/ch06/S2R.svg @@ -0,0 +1,1193 @@ + + + + + + + + + + diff --git a/img/ch06/SM.svg b/img/ch06/SM.svg new file mode 100644 index 0000000..3a7f420 --- /dev/null +++ b/img/ch06/SM.svg @@ -0,0 +1,9571 @@ + + + + + + + + + + diff --git a/img/ch06/V100.svg b/img/ch06/V100.svg new file mode 100644 index 0000000..56a90b6 --- /dev/null +++ b/img/ch06/V100.svg @@ -0,0 +1,22683 @@ + + + + + + + + + + diff --git a/img/ch06/compute_unit.svg b/img/ch06/compute_unit.svg new file mode 100644 index 0000000..206e415 --- /dev/null +++ b/img/ch06/compute_unit.svg @@ -0,0 +1,13867 @@ + + + + + + + + + + diff --git a/img/ch06/davinci_architecture.svg b/img/ch06/davinci_architecture.svg new file mode 100644 index 0000000..5cf2575 --- /dev/null +++ b/img/ch06/davinci_architecture.svg @@ -0,0 +1,3715 @@ + + + + + + + + + + diff --git a/img/ch06/ptx.svg b/img/ch06/ptx.svg new file mode 100644 index 0000000..1c4474e --- /dev/null +++ b/img/ch06/ptx.svg @@ -0,0 +1,1265 @@ + + + + + + + + + + diff --git a/img/ch06/tensor_core.svg b/img/ch06/tensor_core.svg new file mode 100644 index 0000000..a390252 --- /dev/null +++ b/img/ch06/tensor_core.svg @@ -0,0 +1,392 @@ + + + + + + + + + +