GameRes游戏开发资源网 http://www.gameres.com
GPU为什么跑得快?
计算机3D游戏最基本的一个要求是:能以每秒数十帧的速率,根据当前的三维景物实时生成三维动画。你所看到的图像会随着你视点的变化而即时改变,使人产生“身临其境”的感觉。高的帧率可以得到更流畅的画面。一般来说30fps就是可以接受的,但是将帧速增加至60fps则可以明显提升交互感。这与计算机影视制作不同,电影特效可以通过很多台工作站,花几十个小时绘制出长度仅为几秒的高质量画面,并不强调实时性。
渲染一个复杂的三维场景,需要在一秒内处理几千万个三角形顶点和光栅化几十亿的像素。早期的3D游戏,显卡只是为屏幕上显示像素提供一个缓存,所有的图形处理都是由CPU单独完成。图形渲染适合并行处理,擅长于执行串行工作的CPU实际上难以胜任这项任务。所以,那时在PC上实时生成的三维图像都很粗糙。不过在某种意义上,当时的图形绘制倒是完全可编程的,只是由CPU来担纲此项重任,速度上实在是达不到要求。
直到1995年,PC机领域第一款GPU (Graphical Processing Unit) 3dfx Voodoo出来以后,游戏的速度、画质才取得了一个飞跃。3dfx Voodoo有两个主要的特征:深度缓冲区(z-buffer)和纹理映射(texture mapping)。z-buffer执行“隐藏面消除”这一工作,这样可以避免渲染“不可视”的无效像素。利用纹理映射功能则可以十分逼真地表达物体表面细节。1999年,第二代GPU (Nvidia GeForce256,GeForce 2和ATI Radeon 7500)包括了图形的几何变换与光照计算功能(T&L)。而在此之前T&L都是由CPU完成的,这对CPU来说是很复杂的计算。第二代GPU解决了系统的一个瓶颈,减轻了CPU的负荷,速度明显提高了。但是由于是固定的渲染流水线,缺乏灵活性,束缚了开发人员的创造性。2001年,NIVIDA公司的GeForce 3首先引入了可编程的顶点着色器(Vertex Shader)单元。紧接着在2002年,可编程的像素着色器(Pixel Shader)单元也加入了GPU (见图1)。在绘制时,GPU首先接收CPU以三角形顶点形式的发送的几何数据。然后由可编程的顶点着色器单元进行处理,完成几何变换与顶点属性计算等功能。接着,這些三维空间的三角形由一个固定功能的光栅生成器转换为二维屏幕上的像素。每个像素的最终颜色值都通过运行在像素着色器上的小程序运算而得。目前三维游戏借助于GPU,已经能够实时生成十分细腻、逼真的画面。
<shapetype id="_x0000_t75" path=" m@4@5 l@4@11@9@11@9@5 xe" stroked="f" filled="f" o:spt="75" o:preferrelative="t" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0 "></f><f eqn="sum @0 1 0 "></f><f eqn="sum 0 0 @1 "></f><f eqn="prod @2 1 2 "></f><f eqn="prod @3 21600 pixelWidth "></f><f eqn="prod @3 21600 pixelHeight "></f><f eqn="sum @0 0 1 "></f><f eqn="prod @6 1 2 "></f><f eqn="prod @7 21600 pixelWidth "></f><f eqn="sum @8 21600 0 "></f><f eqn="prod @7 21600 pixelHeight "></f><f eqn="sum @10 21600 0 "></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock v:ext="edit" aspectratio="t"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 417pt; HEIGHT: 261pt" coordsize="21600,21600" type="#_x0000_t75"><imagedata src="gpu.files/image001.jpg" o:title="图1"></imagedata></shape>
<shapetype id="_x0000_t202" path=" m0,0 l0,21600,21600,21600,21600,0 xe" o:spt="202" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><path o:connecttype="rect" gradientshapeok="t"></path></shapetype><shape id="_x0000_s1027" style="MARGIN-TOP: 0px; Z-INDEX: 1; MARGIN-LEFT: 135pt; WIDTH: 135pt; POSITION: absolute; HEIGHT: 23.4pt" stroked="f" filled="f" coordsize="21600,21600" type="#_x0000_t202"></shape>
GPU的功能更新很迅速,平均每一年多便有新一代的GPU诞生,运算速度也越来越快。2004年,2004年推出的GPU Nvidia GeForce 6800 Ultra可达到峰值40 Gigaflops( 1 GigaFLOPS=1秒钟进行10亿次的浮点运算),2005年刚发布的 Nvidia GeForce 7800 GTX更是将峰值提高至令人惊讶的169 Gigaflops。而Intel 3GHz Pentium 4采用SSE指令集也只能达到6 Gigaflops(见图2)。GPU的运算速度如此之快,主要得益于GPU是对图形实时渲染量身定制的,具有两点主要特征:超长流水线与并行计算。
<shape id="_x0000_s1032" style="MARGIN-TOP: 257.4pt; Z-INDEX: 5; LEFT: 0px; MARGIN-LEFT: 117pt; WIDTH: 207pt; POSITION: absolute; HEIGHT: 23.4pt; TEXT-ALIGN: left" stroked="f" coordsize="21600,21600" type="#_x0000_t202"></shape><shape id="_x0000_i1026" style="WIDTH: 414.75pt; HEIGHT: 261pt" coordsize="21600,21600" type="#_x0000_t75"><imagedata src="gpu.files/image003.jpg" o:title="图2"></imagedata></shape>
流水线技术与工厂里的装配线在原理上类似。如果装配一台汽车需要10个时间单元,将它分成10个流水线阶段,每个阶段分配一个时间单元,那么一条装配线每一个时间单元就可以生产一辆汽车。显然流水线模式的生产在理想状况下要比串行方式快了十倍。从这个例子中,可以看出为了提高流水线的速度,可以将任务划分成更小的单元,这样流水线的级数就增加了。CPU的设计中就使用了流水线原理,奔腾IV就有20级的流水线。但是流水线级数越多,一条指令从开始进入流水线到最后被执行完毕这之间的延迟间隔会相当大。换句话说,当流水线级数过多时,控制台发出一条指令,会经过很长时间才会真正生效,这不适用于快速反应要求很高的场合。打个比方,当用消防水龙头救火时,正常情况下打开阀门开关几秒后水便喷射出来,立刻可用来扑灭火灾。但是如果延迟了十几分钟,才有水流出,即使这时水流速度(吞吐量)还是很快,不过这时火灾就会造成更大的损失了。CPU的设计目标是不仅要有很高的吞吐量,还要求很小的延迟,这是CPU并不采用过多流水线级数的原因之一。另外流水线只有在满载时,才能发挥出最佳效率来。由于CPU执行的代码中有很多分支语句,因此长流水线需要用有效的技术来预测分支,尽量保持流水线在满负荷状态。但是一旦预测分支失败,就会清除流水线中滞留的大量无用指令,同时将新指令流重新注入流水线。但是如果流水线阶段过多的话,充满整个流水线就需要很长的时间,这样使流水线保持满载的机会不多,速度反而下降了。所以权衡利弊,CPU不会使用深度流水线。
但是GPU却采用了几百级的流水线,比如GeForce 3的流水线有800个阶段。是什么原因,GPU的应用为何可以忍受这么大的延迟呢?假设以每秒50帧的速率显示画面,那么只要求每帧在20ms以内生成就行。而GeForce 3的时钟频率是200MHz(每个时钟周期是5ns),5ns * 800 = 4μs < 20ms。所以对GPU来说这一些延迟根本不成问题,而且GPU中执行的Shader程序中,分支语句用的很少(在早期的GPU中,甚至不提供动态的分支语句)。因此,GPU的流水线深度变大后,利大于弊,大大提升了整体性能。GPU的执行速度很快,但是当运行从内存中获取纹理数据这样的指令时(由于内存访问是瓶颈,此操作比较缓慢),整个流水线便出现长时间停顿。在CPU内部,使用多级Cache来提高访问内存的速度。GPU中也使用Cache,不过Cache命中率不高,只用Cache解决不了这个问题。所以,为了保持流水线保持忙碌,GPU的设计者使用了多线程机制(multi-threading),见图3。当像素着色器针对某个像素的线程A遇到存取纹理的指令时,GPU会马上切换到另外一个线程B,对另一个像素进行处理。等到纹理从内存中取回时,可再切换到线程A。但是使用这种方法有一个前提,线程A与线程B没有数据依赖性,也就是说两线程之间无需通讯。如果线程B需要线程A提供某些数据,那么即使切换到线程B,线程B仍是无法运行,流水线还是处于空闲状态。不过幸运的是,图形渲染本质上是一个并行任务。无论是CPU送给GPU的顶点数据,还是GPU光栅生成器产生的像素数据都是互不相关的,可以并行地独立处理。而且顶点数据(xyzw),像素数据(RGBA)一般都用四元数表示,适合于并行计算。在GPU中专门设置了SIMD指令来处理向量,一次可同时处理四路数据。SIMD指令使用起来非常简洁,可以看一个顶点矩阵变换的例子(见图4)。此外,纹理片要么只能读取,要么只能写入,不允许可读可写,从而解决了存贮器访问的读写冲突。GPU这种对内存使用的约束也进一步保证了并行处理的顺利完成。
<shape id="_x0000_s1028" style="MARGIN-TOP: 339.7pt; Z-INDEX: 2; MARGIN-LEFT: 135pt; WIDTH: 135pt; POSITION: absolute; HEIGHT: 23.4pt" stroked="f" coordsize="21600,21600" type="#_x0000_t202"></shape><shape id="_x0000_i1027" style="WIDTH: 414.75pt; HEIGHT: 331.5pt" coordsize="21600,21600" type="#_x0000_t75"><imagedata src="gpu.files/image005.jpg" o:title="图2"></imagedata></shape>
<shape id="_x0000_s1029" style="MARGIN-TOP: 7.95pt; Z-INDEX: 3; MARGIN-LEFT: 2.5pt; WIDTH: 402.5pt; POSITION: absolute; HEIGHT: 148.05pt" stroked="f" coordsize="21600,21600" type="#_x0000_t202" fillcolor="silver"><textbox style="MARGIN-TOP: 4.031pt; LEFT: auto; MARGIN-LEFT: 7.437pt; WIDTH: 388.125pt; TOP: auto; HEIGHT: 140.843pt; mso-next-textbox: #_x0000_s1029"></textbox></shape>
// c0 - c3 = 几何变换矩阵(world/view/proj matrix)
dp4 oPos.x, r0, c0 //指令dp4完成两个四元数的点积运算
//oP0s.x=(r0.x*c0.x)+(r0.y*c0.y)+
// (r0.z*c0.z)+(r0.w*c0.w)
//四条dp4指令就实现了对一个顶点的矩阵变换
dp4 oPos.y, r0, c1
dp4 oPos.z, r0, c2
dp4 oPos.w, r0, c3
|
<shape id="_x0000_s1030" style="MARGIN-TOP: 0px; Z-INDEX: 4; LEFT: 0px; MARGIN-LEFT: 2in; WIDTH: 153pt; TEXT-INDENT: 0px; POSITION: absolute; HEIGHT: 23.4pt; TEXT-ALIGN: left" stroked="f" coordsize="21600,21600" type="#_x0000_t202"><textbox style="MARGIN-TOP: 3.593pt; LEFT: auto; MARGIN-LEFT: 7.187pt; WIDTH: 138.625pt; TOP: auto; HEIGHT: 16.187pt; mso-next-textbox: #_x0000_s1030"></textbox></shape>
为了进一步提高并行度,可以增加流水线的条数。在GeForce 6800 Ultra中,有多达16组像素着色器流水线, 6组顶点着色器流水线。多条流水线可以在单一控制部件的集中控制下运行,也可以独立运行。在单指令多数据流(SIMD)的结构中,单一控制部件向每条流水线分派指令,同样的指令被所有处理部件同时执行。另外一种控制结构是多指令多数据流(MIMD),每条流水线都能够独立于其他流水线执行不同的程序。GeForce 6800 Ultra的顶点着色器流水线使用MIMD方式控制,像素着色器流水线使用SIMD结构。MIMD能比较有效率地执行分支程序,而SIMD体系结构运行条件语句时会造成很低的资源利用率。不过SIMD需要硬件少,这是一个优势。
CPU中大部分晶体管主要用于构建控制电路(象分支预测等)和Cache,只有少部分的晶体管来完成实际的运算工作。而GPU的控制相对简单,而且对Cache的需求小,所以大部分晶体管可以组成各类专用电路、多条流水线,使得GPU的计算速度有了突破性的飞跃,拥有了惊人的处理浮点运算的能力。现在CPU的技术进步正在慢于摩尔定律,而GPU(视频卡上的图形处理器)的运行速度已超过摩尔定律,每6个月其性能加倍。
虽然GPU最初专门是为图形渲染设计的,但是GPU还可以有效地执行多种通用计算,从线性代数和信号处理到数值仿真等等。有的专家甚至认为GPU将进入计算的主流。过去,学生们在课堂学习计算机体系结构时,听老师讲解各类并行计算机时,仿佛觉得这些东西都是科学家才使用的尖端产品。可又何曾想到,现在并行计算机(GPU) 就在我们的普通PC电脑内,触手可及。
主要参考文献:
1. GPU Gems 2 : Programming Techniques for High-Performance Graphics
and General-Purpose Computation (Gpu Gems): Books by Matt Pharr,Randima Fernando.
2. The Cg Tutorial: The Definitive Guide to Programmable Real-Time Graphics.
3. Lindholm E, Kilgard MJ, Moreton H. A user-programmable vertex engine. In: Proc. of the SIGGRAPH 2001. <city w:st="on"><place w:st="on"><span class="GramE">Los Angeles</span></place></city>, 2001. 149~158.
作者:沈璐
EMAIL:szlongman@hotmail.com
GameRes游戏开发资源网 http://www.gameres.com
相关推荐
GPU为什么跑得快?.pdf
分析GPU算法,和N体数值的模拟算法等。
GPU 专门用于快速完成一些特定类型的数学运算,特别是对于浮点、矢量和矩阵的计算,能将 3D 模型的信息转换为 2D 表示,同时添加不同的纹理和阴影效果,所以 GPU 在硬件里也是比较特殊的存在。
虚拟GPU为VDI带来哪些好处?.pdf
训练效率低?GPU利用率上不去?快来看看别人家的tricks吧〜.rar
训练效率低?GPU利用率上不去?快来看看别人家的tricks吧〜.pdf
GPU的未来在哪里?2011年GPU技术大会亚洲站报道.pdf
未来的汽车需要什么样的GPU?.pdf
未来应用的多面手——“明年”的GPU都能做什么?.pdf
简单测试:GPU比CPU快5秒 补充知识:tensorflow使用CPU可以跑(运行),但是使用GPU却不能用的情况 在跑的时候可以让加些选项: with tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_...
GPU虚拟化、GPU直通、GPU共享的区别.pdf
这个是最新版本的 支持cuda的 开源的GPU版本的sift 懂的人自然知道是好东西哈
GPU上循环矩阵的快速求逆算法.pdf
此篇文章主要是:自己在配置tensorflow-gpu环境后,在进行实验时发现的问题(想测试下是否可用gpu跑代码),此次配置相关信息:windows10+cuda9.0+cudnn7.0+tensorflow-gpu1.5.0 希望我们能够共同学习
GPU 在高性能计算和深度学习加速中扮演着非常重要的角色,GPU 的强大的并行计算能力,大大提升了运算性能。随着运算数据量的不断攀升,GPU 间需要大量的交换数据,GPU 通信性能成为了非常重要的指标。NVIDIA 推出的 ...
GPU编程 GPU精粹:GPU 3D和通用编程技巧
设计了一种在图形处理器(GPU)上完成大型矩阵快速运算的方法,主要通过使用Kahan求和公式来确 保计算精度,根据GPU特点设计矩阵分块方式和内存分配机制来减少对数据访问频次,以发挥GPU的并行体系结构 特性来提高计算...
GPU 通用计算 GPU通用计算 GPU 通用计算 GPU通用计算
GPUmat能够使得Matlab代码运行在GPU上的开发包: (1)不需要任何GPU的知识就可以在Matlab中访问GPU资源; (2)Matlab代码直接运行在GPU上,执行是用户透明的; (3)GPUmat使用GPU多核架构加速Matlab函数; ...