由于最近实验需要在GPU上狠下功夫,所以恶补了GPU的知识。我发现国内blog其实对GPU内部的物理、逻辑的各个组件、对存储单元等总结的并不是特别完备,所以我根据自己的理解以及他人的博客中的内容自己总结了一份GPU相关知识点,用来帮助自己回顾并普及知识。
本文将从GPU的物理结构、逻辑执行结构以及存储模块三个角度进行讲述。
如有叙述不完备的地方,请读者批评指正,我会将不正确的地方进行修改。
一、GPU发展
能看这篇文章的人相比对GPU是有一定的基础了解的,所以我就直接开始从干货开始讲起。
NVIDIA GPU架构历经多次变革,从起初的Tesla发展到最新的Turing架构,发展史可分为以下时间节点:
- 2008 - Tesla
Tesla最初是给计算处理单元使用的,应用于早期的CUDA系列显卡芯片中,并不是真正意义上的普通图形处理芯片。
- 2010 - Fermi
Fermi是第一个完整的GPU计算架构。首款可支持与共享存储结合纯cache层次的GPU架构,支持ECC的GPU架构。
- 2012 - Kepler
Kepler相较于Fermi更快,效率更高,性能更好。
- 2014 - Maxwell
其全新的立体像素全局光照 (VXGI) 技术首次让游戏 GPU 能够提供实时的动态全局光照效果。基于 Maxwell 架构的 GTX 980 和 970 GPU 采用了包括多帧采样抗锯齿 (MFAA)、动态超级分辨率 (DSR)、VR Direct 以及超节能设计在内的一系列新技术。
- 2016 - Pascal
Pascal 架构将处理器和数据集成在同一个程序包内,以实现更高的计算效率。1080系列、1060系列基于Pascal架构
- 2017 - Volta
Volta 配备640 个Tensor 核心,每秒可提供超过100 兆次浮点运算(TFLOPS) 的深度学习效能,比前一代的Pascal 架构快5 倍以上。
- 2018 - Turing
Turing 架构配备了名为 RT Core 的专用光线追踪处理器,能够以高达每秒 10 Giga Rays 的速度对光线和声音在 3D 环境中的传播进行加速计算。Turing 架构将实时光线追踪运算加速至上一代 NVIDIA Pascal™ 架构的 25 倍,并能以高出 CPU 30 多倍的速度进行电影效果的最终帧渲染。2060系列、2080系列显卡也是跳过了Volta直接选择了Turing架构。
我们常用的2080Ti就是图灵架构-https://baijiahao.baidu.com/s?id=1612181658006920092&wfr=spider&for=pc。
然而这些架构与架构之间有很多相同点,但是为了提高性能,也设计了不同的组件组合方式,所以针对同一个CUDA代码,为了达到最优的性能,不同架构的GPU上有着不同的核数配比。
二、GPU的物理结构
我们这里主要讲述Nvidia厂家的GPU结构。
这里大部分内容参考了:https://www.cnblogs.com/timlly/p/11471507.html。
在这里我们需要整体对GPU有一个认识,即GPU的物理结构包括哪些组件呢?
从硬件角度讲,一个GPU由多个SM组成,一个SM包含有多个SP(线程)(以及还有寄存器资源,shared memory资源,L1cache,scheduler,SPU,LD/ST单元等等)。那这些组件各个之间的关系又是什么样子的呢?
1 最早的Tesla架构
Tesla微观架构总览图如上。下面将阐述它的特性和概念:
拥有7组TPC(Texture/Processor Cluster,纹理处理簇)
每个TPC有两组SM(Stream Multiprocessor,流多处理器)
每个SM包含:
- 8个SP(Streaming Processor,流处理器)
- 2个SFU(Special Function Unit,特殊函数单元)
- L1缓存、MT Issue(多线程指令获取)、C-Cache(常量缓存)、共享内存
- 除了TPC核心单元,还有与显存、CPU、系统内存交互的各种部件。
在该GPU中,最核心的执行单元为SP,即真正的劳动者。本章节我们只关心物理结构,具体的存储对比我们放到下面进行讲述。
2 Fermi架构
由于Tesla比较久远,我们就不详细说明,下面为Fermi架构
Fermi架构如上图,它的特性如下:
拥有16个SM
每个SM:
2个Warp(线程束)
两组共32个Core
16组加载存储单元(LD/ST)
4个特殊函数单元(SFU)
- 每个Warp:
16个Core
Warp编排器(Warp Scheduler)
分发单元(Dispatch Unit)
- 每个Core:
1个FPU(浮点数单元)
1个ALU(逻辑运算单元)
通过左图我们能看出,每一个绿色的大block为一个SM,上面8个下面8个共16个SM。右图为SM内部结构,包括了16 * 2个core(真正用于计算的单元),上面是32768个寄存器,可被32个core所使用,上面有两组调度器以及分发单元,理论上两个调度器可以同时调度两个warp,而该架构下一个warp只能运行16个线程,后面的架构会变成32个。
3 Maxwell架构
该架构拥有四个GPC(Graphics Processing Cluster),每个GPC拥有多个SM(SMX、SMM)。当然不同架构下每个GPC的SM数量是不等的。例如,GM204有4个GPC,每个GPC有4个SM,但Tegra X1有1个GPC和2个SM,它们均采用Maxwell设计。
上图中的GPU为GM204,拥有4个GPC,图中一个GPC中包括4个SMM(Maxwell SM)。每组SMM单元又由4个小单元组成,每组32个CUDA核心
4 NVidia Kepler架构
Kepler除了在硬件有了提升,有了更多处理单元之外,还将SM升级到了SMX。该架构包括:
15个SM
6个64位memory controller
192个单精度CUDA cores,64个双精度单元,32个SFU,32个load/store单元(LD/ST)
增加register file到64K
每个Kepler的SM包含四个warp scheduler、八个instruction dispatchers,使得每个SM可以同时issue和执行四个warp。
Kepler K20X(compute capability 3.5)每个SM可以同时调度64个warp共计2048个thread。
分析一下上面的指标,总体来说,SM就是SMX,一个SMX中有192个core,有四个调度器使得每次可以执行四个warp,大大提高了执行效率。
5 Turing架构
上图是采纳了Turing架构的TU102 GPU,它的特点如下:
6 GPC(图形处理簇)
36 TPC(纹理处理簇)
72 SM(流多处理器)
每个GPC有6个TPC,每个TPC有2个SM
4,608 CUDA核
72 RT核
576 Tensor核
288 纹理单元
12x32位 GDDR6内存控制器 (共384位)
该架构非常新,处理单元也大大增加,每一个GPC中都包括了6个TPC,每个TPC中包括了12个SM,每个SM中有了新的设计,包括了64 CUDA核、8 Tensor核、256 KB寄存器文件,图中标注出一共有4个Warp调度器,可以同时运行4个warp。(16384 * 32bit = 64KB,所以64 * 4 = 256KB的寄存器大小)
与Volta架构类似,Turing提供了64个fp32核心,64个int32核心和8个改进的混合精度Tensor Cores核心。这使得fp32和int32的操作可以同时执行。但Turing仅提供2个fp64核心,因此双精度的计算吞吐量较弱。
二、GPU逻辑执行结构
软件概念:
thread-->block-->grid:在利用cuda进行编程时,一个kernel对应一个GRID(http://blog.sina.com.cn/s/blog_80ce3a550101lntp.html),一个grid分为多个block,而一个block分为多个thread,GRID跑在GPU上的时候,可能是独占一个GPU的,也可能是多个kernel并发占用一个GPU的(需要fermi及更新的GPU架构支持)。其中任务划分到是否影响最后的执行效果。划分的依据是任务特性和GPU本身的硬件特性。GRID,BLOCK,THREAD是软件概念,而非硬件的概念。
上图比较清晰的给出了三者之间的概念图,这里需要注意的地方是,一个block中包括多个线程而这些线程会统一放到一个SM中的core中进行操作,这里需要注意的地方是,一个warp为32个核(core),如果有n个Warp scheduler,那么我SM一次便只能执行n个warp,然而一个block可以包含很多线程,那怎么并行呢?
例如我们在2080Ti上测试,一个block可以包含1024个线程,即32个warp。但是如果我一次只能运行有限(<32)个warp,那剩下的warp就要排队(并不是真正的完全并行)。
这里可以补充一点,根据https://stackoverflow.com/questions/6605581/what-is-the-context-switching-mechanism-in-gpu中的说法,一个block会在任务开始前将所有线程的的状态保存到寄存器中,如果当前寄存器不够,那就等待(Thus a block is not launched until there are sufficient registers for all warps of the block, and until there is enough free shared memory for the block.
)。所以上下文切换其实就是不断的切换寄存器即可,所以GPU内部的context switch非常快速(不需要像CPU中需要将寄存器的值保存到memory中)。
从这个结果里也能发现,一个block最多有1024个线程,每个sm最多的线程也是1024。能呼应上“一个block运行在一个SM”这句话。
总结一下,比较清晰的地方是逻辑上的block概念对应物理的SM,逻辑的thread对应物理的SM中的Core。那grid这个概念我个人感觉其实没有对应的单位,即同一个grid中的block其实可以放到一个sm中,也可以放到多个sm中,具体是什么样的放置方法,GPU会自行调度以达到性能最优。
所以有个比较简单的优化规则(这里感谢hnu黄博士的分享):
三、GPU存储结构
本章节参考https://zhuanlan.zhihu.com/p/108019839
GPU内部结构除了计算单元外,最重要的就是存储单元。这里就将GPU中最常用的存储结构进行一个详细介绍,便于读者更深入的了解GPU。
这里借用上面链接中的一张图:
图中给定了一个访问速度,不同存储单元所需要花费的时钟周期信息比较清晰的给了出来。
CPU的存储单元包括全局存储,纹理存储,常量存储,共享存储,局部存储和寄存器等
。
最右侧的scope表示该存储结构在GPU内部的作用域,例如register作用单元为一个线程,shared memory表示一个block之内的单元进行共用。具体的逻辑结构图如下:
从GPU的结构来看,是分成Grid->Block->Thread,所以,GPU也是针对这个层次来设计存储结构的,从上面的表格中可以看到。register和local memory是针对Thread来设计的,shared memory是针对Block来设计,而其他的三个是针对Grid来进行设计的。
而Constant Memory,Texture Memory是用来加速GPU运行而设计的。
下面我们从小到大分析一下各个存储结构:
1 寄存器(register)
寄存器是速度最快的存储单元,位于GPU芯片的SM上,用于存储局部变量。每个SM(SMX)上有成千上万(65536)的32位寄存器,当kernel函数启动后,这些寄存器被分配给指定的线程来使用。由于不同kernel函数需要的寄存器数量也不相等,所以,也有一个规定一个线程的最大寄存器数量是256个。
2 Local Memory
Local Memory本身在硬件中没有特定的存储单元,而是从Global Memory虚拟出来的地址空间。Local Memory是为寄存器无法满足存储需求的情况而设计的,主要是用于存放单线程的大型数组和变量。Local Memory是线程私有的,线程之间是不可见的。由于GPU硬件单位没有Local Memory的存储单元,所以,针对它的访问是比较慢的。从上面的表格中,也可以看到跟Global Memory的访问速度是接近的。
3 Shared Memory
Shared Memory位于GPU芯片上,访问延迟仅次于寄存器。Shared Memory是可以被一个Block中的所有Thread来进行访问的,可以实现Block内的线程间的低开销通信。在SMX中,L1 Cache跟Shared Memory是共享一个64KB的存储单元的,他们之间的大小划分不同的GPU结构不一样;
4 Constant Memory
Constant Memory类似于Local Memory,也是没有特定的存储单元的,只是Global Memory的虚拟地址。因为它是只读的,所以简化了缓存管理,硬件无需管理复杂的回写策略。Constant Memory启动的条件是同一个warp所有的线程同时访问同样的常量数据。
5 Texture Memory
Texture Memory是GPU的重要特性之一,也是GPU编程优化的关键。Texture Memory实际上也是Global Memory的一部分,但是它有自己专用的只读cache。这个cache在浮点运算很有用,Texture Memory是针对2D空间局部性的优化策略,所以thread要获取2D数据就可以使用texture Memory来达到很高的性能。从读取性能的角度跟Constant Memory类似。
6 Global Memory
Global Memory是GPU中最大的存储单元,Host memory与GPU之间的数据交互均会通过Global Memory进行保存,它也是读取速度最慢的组件。GPU中所有计算单元均可以访问该存储单元。
https://nyu-cds.github.io/python-gpu/02-cuda/