前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >GPU数据并行结构

GPU数据并行结构

原创
作者头像
Zero Two
修改2024-06-25 14:18:21
800
修改2024-06-25 14:18:21
举报
文章被收录于专栏:Real-Time RenderingReal-Time Rendering

引用:《Real-Time Rendering》4th 第三章 第一节

知识的学习不应该只是将内容看一遍就复制到自己的笔记里,应该加上自己的思考与理解

处理器在处理数据的过程中,有时会需要访问其他数据,访问这些数据需要花费一定的时间,此时处理器会处于停滞状态等待数据的返回。而等待的这段时间称之为延迟。

不同的处理器有不同的策略来应对这种情况。CPU芯片中大部分面积都是高度的本地缓存,并且还会使用很多策略来避免停滞,如分⽀预测(branch predication)、指令重排序(instruction reordering)、寄存器重命名(register renaming)和缓存预取(prefetching)等。

GPU则不同,为了保证并行数据计算性能,GPU芯片中很大一部分面积是大量的处理器,也叫做着色器核心(shader core),这是用于执行某些相对独立任务的小型处理器。

GPU是一个流处理器,它会依次处理有序的相似数据。由于这些数据的相似性(例如一组顶点或者像素),GPU可以进行大规模的并行处理。能够并行处理的另外一个因素是,着色器调用是相对独立的,他们不需要邻近调用的信息,也不需要共享可写的内存位置。(有时为了新功能,这种规则是可打破的,但会导致处理速度的降低,比如可能处理器间相互限制,一个处理器要等待另一个处理器执行结束后才能开始执行)

因为GPU芯片的很大一部分面积是处理器,所以它的吞吐量(throughput, 数据能够被处理的单位速度)很大;但这也导致了用于缓存和处理逻辑的芯片面积比较少,因此每个着色器核心的延迟通常比CPU处理器的延迟更大。

假设现在有一个着色处理器(shader processor)去处理2000个片元数据。因为只有一个处理器,只能够顺序执行,先为第一个片元执行程序。如果程序要对寄存器中的值进行操作,因为寄存器是本地的,访问速度很快,所以处理器不会停滞。但如果要访问一个纹理数据,它不是本地内存的一部分,则需要去外部读取数据,这会花费一定时间,处理器会停滞并等待这个数据的返回。

如果我们为每个片元的本地寄存器都提供了一点存储空间,用于保存程序运行状态,当第一个片元着色器程序停滞时,可以切换到另一个片元着色器程序,这个切换过程很快,因为基本不需要切换指令,只需要切换顶点数据等。同样地,第二个着色器也会停滞,然后继续切换;2000个片元都会以这种形式依次执行完后纹理数据已经返回,着色器程序可以继续执行。这种执行方式下,单个片元的处理时间会增加,但是整体的时间将会大大减少。

这种设计中,当着色处理器停滞时,会切换执行其他片元的程序,来让GPU时刻忙碌,避免延迟。更进一步来说,GPU可以将指令的逻辑与数据分开,这种设计叫做单指令、多数据(single instruction,multiple data,SIMD)。GPU的设计就是为了并行处理大量的相似任务,因此,GPU采用了SIMD架构来最大化并行度。这个架构的特点是:有一个指令控制单元,负责发出指令。有多个执行单元,这些执行单元可以同时执行相同的指令集。每个执行单元处理不同的数据(如不同的片元数据)。用图形渲染任务来举例就很好理解了,对于要渲染的n个片元,它们的着色器代码都是相同的,所以只需要一个指令控制单元解析代码并发出执行指令;n个片元的数据都是相似的,交给n个着色处理器后,这n个着色处理器同时执行指令控制单元发出的指令。那么这种架构的优势就很明显了,可以使用更小的硅芯片(也就意味着更小的功耗)来处理数据(比如解析代码等)和进行切换(因为都是并行运算)。

用现代GPU的术语来说,每个片元的像素着色器调用都可以被称为一个线程,但不同于CPU的线程,它包括用于存储着色器输入数据的存储空间,以及用于着色器执行的任何寄存器空间。这些使用相同着色器程序的线程会被打包成组,NVIDIA将其称为一个wart,AMD将其成为一个wavefront。一个wart/wavefront(相当于一个指令控制单元)负责调度一定数量的处理器,可能是8到64个,并且都是用SIMD架构。每个线程都会被映射到一个SIMD通道(lane)。

假设有2000个线程要执行,NVIDIA规定一个warp含有32个线程,那么需要分配2000 / 32 = 62.5,也就是63个warp来执行这些线程。因为一个warp内的着色器程序相同,它们都会执行一样的指令,那么一个warp的执行可以抽象地看作单个的GPU处理器的执行过程。当需要存储读取时,必然是全部的线程都遇到了,此时这个warp会切换到另一个wrap,但每个线程的数据不会被修改,warp也会记录下线程正在执行的指令。这个交换只是将一组处理器核心指向了另一组线程。

还有一些其他的技术可以用来优化执行效率,但是warp 交换是GPU 上最重要的延迟隐藏技术。这个过程的工作效率还涉及到好几个其他因素,例如:如果线程的数量很少,那么就只能创建很少的warp,可能无法通过warp交换来缓解处理器核心的停滞,这就没法有效隐藏延迟。

影响执行效率的另一个重要特征是着色器程序的结构,其中最重要的一个因素就是每个线程所使用的寄存器数量。每个线程中运行的着色器程序所需要使用的寄存器数量越多,那么GPU 上能够同时存在的线程数量和warp 数量也就越少。

另一个影响整体运行效率的因素是由“if”语句和循环语句导致的动态分支(dynamic branching)。假设现在着色器程序中遇到了一个“if”语句,如果所有线程都进入了相同的分支,那么这个warp可以不用管其他的分支,继续执行进入的那个分支即可。但是,如果其中有几个线程,甚至是只有一个线程进入了其他的分支,那么这个warp就必须把两个分支都执行一遍,然后再根据每个线程的具体情况,丢弃不需要的结果。这个问题叫做线程发散(thread divergence),它意味着有一些线程需要去执行一个循环操作,或者是进入了所在warp 中其他线程都没有进入的“if”分支,这会导致其他的线程空转。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档