跑得快:GPU为什么跑得快?



  计算机3D游戏最基本个要求是:能以每秒数十帧速率根据当前 3维景物实时生成 3维动画你所看到图像会随着你视点变化而即时改变使人产生“身临其境”感觉帧率可以得到更流畅画面般来说30fps就是可以接受但是将帧速增加至60fps则可以明显提升交互感这和计算机影视制作区别电影特效可以通过很多台工作站花几十个小时绘制出长度仅为几秒高质量画面并不强调实时性  渲染个复杂 3维场景需要在秒内处理几千万个 3角形顶点和光栅化几十亿像素早期3D游戏显卡只是为屏幕上显示像素提供个缓存Cache所有图形处理都是由CPU单独完成图形渲染适合并行处理擅长于执行串行工作CPU实际上难以胜任这项任务所以那时在PC上实时生成 3维图像都很粗糙不过在某种意义上当时图形绘制倒是完全可编程只是由CPU来担纲此项重任速度上实在是达不到要求  直到1995年PC机领域第款GPU (Graphical Processing Unit) 3dfx Voodoo出来以后游戏速度、画质才取得了个飞跃3dfx Voodoo有两个主要特征:深度缓冲区(z-buffer)和纹理映射(texture mapping)z-buffer执行“隐藏面消除”这工作这样可以避免渲染“不可视”无效像素利用纹理映射功能则可以十分逼真地表达物体表面细节1999年第 2代GPU (Nvidia GeForce256,GeForce 2和ATI Radeon 7500)包括了图形几何变换和光照计算功能(T&L)而在此的前T&L都是由CPU完成这对CPU来说是很复杂计算第 2代GPU解决了系统个瓶颈减轻了CPU负荷速度明显提高了但是由于是固定渲染流水线缺乏灵活性束缚了开发人员创造性2001年NIVIDA公司GeForce 3首先引入了可编程顶点着色器(Vertex Shader)单元紧接着在2002年可编程像素着色器(Pixel Shader)单元也加入了GPU (见图1)在绘制时GPU首先接收CPU以 3角形顶点形式发送几何数据然后由可编程顶点着色器单元进行处理完成几何变换和顶点属性计算等功能接着這些 3维空间 3角形由个固定功能光栅生成器转换为 2维屏幕上像素每个像素最终颜色值都通过运行在像素着色器上运算而得目前 3维游戏借助于GPU已经能够实时生成十分细腻、逼真画面














图1. GPU体系结构示意图






  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是对图形实时渲染量身定制具有两点主要特征:超长流水线和并行计算







图2. GPU、CPU浮点运算速度对比图





  流水线技术和工厂里装配线在原理上类似如果装配台汽车需要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)般都用 4元数表示适合于并行计算在GPU中专门设置了SIMD指令来处理向量次可同时处理 4路数据SIMD指令使用起来非常简洁可以看个顶点矩阵变换例子(见图4)此外纹理片要么只能读取要么只能写入不允许可读可写从而解决了存贮器访问读写冲突GPU这种对内存使用约束也进步保证了并行处理顺利完成










图3. GPU中多线程机制



// c0 - c3 = 几何变换矩阵(world/view/proj matrix)
dp4 oPos.x, r0, c0    //指令dp4完成两个 4元数点积运算                       //oP0s.x=(r0.x*c0.x)+(r0.y*c0.y)+

//    (r0.z*c0.z)+(r0.w*c0.w)

// 4条dp4指令就实现了对个顶点矩阵变换

dp4 oPos.y, r0, c1
dp4 oPos.z, r0, c2

dp4 oPos.w, r0, c3
  为了进步提高并行度可以增加流水线条数在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. Los Angeles, 2001. 149~158. 作者:沈璐 EMAIL:[email protected]

Tags: 

延伸阅读

最新评论

发表评论