GPU笔记

GPU笔记

​ GPU全称是Graphics Processing Unit,图形处理单元,起初主要用来绘制图像和处理图元数据,后来逐渐加入了很多其他的功能。GPU是显卡的最主要的部分,显卡除了GPU还有散热器、通讯元件、各类插槽等其他部件。GPU的主要功能有:图形绘制、物理模拟、海量计算、AI运算、其他计算等。显卡不能独立工作,需要装载在主板上,结合CPU、内存、显存、显示器等硬件,组成完整的PC机。

GPU结构

NVidia Teala架构

1617944-20190906000754905-1644664290

Tesla的微观总览图如上:

  • 拥有7组TPC(Texture Processor Cluster,纹理处理簇)
  • 每个TPC拥有两组SM(Stream Multiprocessor,流多处理器)
  • 每个SM包括:
    • 6个SP(Streaming Processor,流处理器)
    • 2个SFU(Special Function Unit,特殊函数单元)
    • L1缓存、MT Issue(多线程指令获取)、C-Cache(常量缓存)、共享内存
  • 除了TPC核心单元,还有与显存、CPU、系统内存交互的各种部件。

NVidia Fermi架构

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(逻辑运算单元)

NVidia Maxwell架构

Maxwell

采用了Maxwell的GM204,拥有4个GPC,每个GPC有4个SM,对比Tesla架构来说,在处理单元上有了很大的提升。

NVidia Kepler架构

NVidia_Kepler

Kepler除了在硬件有了提升,有了更多处理单元之外,还将SM升级到了SMX。SMX是改进的架构,支持动态创建渲染线程(下图),以降低延迟。

SMX

NVidia Turing架构

Nvidia_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位)

单个SM的结构图如下:

Turing_SM

每个SM包含:

  • 64 CUDA核
  • 8 Tensor核
  • 256 KB寄存器文件

GPU运行机制

下图是Fermi架构总览:

F

​ 从Fermi开始NVIDIA使用类似的原理架构,使用一个Giga Thread Engine来管理所有正在运行的工作,GPU被划分为多个GPCs(Graphics Processing Cluster),每个GPC拥有多个SM(SMX、SMM)和一个光栅化引擎(Raster Engine),它们其中有很多的连接,最显著的是CrossBar,它可以连接GPCs和其他功能性模块(例如ROP或其他子系统)。

​ 程序员编写的shader(着色器)是在SM上完成的。每个SM包含许多为线程执行数学运算的Core(核心)。例如,一个线程可以是顶点或像素着色器调用。这些Core和其他单元由Warp Scheduler驱动,Warp Scheduler管理一组32个线程作为Warp(线程束),并将要执行的指令移交给Dispatch Units。

SM_exp

如上图,对于某些GPU(如Fermi部分型号)的单个SM,包含:

  • 32个运算核心 (Core,也叫流处理器Stream Processor)
  • 16个LD/ST(load/store)模块来加载和存储数据
  • 4个SFU(Special function units)执行特殊数学运算(sin、cos、log等)
  • 128KB寄存器(Register File)
  • 64KB L1缓存
  • 全局内存缓存(Uniform Cache)
  • 纹理读取单元
  • 纹理缓存(Texture Cache)
  • PolyMorph Engine:多边形引擎负责属性装配(attribute Setup)、顶点拉取(VertexFetch)、曲面细分、栅格化(这个模块可以理解专门处理顶点相关的东西)。
  • 2个Warp Schedulers:这个模块负责warp调度,一个warp由32个线程组成,warp调度器的指令通过Dispatch Units送到Core执行。
  • 指令缓存(Instruction Cache)
  • 内部链接网络(Interconnect Network)

GPU逻辑管线

下面以Fermi家族的SM为例,进行逻辑管线的详细说明:

FermiAPI

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

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

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

    fermi_core

  4. 在GPC中,每个SM中的Poly Morph Engine负责通过三角形索引(triangle indexes)取出三角形的数据(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,或者循环的次数不一样(比如for循环次数n不是常量,或者被break了,但是别的还在走),因此在shader(着色器)中的分支会显著增加时间消耗,在一个warp中的分支除非32个线程都走到if或者else里面,否则相当于所有的分支都走了一遍。线程不能独立执行指令,而是以这些warp为单位,这些warp之间才是独立的。

  7. warp中的指令可以被一次执行,也可以经过多次调度执行。例如通常SM中的LD/ST(读写)单元数量明显少于基础数学操作单元。

  8. 由于某些指令比其他指令需要更长的时间才能完成,特别是内存加载,warp调度器可能会简单地切换到另一个没有内存等待的warp。为了切换更快,调度器管理的所有warp在寄存器文件中都有自己的寄存器。

    画图处理流程如下:

    draw_processor

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

    triangle

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

    attributes

  11. SM上的Atrribute Setup保证了vertex-shader来的数据经过插值之后是pixel-shader可读的。

  12. GPC上的光栅引擎(raster engines)在它接收到的三角形上工作,负责这些三角形的像素信息的生成(同时会裁剪、背面剔除和Early-Z剔除)。

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

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

    ROP

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