cpu并行和gpu并行_GPU并行架构及渲染优化

最近在看关于GPU架构和渲染优化方面的内容,记录一下,不正确的地方请大神们斧正!本文将主要分为以下四个部分:

  1. CPU与GPU
  2. GPU并行结构
  3. GPU渲染管线
  4. GPU渲染优化

一、CPU与GPU

1.CPU与GPU结构对比

下图仅仅是CPU和GPU架构的简化版,真实的架构非常复杂,而且每一步操作都需要多个组件协同进行。

e0a9f5819b69f0e396d0e947bd8d176c.png
CPU与GPU结构对比图
  • 黄色的Control为控制器,用于协调控制整个CPU(GPU)的运行,包括取出指令、控制其他模块的运行等;绿色的ALU(Arithmetic Logic Unit)是算术逻辑单元,用于进行数学、逻辑运算;橙色的Cache和DRAM用于存储信息。
  • CPU的流水线较长,控制器较为复杂,计算趋于线性执行(可部分并行),善于处理复杂的逻辑运算。CPU内部ALU数量较少,所以不适合做数值计算密集型的数学计算。
  • GPU的流水线很短,控制器较为简单,内部集成了大量ALU。GPU中的ALU可以并行执行,且具有较多浮点运算单元,所以适合进行大量可并行执行的数学计算。我们可以把一些比较独立并且可并行执行的数学计算抛给GPU执行(例如,图像处理、视频编解码、物理粒子计算等)。

2.CPU与GPU数据交互

在程序开始运行之前所有的数据都是保存在磁盘上的。程序开始运行,为了CPU能够快速访问数据,需要先把数据载入内存(RAM)。所有的渲染工作都集中在GPU执行,GPU和CPU一样,也有自己的可以快速访问的类内存结构,叫做显存(VRAM)。在GPU开始工作之前,所有计算所需的资源都必须载入显存:

d6dc83a8d36a152ef41d8cf75814b686.gif
数据由CPU到GPU

GPU进行计算的时候需要从缓存拿取数据

904efef79ac5abfeb51012cffccb3dd8.gif
GPU获取显存数据

当然,与CPU一样,GPU在进行计算获取数据的时候,并不是直接从显存中获取数据,而是经过多级缓存后,然后经过对应的寄存器,最后进入ALU中进行计算,如下图:

6494d3728f606eeaf7c77d4e18fd5ba0.gif
数据由L2级缓存到L1l级缓存

49aa76dc7c2ce89cdf26b29556106d26.gif
数据由L1级缓存到寄存器

同理当GPU完成对数据的计算之后,会经过寄存器—L1级缓存—L2级缓存然后保存在显存中,最终显示器获取显存中的数据进行显示输出。

3.CPU与GPU内存速度

  • CPU内存速度

fd876404959bc1749042236a9b4cf37d.png
CPU各级内存访问时钟数
  • GPU内存速度

65d830a391fc0769a01177faadc494ea.png
GPU各级内存访问时钟数

从上面可以看出CPU和GPU在寄存器—缓存—内存(RAM)访问时间依次增加,对RAM的访问时比较耗时的,而CPU和GPU数据交互时会涉及两者内存(RAM)的访问因此:

(1) 提交渲染数据时,尽量批量提交,减少CPU向GPU提交数据次数。

(2) 尽量避免从GPU回读数据,这样可以减少两者数据交互。

二、GPU并行结构

1.Nvdia GPU架构发展

  • 2010 Fermi:Fermi是第一个完整的GPU计算架构。首款可支持与共享存储结合纯cache层次的GPU架构,支持ECC的GPU架构。
  • 2012 Kepler:Kepler相较于Fermi更快,效率更高,性能更好。

21cab95dac0b1d213dd93c320c3a200d.png
Kepler与Fermi对比
  • 2014 Maxwell:其全新的立体像素全局光照 (VXGI) 技术首次让游戏 GPU 能够提供实时的动态全局光照效果。基于 Maxwell 架构的 GTX 980 和 970 GPU 采用了包括多帧采样抗锯齿 (MFAA)、动态超级分辨率 (DSR)、VR Direct 以及超节能设计在内的一系列新技术。
  • 2016 Pascal:Pascal 架构将处理器和数据集成在同一个程序包内,以实现更高的计算效率。1080系列、1060系列基于Pascal架构

8e8969842dbc8ec8a703f259ad58dc94.png
Kepler/Maxwell/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架构。

2.GPU架构分解

GPU物体架构

881cc52a8ba7515c82db75b0240d236f.png
NV GPU架构
  • Giga Thread Engine来管理所有正在进行的工作
  • GPU被划分成多个GPCs(Graphics Processing Cluster)
  • 每个GPC拥有多个SMM(Nvdia后来把SM改为SMM)和一个光栅化引擎(Raster Engine)

11df8123c2033cdbe3f88576bdb65e95.png
SMM架构
  • 着色器程序的执行都是在SM上完成的
  • sm包含32个运算核心 ,16个LD/ST(load/store)模块来加载和存储数据,4个SFU(Special function units)执行特殊数学运算(sin、cos、log等),128KB寄存器,64KB L1缓存,全局内存缓存,Tex纹理读取单元,TextureCache纹理缓存。
  • polyMorph Engine多边形引擎负责属性装配(attribute Setup)、顶点拉取(VertexFetch)、曲面细分、栅格化(这个模块可以理解专门处理顶点相关的东西)
  • Warp Schedulers这个模块负责warp调度,一个warp由32个线程组成,warp调度器的指令通过Dispatch Units送到Core执行。

三、GPU渲染管线

afcfcb51275f0459c07b584de0c576fc.png

