CUDA程序开发——软硬件层级的调度解读

CUDA程序开发——软硬件层级的调度解读

通过阅读文本你将了解到以下内容:

  • CUDA硬件层级
  • CUDA软件层级
  • 软件层级在硬件层级上的具体调度

关键字:SP、SM、Device、Thread、Block、Grid、Warp

想要编写出高性能的CUDA核函数,研究CUDA的软硬件架构是必不可少的。比如在给每一个核函数配置<<<>>>内的启动参数时,你可能在想到底应该分配多少个block,每个block中应该分配多少个thread,这些参数是试出来的,还是有一定的规律可循。如果你想把这个问题搞清楚,不如先从CUDA的软硬件层级开始了解。

1. 软硬件层级

从硬件方面看,CUDA将GPU的处理单元被分为3个层次:流处理器(Streaming Process,SP)、流多处理器(Streaming Multiprocessor,SM)和设备(Device)。

  • SP是最基本的处理单元,也称为CUDA Core,可以将其理解成CPU的一个核,CUDA的kernel函数运行的具体指令就是在SP上处理的。
  • SM由多个SP和一些其他资源组成,不同的架构包含的SP的数量不同,Kepler架构一个SM包含192个SP(这里可以理解成一个SM有192个核)。SM通过线程调度器为SP分发线程,实际可执行的线程要比SP个数多很多。
  • Device由多个SM和一些其他资源组成,不同的设备包含的SM数量不同,以Tesla K80为例,一个GPU包含13个SM。设备也是通过调度器为SM分发线程块的。

而CUDA对Kernel函数中的线程管理也分为3个层次:线程(Thread)、线程块(Block)和线程格(Grid)。

  • Thread是处理数据的一个线程。
  • Block由多个Thread组成,Block内的Thread可以通过共享内存通信,也可以通过函数同步。
  • Grid由多个Block组成。
CUDA线程与处理单元的对应关系

实际上,将线程分层不仅是为了便于将线程与数据块相对应,也是为了将软件执行与硬件相对应。CUDA将Grid分配到Device上,通过GigaThread engine将Block分配到SM上,再通过Warp Scheduler将Thread分配到SP上。也许你会问它们到底是怎么调度的,其中的细节是怎样的?

2. 具体调度

2.1 Grid(Kernel)调度

一个kernel核函数在启动之后,只能在一个GPU设备上运行,不可能运行在多个GPU上。但是,一个GPU是可以同时运行多个kernel的,这与多个工作队列有关,是为了最大化GPU的利用率。

2.2 Block调度

kernel核函数中的一个block会被调度到一个SM上,注意一个block也不能在多个SM上运行。但是,一个SM能够同时运行多个block,计算能力2.0的SM可以最多运行8个block,计算能力3.0的SM可以最多运行16个block。在程序运行时,一个SM具体能够运行几个block,这与每个block占用的资源数量和SM内拥有的总资源数量有关系。同时block在分配给SM时,是考虑到SM的负载均衡的,举个例子。有19个block,4个SM,分配的情况很可能是这样的:先给每个SM分配4个block,还剩余3个block,然后给其中的3个SM分别分配1个block,而不是给其中的1个SM分配3个block。这种负载均衡是有利于缩短程序的运行时间。

2.3 Thread(Warp)调度

最后,是block中thread的调度情况,这个要复杂一点。CUDA出于程序执行效率和执行灵活性的考虑,将thread的调度粒度设置为32个线程,也就是常说的1个线程束(Warp,warp在英文中有纺织机上经线的意思,多个线组成一排,一起织布)。所以,一个block被分配到一个SM上之后,会被SM按32为一组分成多个warp,这些warp会被随机调度,且会被多次调度。

在具体介绍warp的调度情况之前,我们先了解一下warp的三种状态:

  • 执行态:一个warp占有计算核心,正在被执行。
  • 就绪态:一个warp做好被立即执行的准备,等待调度器调度。
  • 阻塞态:由于执行延迟,一个warp等待资源,此时没有做好被立即执行的准备。

一个warp能从就绪态转为执行态,需要两个条件:一个是必须有32个SP可以被利用,另一个是当前指令需要的资源都已准备好。所有处于这三种状态的warp我们称其为active warp,而那些还有被分配SM的block里的warp则是非active warp。

下面具体说明一下warp的调度情况,如下图,CUDA会随机挑选一个warp,并由Warp Scheduler将其调度给32个sp单元,之后由Instruction Dispatch Unit将Kernel中的一条指令发送给32个sp执行,在执行过程中很可能会出现阻塞情况,比如访问显存时需要等待,等待过程中这个被执行的warp会从执行态进入阻塞态,此时Warp Scheduler又会随机调度一个处于就绪态的warp,然后再由Instruction Dispatch Unit分发一条指令,以此类推,多个warp就是这样被随机且循环调度的。
(注:这里提到的32个SP,是对于计算能力较高的设备而言的,而对于计算能力比较低的设备,是16个sp单元,即将warp分成两份)

这里,有人可能会问,warp切换的开销大不大。因为我们都知道,CPU上线程的切换是需要为当前线程保存上下文信息的(寄存器和程序计数器等),这个保存和恢复上下文的过程,需要几十纳秒到几微秒的时间。那么GPU上涉及到大量的warp切换,会不会很耗时。实际上,SM在进行warp切换时开销很小,可以忽略不计,因为相比于CPU,GPU为所有的thead单独分配了足够的寄存器,每一个thread的状态信息在SM上总是单独存好的,所以切换的时候不需要额外的保存和恢复。

关于warp还有一个问题 ,就是一个SM最多可以执行多少个warp。对于计算能力3.0以上的设备,理论上1个SM最多可以同时运行2048个thread,那么,1个SM最多就可以同时运行2048/32=64个warp。注意这里的同时运行是一个逻辑值,而不是物理值。逻辑值64个warp是指同一时间最多可以同时调度64个active warp;而物理值对应于1个SM中的Warp Scheduler的个数和SP的个数,以Kepler为例,一个SM中有4个Warp Scheduler和192个SP,则物理上最多可以同时运行4个执行态的warp。

同时,细心的你可能已经发现,最大值64个warp是与一个SM可以同时调度多个block相佐证的。因为一个block最多运行1024个线程,即32个warp,这只是最大值的一半,所以从侧面证明了SM可以调度多个block。

3. 总结

总结一下,CUDA将硬件分为三个层次:Device、SM、SP,将软件分为三个层次:Grid、Block、Thread。一个Grid只能运行在一个Device上,而一个Device可以运行多个Grid;一个Block只能运行在一个SM上,而一个SM也可以同时运行多个Block;CUDA的线程调度粒度是32个Thead,即1个Warp,Warp在SP上是随机且多次被调度执行的,Warp的切换开销可以忽略不计。

 

本文为博主原创文章,转载请注明链接 https://blog.csdn.net/luroujuan/article/details/88088834

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章