Skip to content

Commit

Permalink
Merge pull request #106 from xiaotianzhang01/dev_tensorcore_img_format
Browse files Browse the repository at this point in the history
update nv tensorcore img format
  • Loading branch information
chenzomi12 authored Mar 31, 2024
2 parents f6abb32 + c4ee439 commit 86261ab
Show file tree
Hide file tree
Showing 25 changed files with 26 additions and 25 deletions.
23 changes: 12 additions & 11 deletions 02Hardware/04NVIDIA/01.basic_tc.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Tensor Core 是针对深度学习和 AI 工作负载而设计的专用核心,

本节内容将通过三个层次逐步深入地探讨卷积与TensorCore之间的关系、TensorCore的基础工作原理,以及TensorCore架构的发展历程。同时结合实际代码示例,旨在帮助读者不仅能够理解在框架层面如何利用Tensor Core 实现训练加速的具体细节,还能对CUDA编程有初步的了解。这一系列内容将为读者揭开Tensor Core技术的神秘面纱,提供一个全面且条理清晰的认识。

![Nvida GPU架构发展](images/01baisc_tc_gpuarh.png)
![Nvida GPU架构发展](images/01baisc_tc_01.png)

在我们深入探讨之前,让我们先简要回顾一下Nvidia GPU架构的演变历程。

Expand Down Expand Up @@ -41,7 +41,7 @@ Img2col 算法主要包含两个步骤,首先使用 Im2col 将输入矩阵展

卷积默认采用数据排布方式为NHWC,输入维度为4维(N,IH,IW,IC),卷积核维度为( OC, KH, K W , IC),输出维度为(N, OH, O W , OC)。

![CNN转换为GEMM计算](images/01baisc_tc_cnnvsgemm.png)
![CNN转换为GEMM计算](images/01baisc_tc_02.png)

Im2col 算法计算卷积的过程,具体简化过程如下:

Expand All @@ -57,7 +57,7 @@ Im2col 算法计算卷积的过程,具体简化过程如下:

通过Im2col操作和利用GEMM进行批量处理,卷积神经网络中的卷积层计算可以得到显著加速。这种加速不仅提高了模型训练的效率,也使得在实际应用中的推理过程更为迅速,为深度学习模型的开发和部署带来了实质性的好处。

## TensorCore基本原理
## TensorCore原理介绍

在深入探讨TensorCore及其对深度学习训练加速的作用之前,我们首先需要明确一个关键概念——混合精度训练。这个概念的理解常常困扰许多人,有些人可能会直观地认为,混合精度训练意味着在网络模型中同时使用FP16(半精度浮点数)和FP32(单精度浮点数)。

Expand All @@ -70,19 +70,19 @@ Im2col 算法计算卷积的过程,具体简化过程如下:
1. **计算的精度分配**:在模型的前向传播和反向传播过程中,使用较低的精度(如FP16)进行计算,以加快计算速度和降低内存使用量。由于FP16格式所需的内存和带宽均低于FP32,这可以显著提高数据处理的效率。
2. **参数更新的精度保持**:尽管计算使用了较低的精度,但在更新模型参数时,仍然使用较高的精度(如FP32)来保持训练过程的稳定性和模型的最终性能。这是因为直接使用FP16进行参数更新可能会导致训练不稳定,甚至模型无法收敛,由于FP16的表示范围和精度有限,容易出现梯度消失或溢出的问题。

![FP32 vs FP16](images/01baisc_tc_amp.png)
![FP32 vs FP16](images/01baisc_tc_03.png)

而在混合精度的实现上,其通常需要特定的硬件支持和软件优化。例如,Nvidia的TensorCore就是专门设计来加速FP16计算的,同时保持FP32的累加精度,从而使得混合精度训练成为可能。在软件层面,深度学习框架如PyTorch和TensorFlow等也提供了混合精度训练的支持,通过自动化的工具简化了实现过程。可以从上图看出FP16相比于FP32,不管是从整数位还是小数位来看,它所表示的范围要小很多。

![软件层面混合精度计算](images/01baisc_tc_amp2.png)
![软件层面混合精度计算](images/01baisc_tc_04.png)