1.程序通过图形API(DXGLWEBGL)发出drawcall指令,指令会被推送到驱动程序,驱动会检查指令的合法性,然后会把指令放到GPU可以读取的Pushbuffer中。

2.经过一段时间或者显式调用flush指令后,驱动程序把Pushbuffer的内容发送给GPU,GPU通过主机接口(Host Interface)接受这些命令,并通过Front End处理这些命令。

3.在图元分配器(Primitive Distributor)中开始工作分配,处理indexbuffer中的顶点产生三角形分成批次(batches),然后发送给多个PGCs,这一步的理解就是提交上来n个三角形,分配个这几个PGC同时来处理。

11df8123c2033cdbe3f88576bdb65e95.png
SMM架构

4.在GPC中,每个SM中的Poly Morph Engine负责通过三角形索引(triangle indices)取出三角形的数据(vertex data)[图中的Vertex Fetch模块]。

5.在获取数据之后,在sm中以32个线程为一组的线程束(warp)来调度,来开始处理顶点数据。warp是典型的单指令多线程(SIMT,SIMD单指令多数据的升级)的实现,也就是32个线程同时执行的指令是一模一样的,只是线程数据不一样,这样的好处就是一个warp只需要一个套逻辑对指令进行解码和执行就可以了,芯片可以做的更小更快,只所以可以这么做是由于GPU需要处理的任务是天然并行的。

6.SM的warp调度器会按照顺序分发指令给整个warp,单个warp中的线程会锁步(lock-step)执行各自的指令,如果线程碰到不激活执行的情况也会被遮掩(be masked out),被遮掩的原因有很多,例如当前的指令是if(true)的分支,但是当前线程的数据的条件是false,或者比如一个循环被终止了但是别的还在走,因此在shader中的分支会显著增加时间消耗,在一个warp中的分支除非32个线程都走到if或者else里面,否则相当于所有的分支都走了一遍,线程不能独立执行指令而是以warp为单位,而这些warp相互之间是独立的。

7.warp中的指令可以被一次完成,也可能经过多次调度,例如sm中的加载纹理、数据存取明显少于数学运算。

8.由于某些指令比其他指令需要更长的时间才能完成,特别是内存加载,warp调度器可能会简单地切换到另一个没有内存等待的warp,这是gpu如何克服内存读取延迟的关键,只是简单地切换活动线程组。为了使这种切换非常快,调度器管理的所有warp在寄存器文件中都有自己的寄存器。这里就会有个矛盾产生,shader需要越多的寄存器,就会给warp留下越少的空间,就会产生越少的warp,这时候在碰到内存延迟的时候就会只是等待,而没有可以运行的warp可以切换。

9.一旦warp完成了vertex-shader的所有指令,运算结果会被Viewport Transform模块处理,三角形会被裁剪然后准备栅格化,GPU会使用L1和L2缓存来进行vertex-shader和pixel-shader的数据通信

10.接下来这些三角形将被分割,再分配给多个GPC,三角形的范围决定着它将被分配到哪个光栅引擎(raster engines),每个raster engines覆盖了多个屏幕上的tile,这等于把三角形的渲染分配到多个tile上面。也就是像素阶段就把按三角形划分变成了按显示的像素划分了。

11.sm上的Attribute Setup保证了从vertex-shader来的数据经过插值后是pixel-shade是可读的。

12.GPC上的光栅引擎(raster engines)在它接收到的三角形上工作,来负责这些这些三角形的像素信息的生成。

13.32个像素线程将被分成一组,或者说8个2X2的像素块,这是在像素着色器上面的最小工作单元,在这个像素线程内,如果没有被三角形覆盖就会被遮掩,sm中的warp调度器会管理像素着色器的任务。

14.接下来的阶段就和vertex-shader中的逻辑步骤完全一样,但是变成了在像素着色器线程中执行。 由于不耗费任何性能可以获取一个像素内的值,导致锁步执行(SIMD)非常便利,所有的线程可以保证所有的指令可以在同一点。

15.最后一步,现在像素着色器已经完成了颜色的计算还有深度值的计算,在这个点上,我们必须考虑三角形的原始api顺序,然后才将数据移交给渲染输入单元ROP(render output unit),一个ROP内部有很多ROP单元,在ROP单元中处理深度测试,和framebuffer的混合,深度和颜色的设置必须是原子操作,否则的话两个不同的三角形在同一个像素点就会有冲突和错误。

四、GPU渲染优化

  • 减少CPU/GPU 数据交互:数据批量提交,避免GPU数据回读。
  • 合并drawcall:因为即使渲染一个三角形,在GPU中也要走系列复杂的流程,这系列流程带来的延迟远超过计算一个三角本身,只有同时并行多处理才能发挥GPU的强大并行能力,这也是我们优化的时候要合并渲染的原因,越合并越能最大限度的利用GPU。
  • 降低渲染面数:面数越少VS计算使用的线程就越少,顶点计算就越快。
  • 避免在shader中使用if else:因为按照SIMD的执行方式,if else可能会完全不生效,导致两个分支都要走一遍。同样循环中的break也会导致这样的问题。
  • 降低采样次数:因为纹理的读取速度实在是太慢了,读取跟不上运算会导致极大的延迟。

参考文献

https://www.nvidia.cn/design-visualization/technologies/turing-architecture/​www.nvidia.cn 图形渲染及优化-渲染管线基础-腾讯游戏学院​gameinstitute.qq.com
e0d410a73f69b28887cbc9989147d379.png
刘小刘:渲染优化-从GPU的结构谈起​zhuanlan.zhihu.com
1aa5f78556e435cdb293ddc41db44674.png
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值