feat(accelerator/practise): add chapter practise (#344)
@@ -29,13 +29,13 @@
|
||||
|
||||
- **寄存器文件(Register File)**:片上最快的存储器,但与CPU不同,GPU的每个SM(流多处理器)有上万个寄存器。但当每个线程使用过多的寄存器时,SM中能够调度的线程块数量就会受到限制,可执行的线程总数量会因此受到限制,可执行的线程数量过少会造成硬件无法充分的利用,性能急剧下降。所以要根据算法的需求合理使用寄存器。
|
||||
|
||||
- **共享内存(Shared Memory)**:共享内存实际上是用户可控的一级缓存,每个SM(流多处理器)中有128KB的一级缓存, 开发者可根据应用程序需要配置最大96KB的一级缓存作为共享内存。共享内存的延迟极低,只有几十个时钟周期,几乎与寄存器相当。共享内存具有高达1.5TB/s的带宽,远远高于全局内存的峰值带宽900GB/s。所以说,共享内存的使用对于一个高性能计算工程师来说是一个必须要掌握的一个概念。
|
||||
- **共享内存(Shared Memory)**:共享内存实际上是用户可控的一级缓存,每个SM(流多处理器)中有128KB的一级缓存, 开发者可根据应用程序需要配置最大96KB的一级缓存作为共享内存。共享内存的访存延迟极低,只有几十个时钟周期。共享内存具有高达1.5TB/s的带宽,远远高于全局内存的峰值带宽900GB/s。所以说,共享内存的使用对于一个高性能计算工程师来说是一个必须要掌握的一个概念。
|
||||
|
||||
- **全局内存(Global Memory)**:全局内存之所以称为全局,是因为GPU与CPU都可以对它进行读写操作。全局内存对于GPU中的每个线程都是可见的,都可以直接对全局内存进行读写操作。CPU等其他设备可以通过PCI-E总线对其进行读写操作。全局内存也是GPU中容量最大的一块内存,可达16GB之多。同时也是延迟最大的内存,
|
||||
- **全局内存(Global Memory)**:全局内存之所以称为全局,是因为GPU与CPU都可以对它进行读写操作。全局内存对于GPU中的每个线程都是可见的,都可以直接对全局内存进行读写操作。CPU等其他设备可以通过PCI-E总线对其进行读写操作。全局内存也是GPU中容量最大的一块内存,可达16GB之多。同时也是延迟最大的内存,通常有高达上百个时钟周期的访存延迟。
|
||||
|
||||
- **常量内存(Constant Memory)**:常量内存其实只是全局内存的一种虚拟地址形式,并没有真正的物理硬件内存块。常量内存有两个特性,一个高速缓存,另一个更重要的特性是它支持将某个单个值广播到线程束中的每个线程中。
|
||||
|
||||
- **纹理内存(Texture Memory)**:纹理内存是全局内存的一个特殊形态。当全局内存被绑定为纹理内存时,执行读写操作将通过专用的纹理缓存来加速。在早期的GPU上没有缓存,因此每个SM(流多处理器)上的纹理内存为设备提供了唯一真正缓存数据的方法。然而随着硬件的升级,一级缓存和二级缓存的出现,纹理缓存的这项优势已经荡然无存。纹理内存的另外一个特性,也是最有用的特性就是当访问存储单元时,允许GPU实现硬件相关的操作。比如说使用纹理内存,可以通过归一化的地址对数组进行访问,获取的数据可以通过硬件进行自动插值,从而达到快速处理数据的目的。此外对于二维数组和三维数组,支持硬件级的双线性插值与三线性插值。纹理内存另一个实用的特性是可以根据数组的索引自动处理边界条件,不需要对特殊边缘进行处理即可完成数组内元素操作,从而防止线程中分支的产生。
|
||||
- **纹理内存(Texture Memory)**:纹理内存是全局内存的一个特殊形态。当全局内存被绑定为纹理内存时,执行读写操作将通过专用的纹理缓存来加速。在早期的GPU上没有缓存,因此每个SM(流多处理器)上的纹理内存为设备提供了唯一真正缓存数据的方法。纹理内存的另外一个特性,也是最有用的特性就是当访问存储单元时,允许GPU实现硬件相关的操作。比如说使用纹理内存,可以通过归一化的地址对数组进行访问,获取的数据可以通过硬件进行自动插值,从而达到快速处理数据的目的。此外对于二维数组和三维数组,支持硬件级的双线性插值与三线性插值。纹理内存另一个实用的特性是可以根据数组的索引自动处理边界条件,不需要对特殊边缘进行处理即可完成数组内元素操作,从而防止线程中分支的产生。
|
||||
|
||||
由于寄存器的高速读取特性,因此每次计算都离不开寄存器的参与。接着是一级缓存和共享内存,然后是常量内存、纹理内存、全局内存,最后则是主机端内存。根据不同存储器之间的存储速度的数量级的变化规律,选用适当类型的内存以及最大化地利用它们,从而发挥硬件的最大算力,减少计算时间。
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
- **标量计算单元**:与标准的精简指令运算集(Reduced Instruction Set Computer,RISC)相似,一次计算一个标量元素。
|
||||
|
||||
- **一维向量计算单元**:一次可以完成多个元素的计算,与传统的CPU和GPU架构中单指令多数据(SIMD)相似,已广泛应用于高性能计算(High Performance Computing,HPC)和信号处理中。
|
||||
- **一维向量计算单元**:一次可以完成多个元素的计算,与传统的CPU和GPU架构中单指令多数据(SIMD)相似,已广泛应用于高性能计算(High Performance Computing,HPC)和信号处理中。
|
||||
|
||||
- **二维向量计算单元**:一次运算可以完成一个矩阵与向量的内积,或向量的外积。利用数据重复使用这一特性,降低数据通信成本与存储空间,更高效的提高矩阵乘法性能。
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
:width:`800px`
|
||||
:label:`compute-unit`
|
||||
|
||||
GPU计算单元主要由标量计算单元和三维向量计算单元组成。如 :numref:`SM`所示,对于每个SM,其中64个32位浮点运算单元、64个32位整数运算单元、32个64位浮点运算单元均为标量计算单元。而8个张量核则是专为神经网络应用设计的三维向量计算单元。
|
||||
GPU计算单元主要由标量计算单元组成,而在Volta及以后的架构中还加入了三维向量计算单元。如 :numref:`SM`所示,对于每个SM,其中64个32位浮点运算单元、64个32位整数运算单元、32个64位浮点运算单元均为标量计算单元。而8个张量核则是专为神经网络应用设计的三维向量计算单元。
|
||||
|
||||

|
||||
:width:`800px`
|
||||
|
||||
1041
chapter_accelerator/accelerator_practise.md
Normal file
@@ -1,7 +1,7 @@
|
||||
## 加速器基本编程原理
|
||||
:label:`accelerator-program-title`
|
||||
|
||||
本章前两节主要介绍了硬件加速器设计的意义、思路以及基本组成原理。软硬件协同优化作为构建高效AI系统的一个重要指导思想,需要软件算法/软件栈和硬件架构在神经网络应用中互相影响、紧密耦合。为了最大限度地发挥加速器的优势,要求能够基于硬件系统架构提供易用、高效的编程方法。因此,在本节中将着重介绍加速器的可编程性,包括编程接口直接调用方式及算子编译器优化方式。最后,通过示例介绍如何通过编程使能加速器,提升神经网络算子的计算效率。
|
||||
本章前两节主要介绍了硬件加速器设计的意义、思路以及基本组成原理。软硬件协同优化作为构建高效AI系统的一个重要指导思想,需要软件算法/软件栈和硬件架构在神经网络应用中互相影响、紧密耦合。为了最大限度地发挥加速器的优势,要求能够基于硬件系统架构提供易用、高效的编程方法。因此,在本节中将着重介绍加速器的可编程性,包括编程接口直接调用方式及算子编译器优化方式。
|
||||
|
||||
### 硬件加速器的可编程性
|
||||
:label:`accelerator-programable-title`
|
||||
@@ -157,37 +157,3 @@ 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)
|
||||
```
|
||||
|
||||
### 硬件加速器高性能编程实例
|
||||
|
||||
本节 :numref:`accelerator-program-title`前几个小节主要介绍了硬件加速器的不同层级的多样化编程方法。调用计算库的方式留给程序员的优化空间较少,合理利用硬件加速器不同层级的编程,可以实现更好的性能优化。 为了更好的让读者理解硬件加速器的使用,本节会继续 :numref:`accelerator-programable-title`节中的GEMM运算,仍以WMMA API使能Tensor Core加速单元为例,介绍如何通过矩阵分块、资源映射等方式更高效的利用硬件加速器。
|
||||
|
||||

|
||||
:width:`800px`
|
||||
:label:`gemm-tensor-core-algorith`
|
||||
|
||||
若要得到高性能CUDA程序,提高并行性、增大吞吐量、优化指令执行是至关重要的三个优化目标。针对该实例,具体地实现和优化方案列出如下,对应到具体实例伪代码如 :numref:`gemm-tensor-core-algorith`所示:
|
||||
|
||||
1. **优化内存结构------增大吞吐量**:将原始大规模矩阵根据不同阈值切分成不同层级的子矩阵块,使得子矩阵块能被如共享内存、寄存器等高性能体系结构存储下来,以此提高吞吐量。设置切分参数为$BlockTile[Ms, Ns, Ks]$和$WarpTile[Mw, Nw, Kw]$,对应的将BlockTile下的矩阵由全局内存搬移至共享内存,以提高全局内存合并访问和数据局部性,如 :numref:`GEMM-BlockTile`所示;再将内层WarpTile下的矩阵由共享内存搬移至寄存器中,如 :numref:`GEMM-WarpTile`所示,以备Tensor Core加速器数据存取。
|
||||
|
||||

|
||||
:width:`800px`
|
||||
:label:`GEMM-BlockTile`
|
||||
|
||||

|
||||
:width:`800px`
|
||||
:label:`GEMM-WarpTile`
|
||||
|
||||
2. **并行资源映射------提高并行性**:将多层级的并行资源(Block、Warp、Thread)与对应需要计算/搬移的数据建立映射关系,提高程序并行性。将可并行的计算/数据搬移操作映射到并行资源上,对于GEMM实例,M/N轴即为可并行轴,将数据搬移操作中的循环指令映射分配到Block层级(即 :numref:`gemm-tensor-core-algorith`中的2-4行$For$循环),将内层循环指令映射分配到Warp层级(即 :numref:`gemm-tensor-core-algorith`中的8-9行$For$循环)。(前文介绍,线程束Warp作为调度的基本单位,且是WMMA API操纵的基本层级,因此对Warp层级进行数据映射比Thread层级映射更为合适)
|
||||
|
||||
3. **Warp统一的Tensor Core数据交互------增大吞吐量**:根据 :numref:`diversified-programming-title`节中介绍的编程方法,除调用算子库外,均需要使用或将指令封装成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()$接口拷贝至目标内存地址。
|
||||
|
||||

|
||||
:width:`800px`
|
||||
:label:`GEMM-TensorCore`
|
||||
|
||||
4. **优化数据访存------提高并行性**:在进行内存结构变化(矩阵数据搬移)时,需要注意全局内存的合并访问、共享内存的存储体冲突等常见性能瓶颈点。
|
||||
|
||||
5. **资源负载均衡------增大吞吐量**:调整平衡每个线程处理的数据量、共享内存使用量、寄存器使用量,以获得更高的SM占用率。一般在实际程序中BlockTile和WarpTile的选取至关重要。
|
||||
|
||||
6. **优化指令执行**:使用\#unroll功能进行循环展开来提升指令级并行,如 :numref:`gemm-tensor-core-algorith`中13行;使用向量化加载指令以提高带宽等,对于GPU Volta架构,最大向量化加载指令为ldg128,即128比特带宽,对于 :numref:`gemm-tensor-core-algorith`中5-6行数据由全局内存加载至共享内存时,即可采用Float4\*类型指针进行内存读取。
|
||||
|
||||
@@ -20,5 +20,6 @@
|
||||
accelerator_introduction
|
||||
accelerator_architecture
|
||||
accelerator_programming
|
||||
accelerator_practise
|
||||
summary
|
||||
```
|
||||
|
||||
3
img/ch06/6.4/duplicated_data.svg
Executable file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="434px" height="262px" viewBox="-0.5 -0.5 434 262"><defs/><g><rect x="160" y="160" width="100" height="100" fill="#ccffcc" stroke="rgb(0, 0, 0)" pointer-events="all"/><rect x="0" y="160" width="120" height="100" fill-opacity="0.5" fill="#ffccff" stroke="rgb(0, 0, 0)" stroke-opacity="0.5" pointer-events="all"/><rect x="160" y="0" width="100" height="120" fill-opacity="0.5" fill="#ffffcc" stroke="rgb(0, 0, 0)" stroke-opacity="0.5" pointer-events="all"/><path d="M 0 210 L 260 210" fill="none" stroke="#666666" stroke-miterlimit="10" stroke-dasharray="1 3" pointer-events="stroke"/><path d="M 210 0 L 210 260" fill="none" stroke="#808080" stroke-miterlimit="10" stroke-dasharray="1 3" pointer-events="stroke"/><rect x="0" y="170" width="120" height="10" fill="#ffcce6" stroke="rgb(0, 0, 0)" pointer-events="none"/><rect x="31.3" y="170" width="5.22" height="10" fill="#ff99ff" stroke="rgb(0, 0, 0)" pointer-events="none"/><rect x="115.39" y="55" width="120" height="10" fill="#ffffcc" stroke="rgb(0, 0, 0)" transform="rotate(90,175.39,60)" pointer-events="none"/><rect x="172.78" y="30" width="5.22" height="10" fill="#ffff99" stroke="rgb(0, 0, 0)" transform="rotate(90,175.39,35)" pointer-events="none"/><rect x="170" y="170" width="10" height="10" fill="#99ff99" stroke="rgb(0, 0, 0)" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 8px; height: 1px; padding-top: 175px; margin-left: 171px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: "Times New Roman"; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">x</div></div></div></foreignObject><text x="175" y="178" fill="rgb(0, 0, 0)" font-family="Times New Roman" font-size="11px" text-anchor="middle">x</text></switch></g><rect x="190" y="170" width="10" height="10" fill="#99ff99" stroke="rgb(0, 0, 0)" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 8px; height: 1px; padding-top: 175px; margin-left: 191px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: "Times New Roman"; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><font style="font-size: 11px;">y</font></div></div></div></foreignObject><text x="195" y="178" fill="rgb(0, 0, 0)" font-family="Times New Roman" font-size="11px" text-anchor="middle">y</text></switch></g><rect x="135" y="55" width="120" height="10" fill="#ffffcc" stroke="rgb(0, 0, 0)" transform="rotate(90,195,60)" pointer-events="none"/><rect x="192.39" y="30" width="5.22" height="10" fill="#ffff99" stroke="rgb(0, 0, 0)" transform="rotate(90,195,35)" pointer-events="none"/><path d="M 170.39 120 L 170 170" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 3" pointer-events="none"/><path d="M 180.39 120 L 180 170" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 3" pointer-events="none"/><path d="M 170.39 120 L 170 170" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 3" pointer-events="none"/><path d="M 190.39 120 L 190 170" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 3" pointer-events="none"/><path d="M 200.39 120 L 200 170" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 3" pointer-events="none"/><path d="M 120 170 L 190 170" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 3" pointer-events="none"/><path d="M 120 179.31 L 190 179.31" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 3" pointer-events="none"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
3
img/ch06/6.4/hide_global_latency.svg
Executable file
|
After Width: | Height: | Size: 22 KiB |
3
img/ch06/6.4/hide_smem_latency.svg
Executable file
|
After Width: | Height: | Size: 16 KiB |
3
img/ch06/6.4/naive.svg
Executable file
|
After Width: | Height: | Size: 13 KiB |
3
img/ch06/6.4/use_float4.svg
Executable file
|
After Width: | Height: | Size: 19 KiB |
3
img/ch06/6.4/use_smem_load.svg
Executable file
|
After Width: | Height: | Size: 27 KiB |
3
img/ch06/6.4/use_smem_pipeline.svg
Executable file
|
After Width: | Height: | Size: 15 KiB |
3
img/ch06/6.4/use_smem_store.svg
Executable file
|
After Width: | Height: | Size: 26 KiB |
3
img/ch06/6.4/use_tile.svg
Executable file
|
After Width: | Height: | Size: 28 KiB |