GPU结构与CUDA系列3GPU软件抽象:Grid,Block,Thread,Warp定义说明与硬件的映射执行细节
Posted 呆呆象呆呆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GPU结构与CUDA系列3GPU软件抽象:Grid,Block,Thread,Warp定义说明与硬件的映射执行细节相关的知识,希望对你有一定的参考价值。
1 GPU运行机制总述
市面上有很多GPU
厂家,他们产品的架构各不相同,但是核心往往差不多,整明白了一个基本上就可以触类旁通了。
1.0 GPU计算流程(CPU协同GPU计算)
一个典型的计算流程是这样的:
- 数据从
CPU
的内存拷贝到GPU
的内存 CPU
把计算指令传送给GPU
GPU
把计算任务分配到各个CUDA core
并行处理- 计算结果写到
GPU
内存里, 再拷贝到CPU
内存里.
1.1 Host与Device
一个CUDA
程序的可以分为两个部分(两者拥有各自的存储器):
- 在
CPU
上运行的称为Host程序
- 在
GPU
上运行的称为Device程序
1.2 Kernel
GPU
上运行的函数又被叫做Kernel函数
。
Host程序
在调用Device程序
时,可以通过参数确定执行该Kernel
的CUDA threads
的数量。
每个Thread
在执行Kernel函数
时,会被分配一个thread ID
,Kernel函数
可以通过内置变量threadIdx
访问。
1.3 图解说明从CPU到GPU的运行机制
CUDA
在执行的时候是让Host程序
里面的一个一个的Kernel函数
按照Grid
(线程网格)的概念在GPU
上执行。
一个Kernel函数
对应一个Grid
。
每个Grid
中的任务是一定的。当要执行这些任务的时候,每一个Grid
又把任务分成一部分一部分的Block
(线程块),Block
中间有若干Thread
(线程),再分成线程来完成。
1.4 Single Instruction Multiple Threads(SIMT)
-
GPU
中的SIMT体系结构
相对于CPU
的SIMD
(单指令多数据,Single Instruction Multiple Data)。中文翻译:单指令多线程。SIMT
对于可编程性的好处使得NVIDIA
的GPU
架构师为这种架构命名,而不是将其描述为SIMD
。 -
为了有效地管理和执行多个单线程,流多处理器(
SM
)采用了SIMT架构
。此架构在第一个unified computing GPU
中由NVIDIA公司
生产的GPU
引入。 -
GPU
使用SIMT
执行 32 个并行线程的Warp
,实现单指令、多线程,这使得每个线程能够访问自己的寄存器,从不同的地址加载和存储,并遵循不同的控制流路径。CUDA编译器
和GPU
一起工作,以确保Warp
的线程组尽可能频繁地被分配到SM
中,一起执行相同的指令序列,从而最大限度地提高性能。 -
每个线程可以包含控制流指令(控制流指令为标量指令)
-
同组
Warp
中的这些线程可以执行不同的控制流路径 -
当一个
Warp
中的线程分支到不同的执行路径时,产生分支发散(Branch divergence)
优势
- 共享控制逻辑可以有更多的空间面基去分配给计算单元
- 大量的并行操作,不需要进行复杂的控制编程
SIMD VS SIMT
-
CPU
中通过SIMD
来处理矢量数据;纯粹使用SIMD
不能并行的执行有条件跳转的函数,很显然条件跳转会根据输入数据不同在不同的线程中有不同表现。 -
GPU
则使用SIMT
,无需开发者费力把数据凑成合适的矢量长度,并且SIMT
允许每个线程有不同的分支,利用SIMT
才能做到不同分支的并行操作。
1 GPU的软件抽象
软件资源的抽象即为GPU
的线程模型,可以分为Grid
、Block
、Thread
和Warp
。
Grid
、Block
、Thread
是一种软件组织结构,是线程组织的三个层次,并不是硬件的,因此理论上我们可以以任意的维度(一维、二维、三维)去排列Grid
,Block
,Thread
;在硬件上就是一个个的SM
或者SP
,并没有维度这一说,只是软件上抽象成了具有维度的概念。
thread,block,gird在不同维度的大小根据算力不同是有限制的:所以在不同CUDA版本或在编译时没有指定架构的情况下,可能CUDA版本也会对thread,block,grid在不同维度的大小产生影响。
1.1 Grid(线程网格)
一个Kernel函数
对应一个Grid
。
一个Grid
中会分成若干个Block
。同一Grid
下的不同Block
可能会被分发到不同的SM
上执行。
Grid
跑在GPU
上的时候,可能是独占一个GPU
,也可能是多个kernel函数
并发占用一个GPU
(后面这种实现需要fermi及更新的GPU
架构支持)。
1.2 Block
数个threads
会被群组成一个block
,同一个block
中的threads
可以同步,也可以通过shared memory
通信
1.3 Thread
一个CUDA
的并行程序会被以许多个Thread
来执行
每个Thread
中的局域变量被映射到SM
的寄存器上,而Thread
的执行则由CUDA核心
也就是SP
来完成。
1.4 Warp
Warp
是GPU
执行程序时的调度单位,同一个Warp
里的线程执行相同的指令,即SIMT
。
一个SM
的CUDA core
会分成几个Warp
(即CUDA core
在SM
中分组),由Warp scheduler
负责调度。尽管Warp
中的线程从同一程序地址,但可能具有不同的行为,比如分支结构。因为GPU
规定同一Warp
中所有线程在同一周期执行相同的指令,Warp
发散分支过多会导致有效分支减少性能下降。
一个SM
同时并发的Warp
是有限的,因为资源限制,SM
要为每个线程块分配共享内存,也要为每个线程束中的线程分配独立的寄存器,所以SM
的配置会影响其所支持的线程块和Warp
并发数量。
一个Warp
中的线程必然在同一个block
中,如果block
所含线程数目不是Warp
大小的整数倍,那么多出的那些thread
所在的Warp
中,会剩余一些inactive的thread
,也就是说,即使凑不够Warp
整数倍的thread
,硬件也会为Warp
凑足,只不过那些thread
是inactive状态,需要注意的是,即使这部分thread
是inactive的,也会消耗SM
资源。由于warp的大小一般为32,所以block所含的thread的大小一般要设置为32的倍数。
例:如果一个块中有128个线程,那么线程0-31将在一个
Warp
中,32-63将在下一个Warp
中
Warp
非常重要,原因如下:
Warp
中的线程是被绑定在一起的。如果Warp
中的一个线程沿着if-else块
的if
侧走,而其他线沿着else
侧走,那么实际上所有32条线程都会沿着两侧走。在执行功能上是没有问题的,那些不应该被执行分支的线程会被禁用,因此始终获得正确的结果,但是如果双方都很长,那么性能损失就很重要。Warp
内的线程(实际上是半纠缠的(self-warp))一起从内存中获取数据,是一起访问共享内存中的同一段数据同一段的。也就是说如果可以确保Warp
中的所有线程都从同一段内获取数据,就只需要实现一次内存转换。- 如果它们都从随机地址获取数据,那么就需要排队去实现32次内存转换。
2 软件抽象和硬件结构的一一对应关系
硬件结构可以参考之前的一篇文章
2.1 Block对应于SM
-
SM
上可以同时存在多个Block
被执行,这些Block
不一定来自同一个kernel函数
。 -
SM
设备有Device Limit
,Warp
和Block
的数量不能超过对应的上限。 -
除了受到设备定义的限制之外,还受到硬件资源的限制:
SP
的寄存器数量- 线程块消耗的共享内存量
每个线程会占用一定数量的寄存器和
Shared Memory
,因此SM
上同时存活的Block
数目不应当超过这些硬件资源的限制。由于SM
上可以同时有来自不同kernel
的Block
存在,因此有时候即便SM
上剩余资源不足以再容纳一个kernel A
的Block
,但却仍可能容纳下一个kernel B
的Block
。 -
一个线程块的
thread
只能在一个SM
上调度
2.2 Block与Thread之间的联系Warp 对应于 SM与SP之间的联系
- 软件抽象里,认为任务分配到
Block
之后,所有的线程是并行执行的,这只是个逻辑上无懈可击的抽象,事实上我们不可能对一个任意大小的Block
都给出一个同等大小的CUDA核心阵列
去推动它的并行计算,来真正并行的执行它们。因而有了Warp
这个概念。物理上,Block
被划分成一块块的warp
分别映射到CUDA核心阵列
上执行,每一个warp
就都可以理解为是一个线程的集装箱,为的是线程数量固定统一可以给他分配统一的硬件资源,每个集装箱只装一种货物,也就是下面同步执行的意思。 - 目前,CUDA中的
Warp
都是从threadIdx = 0开始,以threadIdx连续的32个线程为一组划分得到,即便最后剩下的线程不足32个,也将其作为一个Warp
。CUDA kernel
的配置中,我们经常把Block
的size设置为32的整数倍,正是为了让它能够精确划分为整数个Warp
(更深刻的原因和存储器访问性能有关,但这种情况下仍然和Warp
的size
脱不了干系)。 Warp
是SM
调度和执行的基础概念。Block
被划分成32个线程组成的Warp
。这样,大量的Warp
生存在SM
上,等待被调度到CUDA核心阵列
去执行。Warp
中的活动线程由Warp Scheduler
驱动。每一块SM
中有单独的一个或者多个Warp Scheduler
(举例:GM204中32个CUDA核心共享一个Warp Scheduler),以及多个CUDA核心。- 当一个
Warp
执行中出现等待(存储器读写延迟等)后,Warp Scheduler
就迅速切换到下一个可执行的Warp,对其发送指令直到这个Warp
又一次出现等待,周而复始。这就是常说“用多线程掩盖延迟”。SM
会从驻留在SM
上的所有Warp
中进行指令调度。(这里的驻留表示已经可以被执行的Warp
,会从这里挑选,这时候挑选出来的Warp
能来自于驻留在SM
上的任何线程块)。 - 通常一个
SM
中的SP
会分成几个Warp
(也就是SP
在SM
中是进行分组的,物理上进行的分组)。 - 同步执行:
Warp
中的32个SP
是一起工作的,执行相同的指令,如果没有这么多thread
需要工作,那么这个Warp中的一些SP
是不工作的,处于闲置状态。
2.3 Thread对应于SP
Thread
在SP
也就是CUDA Cores
上执行Thread
会被分配Register/Local Memory
,数据存在这里SM
上的CUDA核心
是有限的,它们代表了能够在物理上真正并行的线程数(也就是优化到最佳情况下所能最大达到同一时刻在运行的并行数量)- 每一个线程都有自己的寄存器内存和
local memory
,一个warp
中的线程是同时执行的,也就是当进行并行计算时,线程数尽量为32的倍数,如果线程数不上32的倍数的话;假如是1,则warp
会生成一个掩码,当一个指令控制器对一个warp单位的线程发送指令时,32个线程中只有一个线程在真正执行,其他31个 进程会进入静默状态。
3 软件抽象和硬件结构对应关系的例子
把GPU
跟一个学校对应起来,学校里有教学楼、操场、食堂,还有老师和学生们;很快有领导(CPU
)来检查卫生(需要执行的任务Host程序
),因此这个学校的学生们要完成打扫除的工作(Device程序
)。
-
软件抽象资源包括Thread、Warp、Block和Grid
-
硬件资源包括SP和SM
3.1 软件抽象
Grid
对应的是年级
是抽象的划分组织方式
根据年级划分任务,Grid可以分为多个不同的班级
Block
对应的是班级
是抽象的划分组织方式
每个班级有若干的同学(线程),可能一个两个不同的年级会出现在同一层楼(SM),或者一层楼只有一个班级,或者没有班级,但是每一层楼的班级最大数量是固定的
Warp
对应的是兴趣小组
每个小组有32个学生;(同一时间他们一定是一个班级下的小组)
并且数量固定,即使凑不满这么多学生需要加进来不干活的学生,凑够一个小组
只要求他们有着一样的兴趣爱好(能执行相同的任务)
Thread
对应的是学生
一个Thread对应一个SP
每个学生都有个课桌 ,放自己的物品,不能让别人用,表示每个Thread在软件上都有自己的空间(寄存器等)
3.2 硬件资源
SM
对应的是教学楼的一个楼层
是实际存在的资源
一个楼层上可以有多个班级,年级和楼层并没有确定的对应关系,一个楼层中可以有很多来自不同的年级的Block
SM中的SP会被分成兴趣小组,承接不同的任务
SP
对应的是学生
一个SP对应一个Thread
是实际存在的资源
每个学生都有个课桌 ,放自己的物品,不能让别人用,表示每个SP在硬件上都有自己的空间(local memory + registers);
在楼层中,有公共的空间(走廊、厕所等),这一层楼的所有同学都可以停留,表示一个SM中有shared memory,这个SM上的Block都可以访问;(shared memory是不是所有的block都可以访问)
学校里的公共区域,比如操场、食堂等,所有同学都可以去运动、吃饭,表示GPU中有一些公共的存储空间供所有的Grid访问。
3.3 执行任务
虽然GPU
是并行运行,但也并不是我们理想中所有的Thread
一起工作,在打扫卫生时,并不是所有学生一起干活,学生经过老师(这里我们理解为Wrap Scheduler
)安排后,分为一组一组的小组,每一个小组都只会做一件一样的事情,如果有人先做完了或者不需要做,那么他也会在旁边等他的组员,处于等待状态idle
。
4 用多线程掩盖延迟
Global Memory
访存延迟可以达到数百个时钟周期,即便是最快的Shared Memory
和寄存器在有写后读依赖时也需要数十个时钟周期。这似乎和CUDA
强大的处理能力完全相悖。
为什么GPU
具有这么高的计算能力?如果连寄存器都这么慢,怎么会有高性能呢?难道这不会成为最大的瓶颈吗?
因为这个高延迟的开销被掩盖了,掩盖在大量线程之下。更清楚的说,控制单元(Warp Scheduler
)在多组线程之间快速切换,当一组线程Warp
(一个线程组,在CUDA里叫做Warp
)因为访存或其他原因出现等待时,就将其挂起,转而执行另一组线程,GPU
的硬件体系允许同时有大量线程存活于GPU
的SM
(流多处理器)之中,这种快速切换保证资源的最大利用率——控制单元始终有指令可以发放,执行单元始终有任务可以执行,仍然可以保持最高的指令吞吐,每个单元基本都能保持充分的忙碌。
这就是GPU
硬件设计中非常有特色的基本思想:用多线程掩盖延迟
。这一设计区别于CPU
的特点是,大量高延迟寄存器取代了少量低延迟寄存器,寄存器的数量保证了可以有大量线程同时存活,且可以在各组线程间快速切换。尽管每个线程是慢的,但庞大的线程数成就了GPU
的数据吞吐能力。
下面图片可以说明:GPU
用多个Warp
掩盖延迟 / 与CPU
计算模式的对比
GPU
因为多个Warp
可以快速切换来掩盖延迟,而CPU
用快速的寄存器来减小延迟。两者的重要区别是寄存器数目,CPU
的寄存器快但少,因此Context Switch
代价高;GPU
寄存器多而慢,但寄存器数量保证了线程Context Switch
非常快。同时也是因为GPU
对高延迟的容忍度比较高,他只追求在长时间内比较稳定的较大吞吐量,而不在意响应时间。
4.1 多少线程才能够掩盖掉常见的延迟呢?
对于GPU
,最常见的延迟大概要数寄存器写后读依赖,即一个局域变量被赋值后接着不久又被读取,这时候会产生大约24个时钟周期的延迟。为了掩盖掉这个延迟,我们需要至少24个Warp
轮流执行,一个Warp遇到延迟后的空闲时间里执行其余23个Warp
,从而保持硬件的忙碌。在Compute Capability 2.0
,SM
中有32个CUDA
核心,平均每周期发射一条指令的情况下,我们需要
24
∗
32
=
768
24*32 = 768
24∗32=768个线程来掩盖延迟。
保持硬件忙碌,用CUDA
的术语来说,就是保持充分的Occupancy
,这是CUDA
程序优化的一个重要指标。
5 关于现代GPU如此进行软件抽象和硬件设计的一些思考
整个设计逻辑关系我觉得可以归结为如下的情况
-
目标是实现任务
-
发现任务具有如下的特性:允许一定的延迟;需要大吞吐量;有大量同样的操作或者计算
-
所以设计了现有的硬件体系架构,软件抽象模型
那么为什么这样的计算或者说任务可以被如上所说的硬件软件更好的完成呢?
其实是因为我们是在已知任务特性的情况下(我们实际使用中所需要完成的任务大概率属于这些,或者说这些任务在CPU
上比较容易有掣肘),才把结构设计成这样的。
-
第一方面:
-
现实世界中应用在大规模数据上的计算,通常都涵盖在这一计算模式之中,因而考虑更复杂的模式本质上是不必要的。
比如计算大气的流动,每一点的风速仅仅取决于该点邻域上的密度和压强分布;
比如计算图像的卷积,每一个输出像素都仅是对应源点邻域和一个卷积核的内积。
-
从这些例子中我们可以看到,除了各个数据单元上进行的计算是一样的,计算中数据之间的相互影响也具有某种“局域性”,一个数据单元上的计算最多需要它某个邻域上的数据。这一点意味着线程之间是弱耦合的,邻近线程之间会有一些共享数据(或者是计算结果),远距离的线程间则独立无关。
这个性质反映在
CUDA
里,就是Block
划分的两重天地:Block
内部具有Shared Memory
,线程间可以共享数据、通讯和同步,Block
外部则完全独立,Block
间没有通讯机制,相互执行顺序不影响计算结果。这一划分使得我们既可以利用线程间通讯做一些复杂的应用和算法加速,又可以在Block
的粒度上自由调度计算任务,在不同计算能力的硬件平台上自适应的调整任务安排。
-
-
第二方面:
多个线程同步执行一致的运算,使得我们可以用单路指令流对多个执行单元进行控制,大幅度减少了控制器的个数和系统的复杂度
-
第三方面:
- 把注意力放在“几乎一致”这里。最简单的并行计算方案是多路数据上同时进行完全一致的计算,即
SIMD
(单指令多数据流)。这种方案是非常受限的。事实上我们可以看出,“完全一致”是不必要的。只要这些计算在大多数时候完全一致,就可以对它们做类似于SIMD
的加速,不同点是在计算分叉时候,各个线程不一致的特殊情况下,只需要分支内并行,分支间串行执行即可,毕竟这些只是很少出现的情况。 这样,把“完全一致”这个限制稍微放松,就可以得到更广阔的应用范围和不输于SIMD
的计算性能,即SIMT
(单指令流多线程)的一个重要环节,这是GPU
强大处理能力的原因。
- 把注意力放在“几乎一致”这里。最简单的并行计算方案是多路数据上同时进行完全一致的计算,即
LAST 参考文献
(3条消息) gpu的单位表示_GPU中的基本概念_weixin_39717121的博客-CSDN博客
CUDA的thread,block,grid和warp - 知乎
GPU架构之Hierarchy Memory多级存储 - 知乎
Nvidia GPU架构 - Cuda Core,SM,SP等等傻傻分不清?_咚咚锵的博客-CSDN博客_cuda sm
Fermi威力完美呈现,GeForce GTX 580全球同步评测 - 超能网
以上是关于GPU结构与CUDA系列3GPU软件抽象:Grid,Block,Thread,Warp定义说明与硬件的映射执行细节的主要内容,如果未能解决你的问题,请参考以下文章
GPU结构与CUDA系列4GPU存储资源:寄存器,本地内存,共享内存,缓存,显存等存储器细节
cuda编程CUDA的运行方式以及gridblock结构关系
cuda编程CUDA的运行方式以及gridblock结构关系
GPU结构与CUDA系列1GPU与CPU比较:GPU介绍设计差异计算流程