找回密码
 立即注册
查看: 355|回复: 19

GPU架构 核心问题笔记

[复制链接]
发表于 2023-2-12 06:50 | 显示全部楼层 |阅读模式
近日,笔者阅读学习了这篇详细介绍GPU架构的文章,实在受益匪浅。
而文章的作者在开头提出了13个问题,笔者为了不让自己在学习后过快地遗忘,决定还是写一篇笔记记录所学,以供日后复习之用,顺便也向各位安利这篇好文。
*笔记中大部分的描述都取自原文章,以保证正确性。如果有部分内容存在理解错误,还请各位大佬指出。
*本笔记原发布于CSDN,转到知乎后可能存在双水印问题,还请各位多多包涵。
1、GPU是如何与CPU协调工作的?

CPU与GPU的交流通过MMIO(Memory Mapped IO)进行。CPU 通过 MMIO 访问 GPU 的寄存器状态。 任何命令都是由CPU发出。命令流(command stream)被提交到硬件单元,也就是GPU Channel。





CPU-GPU数据流

<hr/>2、GPU也有缓存机制吗?有几层?它们的速度差异多少?

有,某些GPU最多包含5层结构,分别为寄存器、L1缓存、L2缓存、GPU显存、系统显存。





以2010年推出的NVidia Fermi架构为例(L2缓存在左下图中)



<hr/>3、GPU的渲染流程有哪些阶段?它们的功能分别是什么?




顶点着色器:

  • 将顶点从模型空间转换到世界空间
  • 从世界空间转换到摄像机空间
  • 确定光线对材质的影响(Shading) *此时将信息储存,用于之后的逐片元计算
  • 进行投影(正交/透视),转换至裁剪坐标系/标准化设备坐标系(normalized device coordinates,即NDC)(所有坐标在-1到1之间)
曲面细分: 根据摄像机的距离,调整片面的数量。
几何着色器: 对输入的几何体进行一系列操作,使其变成另一种几何体,如将粒子转化为一个正方形面片(由两个三角形面片组成),以供之后渲染。
光栅化: 在此阶段,将计算三角形的微分、边方程和其他数据。这些数据可用于三角形遍历,以及几何阶段生成的各种阴影数据的插值
Early-Z: 在像素着色器之前进行深度测试,具体下一问会谈到。
像素着色器: 以从光栅化阶段所传来的顶点各值的插值进行各种计算、纹理采样
透明度测试: 即Alpha Test,若某片元的Alpha值不满足某条件,则直接将其舍弃。
模板测试: 即Stencil Testing
深度测试: 将被其他片元挡住的片元舍弃。
<hr/>4、Early-Z技术是什么?发生在哪个阶段?这个阶段还会发生什么?会产生什么问题?如何解决?

Early-Z技术:将深度测试提至像素着色器之前 Early-Z技术可以将很多无效的像素提前剔除,避免它们进入耗时严重的像素着色器。Early-Z剔除的最小单位不是1像素,而是像素块(2x2)
但是,以下情况会导致Early-Z失效:

  • 开启Alpha Test:由于Alpha Test需要在像素着色器后面的Alpha Test阶段比较,所以无法在像素着色器之前就决定该像素是否被剔除。
  • 开启Tex Kill:即在shader代码中有像素摒弃指令(DX的discard,OpenGL的clip)。
  • 在pixel shader中修改深度值。
  • 关闭深度测试。Early-Z是建立在深度测试看开启的条件下,如果关闭了深度测试,也就无法启用Early-Z技术。
///////////
感谢@GuardHei 指出原文章中的“开启Multi-Sampling会导致Early-Z失效”这一结论错误,已修改
///////////
Early-Z也会导致一个问题——深度数据冲突(depth data hazard)
假设数值深度值5已经经过Early-Z即将写入Frame Buffer,而深度值10刚好处于Early-Z阶段,读取并对比当前缓存的深度值15,结果就是10通过了Early-Z测试,会覆盖掉比自己小的深度值5
避免深度数据冲突的方法之一是在写入深度值之前,再次与frame buffer的值进行对比
<hr/>5、SIMD和SIMT是什么?它们的好处是什么?co-issue呢?

SIMD:Single Instruction Multiple Data,单指令多数据。



SIMT:Single Instruction Multiple Threads,单指令多线程。是SIMD的升级版。可对GPU中单个SM中的多个Core同时处理同一指令,并且每个Core存取的数据可以是不同的。


co-issue是为了解决SIMD运算单元无法充分利用的问题。



<hr/>6、GPU是并行处理的么?若是,硬件层是如何设计和实现的?

当然是,而且是高度并行的。 仍旧以Fermi架构为例, 它拥有拥有16个SM,每个SM拥有2个Warp(线程束),每个Warp拥有16个Core,每个Core又有1个FPU(浮点数单元)以及1个ALU(逻辑运算单元)……
在获取数据之后,在SM中以32个线程为一组的线程束(Warp)来调度,来开始处理顶点数据。Warp是典型的单指令多线程(SIMT,SIMD单指令多数据的升级)的实现,也就是32个线程同时执行的指令是一模一样的,只是线程数据不一样,这样的好处就是一个Warp只需要一个套逻辑对指令进行解码和执行就可以了。也就实现了各个Warp之间的并行
<hr/>7、GPC、TPC、SM是什么?Warp又是什么?它们和Core、Thread之间的关系如何?

GPC:图形处理簇,内含数个TPC
TPC:纹理处理簇,内含数个SM
SM:Stream Multiprocessor,流多处理器,内含数个Warp
Warp:线程束,内含数十个Core,而每个Core可以作为线程(Thread)的执行者。