混合精度训练不仅仅是在模型中同时使用FP16和FP32那么简单,而是指在底层硬件算子层面,使用半精度(FP16)作为输入和输出,使用全精度(FP32)进行中间 结果计算从而不损失过多精度的技术。这个底层硬件层面其实指的就是 Tensor Core,所以 GPU 上有 Tensor Core 是使用混合精度训练加速的必要条件。

### 第一代TensorCore

当Nvidia的架构演进到Volta架构时,它标志着对深度学习优化的重大突破。Volta架构的一个显著特点是引入了大量的TensorCore,这一变化对于加速深度学习应用产生了革命性的影响。

![CNN转换为GEMM计算](images/01baisc_tc_voltasm.png)
![CNN转换为GEMM计算](images/01baisc_tc_05.png)

在TensorCore出现之前,CUDA Core是实现深度学习加速的核心硬件技术。CUDA Core可以处理各种精度的运算。如上图 Volta架构图所示,左侧有FP64、FP32和INT32 CUDA Cores核心,右侧则是许多TensorCore核心。

Expand All @@ -102,7 +102,7 @@ Im2col 算法计算卷积的过程,具体简化过程如下:

在具体的运算过程中,Tensor Core采用融合乘法加法(FMA)的方式来高效地处理计算任务。每个 Tensor Core 每周期能执行 **4x4x4 GEMM**,64 个 浮点乘法累加(FMA)运算。

![CNN转换为GEMM计算](images/01baisc_tc_tensorcore.png)
![CNN转换为GEMM计算](images/01baisc_tc_06.png)

如上图所示,在执行运算 **D=A*B+C**,其中A、B、C 和 D是 4×4 矩阵。**矩阵乘法**输入 A 和 B 是 FP16 矩阵,而**累加矩阵** C 和 D 可以是 FP16或 FP32 矩阵。

Expand All @@ -116,7 +116,7 @@ Im2col 算法计算卷积的过程,具体简化过程如下:

### TensorCore&CUDA编程

![Warp与TensorCore关系](images/01baisc_tc_cuda-tensorcore.png)
![Warp与TensorCore关系](images/01baisc_tc_07.png)

如上图所示,在CUDA编程体系中,我们并非直接对线程进行控制,也就是图中的弯弯的线,而是通过控制一个Warp,一个Warp包含很多线程(通常为32个线程),这些线程同时并行执行,利用GPU的并行计算能力。

Expand All @@ -136,6 +136,7 @@ void fill_fragment(fragment<...> &a, const T& v);
void mma_sync(fragment<...> &d, const fragment<...> &a, const fragment<...> &b, const fragment<...> &c, bool satf=false);