Turing架构的GPU

<hr/>8、顶点着色器(VS)和像素着色器(PS)可以是同一处理单元吗?为什么?

可以,因为如今的GPU已经引入了统一着色器架构(Unified shader Architecture)。用了此架构的GPU,VS和PS用的都是相同的Core。也就是说,同一个Core既可以是VS又可以是PS。
<hr/>9、像素着色器(PS)的最小处理单位是1像素吗?为什么?会带来什么影响?

不是, 像素着色器中,会将相邻的四个像素作为不可分隔的一组,送入同一个SM内4个不同的Core
原作者推测有以下原因: 1、简化和加速像素分派的工作。 2、精简SM的架构,减少硬件单元数量和尺寸。 3、降低功耗,提高效能比。 4、无效像素虽然不会被存储结果,但可辅助有效像素求导函数。
同时,也会激化过绘制(Over Draw)的情况,损耗额外的性能


原本只需绘制三个像素,使用3个Core,但实际上这个三角形涉及到了3个像素块,因此需要使用12个Core来绘制,造成性能浪费



<hr/>10、Shader中的if、for等语句会降低渲染效率吗?为什么?

绝大部分情况下会降低,除非一个Warp中的所有线程都走进了同一分支或循环次数相等。



SM的warp调度器会按照顺序分发指令给整个warp,单个warp中的线程会锁步(lock-step)执行各自的指令,如果线程碰到不激活执行的情况也会被遮掩(be masked out)。 被遮掩的原因有很多,例如当前的指令是if(true)的分支,但是当前线程的数据的条件是false,或者循环的次数不一样(比如for循环次数n不是常量,或被break提前终止了但是别的还在走),因此在shader中的分支会显著增加时间消耗,在一个warp中的分支除非32个线程都走到if或者else里面,否则在进行其中一个分支时,走入另一个分支的wrap会阻塞而不进行任何操作,在时间开销上相当于所有的分支都走了一遍,线程不能独立执行指令而是以warp为单位,而这些warp之间才是独立的。
感谢@烟雨迷离半世殇 指出这一段中加粗字体的措辞易产生歧义,已修改
<hr/>11、如下图,渲染相同面积的图形,三角形数量少(左)的还是数量多(右)的效率更快?为什么?




左边效率高,因为若同一个像素块如果分属不同的三角形,就会分配到不同的SM进行处理。由此推断,相同面积的区域,如果所属的三角形越多,就会导致分配给SM的次数越多,消耗的渲染性能也越多
<hr/>12、GPU Context是什么?有什么作用?

GPU Context:  - GPU Context代表了GPU计算的状态。  - 在GPU中拥有自己的虚拟地址。  - GPU 中可以并存多个活跃态下的Context。
由于SIMT技术的引入,导致很多同一个SM内的很多Core并不是独立的,当它们当中有部分Core需要访问到纹理、常量缓存和全局内存时,就会导致非常大的卡顿(Stall)。
例如下图中,有4组上下文(Context),它们共用同一组逻辑运算单元ALU。


延迟的后果是每组Context的总体执行时间被拉长了,但是,越多Context可用就越可以提升运算单元的吞吐量
<hr/>13、造成渲染瓶颈的问题很可能有哪些?该如何避免或优化它们?

CPU和GPU的数据交换次数过多:



过绘制(Overdraw):



分支或循环、计算优化:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2023-2-12 06:57 | 显示全部楼层
好文章
发表于 2023-2-12 07:04 | 显示全部楼层
sm的warp那一块并不是所有分支都走一遍,这部分描述和rtr4里一致,都是错误的,应该是非此分支数据会导致当前处理单元处于阻塞状态
https://www.sciencedirect.com/topics/computer-science/thread-divergence
https://blog.csdn.net/junparadox/article/details/50541825
https://cvw.cac.cornell.edu/gpu/thread_div
发表于 2023-2-12 07:09 | 显示全部楼层
我觉得文章的“相当于所有的分支都走了一遍”意思是说不同分支的处理单元阻塞,在时间开销上相当于分支都走了一次吧,应该跟你的观点不冲突才对
发表于 2023-2-12 07:13 | 显示全部楼层
如果你理解的是“相当于所有的分支都走了一遍”的意思是没有执行分支的话,那确实是和我观点不冲突
发表于 2023-2-12 07:20 | 显示全部楼层
事实上“相当于所有的分支都走了一遍”这种说法,很容易被误解为计算机操作系统中数据的条件转移操作
发表于 2023-2-12 07:29 | 显示全部楼层
msaa导致earlyz失效在现在的gpu上还有吗?那depth prepass fwd岂不是也不能用msaa了?不对吧?
发表于 2023-2-12 07:35 | 显示全部楼层
我查了下,应该是除非改了SV_Coverage,否则只是开了msaa应该不会导致earlyz失效,subsample也是会earlyz的
发表于 2023-2-12 07:42 | 显示全部楼层
你是对的,我也找不到其他能支持“MSAA会导致early-z失效”这一结论的资料。大半年前啥都不懂时做的笔记现在回头看确实能发现一些问题,还有文中提到的Alpha Blend也是一大迷惑点。
发表于 2023-2-12 07:42 | 显示全部楼层
您好,请问 12 条 GPU Context 那里的图出处是哪里呢? 我理解的这个图应该指的是 SM 内部多个 Warp 执行的方式,而不是 GPU Context 的并发执行模式。不同 GPU Context 目前应该只能 time-slice sharing 吧?
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 07:33 , Processed in 0.072230 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表