```
- fragment:Tensor Core数据存储类,支持matrix_a、matrix_b和accumulator
- load_matrix_sync:Tensor Core数据加载API,支持将矩阵数据从global memory或shared memory加载到fragment
- store_matrix_sync:Tensor Core结果存储API,支持将计算结果从fragment存储到global memory或shared memory
Expand All @@ -146,21 +147,21 @@ CUDA通过**CUDA C++ WMMA API**向外提供了Tensor Core在Warp级别上的计
在上文,我们提到一个Tensor Core每个周期可以执行4x4x4的GEMM(General Matrix Multiply)运算。然而,在CUDA的层面,为什么其却提供了使用16x16x16的GEMM运算API呢?
![TensorCore硬件原理](images/01baisc_tc_amp-tensorcore.png)
![TensorCore硬件原理](images/01baisc_tc_08.png)
事实上,如果我们整体来看,如上图所示,一个Tensor Core是一个4x4的TensorCore核心。但实际上,在一个SM(Streaming Multiprocessor)中有多个Tensor Core,我们无法对每个Tensor Core进行细粒度的控制,否则效率会很低。因此,一个Warp就扮演了重要角色,将多个Tensor Core打包在一起,以执行更大规模的计算任务。
通过Warp层的卷积指令,CUDA向外提供了一个16x16x16的抽象层,使得开发者可以通过一条指令完成多个Tensor Core的协同工作,实现高效的并行计算。这条指令也即我们之前提到的`mma_sync` API,它允许开发者利用Warp内的线程同时调度多个Tensor Core执行矩阵乘加操作,从而提高GPU计算的效率和性能。
那么现在有一个问题, Tensor Core 是如何跟卷积计算或者 GEMM 计算之间进行映射的呢?
例如GPU中的Tensor Core 一次仅仅只有4x4这么小的kernel,怎么处理 input image 224*224,kernel 7 * 7 的GEMM计算呢?
例如GPU中的Tensor Core 一次仅仅只有4x4这么小的kernel,怎么处理 input image 224*224,kernel 7* 7 的GEMM计算呢?
或者说在现在大模型时代,其是怎么处理Transformer结构inputembedding为2048*2048,hiddensize为1024*1024的GEMM呢?
上文我们已经提到,卷积运算可以被转化为矩阵乘法操作,这一点是连接卷积和TensorCore的桥梁。
![TensorCore硬件原理](images/01baisc_tc_cnn-tensorcore.png)
![TensorCore硬件原理](images/01baisc_tc_09.png)
在实际执行过程中,如上图中所示,蓝色矩阵和黄色矩阵的片段会被取出进行计算,即所谓的 Fragment。这些 Fragment 进行计算后形成 Fragment block,而这些Fragment block在CUDA编程模型中就是通过线程块(Thread block)的来组织执行的。在线程块内部的计算过程中,会进一步提取部分数据形成 Warp level级别的计算,Warp level的计算其实还是很大,于是在Fragment执行时会将其变为满足我们Tensor Core和矩阵输入的计算了。
Expand Down
28 changes: 14 additions & 14 deletions 02Hardware/04NVIDIA/02.history_tc.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# TensorCore架构研进

![Nvida GPU TensorCore发展](images/02history_tc_gpuarh.png)
![Nvida GPU TensorCore发展](images/02history_tc_01.png)

自Volta架构时代起,Nvidia的GPU架构已经明显地转向深度学习领域的优化和创新。2017年,Volta架构横空出世,其中引入的张量核心(Tensor Core)设计可谓划时代之作,这一设计专门针对深度学习计算进行了优化,通过执行融合乘法加法操作,大幅提升了计算效率。与前一代Pascal架构相比,Volta架构在深度学习训练和推理方面的性能提升了3倍,这一飞跃性进步为深度学习的发展提供了强大的硬件支持。

Expand All @@ -18,7 +18,7 @@

在开始介绍Volta架构中的第一代TensorCore之前,我们先来了解一下Volta架构的实现细节。

![Volta SM](images/02history_tc_voltasm.png)
![Volta SM](images/02history_tc_02.png)

如上图所示,左边就是Volta SM的架构图,Volta架构中的Streaming Multiprocessor(SM)通过引入子核心(subCore)概念,提升了其执行效率和灵活性。在Volta架构中,一个SM由4个subCore组成,每个subCore可以被视为一个更小的、功能完备的执行单元,它们共同工作以提高整体的处理能力。

Expand All @@ -36,13 +36,13 @@

值得注意的是,每个SM Sub-Core内含有两个4x4x4 Tensor Core。Warp Scheduler向Tensor Core发送矩阵乘法GEMM运算指令。Tensor Core接收来自寄存器文件的输入矩阵(A、B、C),执行多次4x4x4矩阵乘法操作,直至完成整个矩阵乘法,并将结果矩阵写回寄存器文件(Register File)中。

![Volta SM微架构](images/02history_tc_volta_smv.png)
![Volta SM微架构](images/02history_tc_03.png)

如上图所示,最上面是共享的L1缓存,每个时钟周期可以执行4个Warp指令,下属4个独立的SubCore,里面的数据是不进行缓存的,但是每个SubCore里有两个TensorCore, 这两个TensorCore中的数据是可以共享的,再往下有一个共享内存,每个时钟周期可以传输128B的数据,当所有的SM计算完这个权重矩阵就会将数据回传到L2 Cache 中,最后返回Host Cpu中。

- SubCore微架构

![Volta SubCore微架构](images/02history_tc_subcore_v.png)
![Volta SubCore微架构](images/02history_tc_04.png)

如上图所示,在一个SubCore内的微架构中,顶部依然是L1 Cache,紧随其后的是L0 Cache,也就是Register File。Register File负责将数据传输到Warp Scheduler,而具体的指令调度则依赖于Warp Scheduler。针对通用的矩阵计算任务,即CUDA Core计算,我们通过Math Dispatch Unit将指令分发到具体的FP64、INT、FP32和MUFU等执行单元进行计算。

Expand All @@ -58,7 +58,7 @@

在Turing架构中,我们直接进入SubCore,即微内核,来了解Tensor Core的第二代。

![Turing SubCore微架构](images/02history_tc_Turing_arch.png)
![Turing SubCore微架构](images/02history_tc_05.png)

如上图所示,与之前的版本相比,Turing架构的Tensor Core除了支持FP16类型之外,还增加了INT8和INT4等多种类型,这一变化使得Turing架构在处理不同精度的计算任务时更加得心应手。

Expand All @@ -75,7 +75,7 @@ Turing架构的第二代Tensor Core在距离上一代Volta架构仅一年的时

- Ampere多级带宽体系

![Ampere多级带宽体系](images/02history_tc_data_move_sys.png)
![Ampere多级带宽体系](images/02history_tc_06.png)

如上图所示为Ampere架构多级带宽体系,我们从下往上看,最下面为这次架构升级所引入NVLink技术,它主要来优化单机多块GPU卡之间的数据互连访问。在传统的架构中,GPU之间的数据交换需要通过CPU和PCIe总线,这成为了数据传输的瓶颈。而NVLink技术允许GPU之间直接进行高速的数据传输,极大地提高了数据传输的效率和速度。

Expand All @@ -91,7 +91,7 @@ Turing架构的第二代Tensor Core在距离上一代Volta架构仅一年的时

在Ampere 之前的 GPU 架构中,如果要使用共享内存(Shared Memory),必须先把数据从全局内存(Global Memory)加载到寄存器中,然后再写入共享内存。这不仅浪费了宝贵的寄存器资源,还增加了数据搬运的时延,影响了GPU的整体性能。

![Ampere异步内存拷贝机制](images/02history_tc_sync_copy.png)
![Ampere异步内存拷贝机制](images/02history_tc_07.png)

如上图所示,Ampere 架构中提供异步内存拷贝机制,通过新指令LDGSTS(Load Global Storage Shared),实现全局内存直接加载到共享内存,避免了数据从全局内存到寄存器再到共享内存的繁琐操作,从而减少时延和功耗。

Expand All @@ -101,7 +101,7 @@ Turing架构的第二代Tensor Core在距离上一代Volta架构仅一年的时

- TensorCore FFMA

![Ampere FFMA](images/02history_tc_A_ffma.png)
![Ampere FFMA](images/02history_tc_08.png)

当我们深入研究FFMA(Fuse Fold Math and Add)操作时,我们可以从上图中得到更多的信息。在这个图中,绿色的小块代表SubCore,也就是之前提到的SubCore。而图中连续的蓝色框代表寄存器Registers。

Expand Down Expand Up @@ -131,7 +131,7 @@ Turing架构的第二代Tensor Core在距离上一代Volta架构仅一年的时

在第4代Tensor Core中,一个显著的创新是引入了Tensor Memory Accelerator(简称TMA),这一功能被称为增量内存加速。这一技术的出现,极大地提升了数据处理效率,为高性能计算领域注入了新的活力。

![Hopper FFMA](images/02history_tc_A100vsH100.png)
![Hopper FFMA](images/02history_tc_09.png)

对比A100与H100的SM架构图,如上图所示,我们可以发现二者在结构上并没有太大的差异。然而,由于工艺制程的进步,H100中的CUDA Core和Tensor Core的密度得以显著提升。更为重要的是,H100中新增了Tensor Memory Accelerator,这一硬件化的数据异步加载机制使得全局内存的数据能够更为高效地异步加载到共享内存,进而供寄存器进行读写操作。

Expand All @@ -141,21 +141,21 @@ Turing架构的第二代Tensor Core在距离上一代Volta架构仅一年的时

- 分布式共享内存和warp group编程模式

![Hopper TBC](images/02history_tc_grid_a100vsh100.png)
![Hopper TBC](images/02history_tc_10.png)

如上图所示,在H100之前的架构中,线程的控制相对有限,主要基于Grid和Block的划分,分别对应硬件SM和Device,且局部数据只能通过 shared memory 限制在SM内部,不能跨SM。然而,在Hopper架构中,情况发生了显著变化。通过在硬件层面引入交叉互联网络,数据得以在4个SM之间进行拓展和共享,GPC内SM可以高效访问彼此的共享内存。这一创新使得SM之间能够实现高效的通信,从而打破了之前架构中SM间的数据隔阂。

此外,随着硬件架构的变革,软件编程模式也相应发生了调整。Warp group编程模式的提出,以及与之相关的Tensor Block Cluster概念,都体现了软硬件协同优化的思想。这种编程模式能够更好地利用Hopper架构的特性,实现更高效的计算性能。

更直观的从软件层面去看一下,有什么区别呢?

![Hopper TBC Block](images/02history_tc_warpCluster_a100vsh100.png)
![Hopper TBC Block](images/02history_tc_11.png)

如上图左边所示, 就是没有进行分布式共享内存的,每个Thread Block都对应一个SM,每个SM内部拥有自己的共享内存。然而,SM与SM之间无法进行直接的数据交互,这意味着它们之间的通信必须通过全局内存进行。这种通信方式不仅增加了数据传输的时延,还可能导致寄存器的过度使用,降低了计算效率。

而在H100架构中,如上图右边所示,通过引入SM的Cluster或Block的Cluster,实现了硬件层面的分布式共享内存。这意味着SM与SM之间的数据可以直接进行互联,无需再通过全局内存进行中转。这种机制极大地减少了数据传输的时延,提高了数据交互的效率。同时,由于数据可以在SM之间直接共享,寄存器的使用也得到了更加合理的分配,减少了不必要的浪费。

![Hopper 直接读取共享内存异步Tensor Core](images/02history_tc_shareMemcp_a100vsh100.png)
![Hopper 直接读取共享内存异步Tensor Core](images/02history_tc_12.png)

此外,通过TMA将SM组织成一个更大的计算和存储单元,从而实现了数据从全局内存(global memory)到共享内存(shared memory)的异步加载,以及数据到寄存器的计算和矩阵乘法的流水线处理,最后通过硬件实现了矩阵乘法的流水线。硬件实现的矩阵乘法流水线确保了计算过程的连续性和高效性,使得GPU能够更快地处理大规模矩阵运算。

Expand All @@ -166,7 +166,7 @@ Turing架构的第二代Tensor Core在距离上一代Volta架构仅一年的时

在处理这些大模型时,词向量的维度和矩阵的规模都会变得非常庞大,这给计算带来了极大的压力。而Tensor Core的数量是有限的,尽管在V100中,我们能够通过软件的方式将其扩展到16x16x16的规模,通过局部数据的搬运来提升计算效率,但这并不意味着我们能够轻松地处理所有嵌入的向量或大矩阵。

![transformer计算](images/02history_tc_transformer.png)
![transformer计算](images/02history_tc_13.png)

实际上,对于大模型的处理,我们需要更加精细地考虑数据的布局和计算的方式。例如,当input size达到1024,batch size为5120时,使用FP16进行训练,整个词汇表的大小可能超过三万个词汇。在transformer的Attention架构中,会涉及到大量的矩阵乘法运算。为了提高性能,我们通常需要对矩阵进行padding操作,使其维度成为8的倍数。这样一来,整体的计算性能会得到显著提升。

Expand All @@ -177,7 +177,7 @@ Turing架构的第二代Tensor Core在距离上一代Volta架构仅一年的时

经过上述探讨,我们可以对历代的Tensor Core进行一个简要的总结。Tensor Core的演进主要带来了三大关键提升。

![tensorCore性能提升](images/02history_tc_sumary.png)
![tensorCore性能提升](images/02history_tc_14.png)

首先,是内存能力的提升,有效地打破了所谓的“内存墙”。随着技术的不断进步,Tensor Core在处理大规模数据时,能够更高效地利用和管理内存资源,从而显著提升了计算性能。

Expand Down

0 comments on commit 86261ab

Please sign in to comment.