GPU 优化总结

  前面说了对我这一年多的工作进行一个总结,由于工作比较紧,加上本人比较懒,一直没能抽出时间来写,最近稍微闲下来了。先写一篇GPU优化的,后续的文章希望能慢慢补齐。这些基本都是我个人优化的实际经验,也参考了一些文章,我都放在后面引用 部分了,感兴趣的可以深入研究。个人理解可能有问题,如有不正确的还请指正,下面进入正题。

由于图形引擎的复杂性,瓶颈可能发生在CPU、GPU、,也可能发生在CPU与GPU的传输数据与交互之中。这里我们只假设瓶颈在GPU上,讨论GPU的优化方法。

Premature optimization is the root of all evil. -- Donald Knuth 这告诉我们过早优化程序是不可取的,我觉得有两方面的意思,1、在没有找到高效的算法前就开始优化。2、在没有找到真正的瓶颈关就开始优化。正确的流程大概是这样的

  1、使功能能工作,程序能跑起来。

  2、功能正确的工作。

  3、让整个程序能工作。

  4、让整个程序能正确工作。

  5、使用这个程序并找到性能瓶颈。

  6、使用性能分析工具找到瓶颈所在。

  7、使程序高效正确的运行。[1]

  还有一个原则就是80~20原则,即只有百分之二十的代码是常用的,所以要集中优化这些代码,而不是一些很少执行的代码上花些时间。

既然直接谈GPU优化,那我们就假设上面流程中的前五条已经满足,我们假定GPU有瓶颈了,这时我们可以借助一些工具如Perfhud、Intel GPA、NSight(替代Perfhud的工具)或其它方法来找到程序的瓶颈所在,然后根据这些分析结果来有目的的优化程序。

GPU可能存在的瓶颈主要在以下几个部分:

  1、纹理传输带宽限制 (显存到高速缓冲区)

  2、光栅操作完成后帧传输带宽限制(高速缓冲区到显存)

  3、顶点着色处理能力限制(VS)

  4、像素着色处理能力限制(PS)在新的硬件中使用统一着色处理单元时,可以动态调整VS PS使用的数量。

  5、光栅化限制。

  6、显卡显存过小。

  7、算法本身不够高效。

  8、Shader指令使用不合理。

具体查找瓶颈的方法如下:[2]

  1、如果改变纹理尺寸,帧率有明显变化,则瓶颈可能在纹理传输带宽限制或纹理AGP传输能力限制。若改变纹理过滤方式帧率提高了,则可能是纹理传输带宽限制。此时可通过减小纹理分辨率或纹理过滤方式来解决。

  2、若改变窗口大小,帧率有明显变化,则可能是由于光栅化或像素着色Shader限制,或者帧缓冲区带宽限制。此时减少PS指定数量,若FPS有明显变化,则说明是PS是瓶颈。否则此时改变后台缓冲区位宽,若帧率有明显变化则说明是帧缓冲区带宽限制,否则光栅化是瓶颈。

  3、若改变颜色位宽帧率有明显变化,则说明瓶颈在帧缓冲区,此时可以通过改变帧缓冲区带宽来提高帧率,这在帧缓冲区带宽小的低端显卡止效果很明显。

  4、减少VS指令数量,如果帧率有明显变化,则说明瓶颈在VS上,这种情况一般不会出现,如果在VS中访问纹理会比较慢,瓶颈可能会出现(Shader Model 3.0)。

  5、如果减少顶点数量,帧率有明显提升,则说明瓶颈可能在顶点过多,或顶点AGP

传输限制,此时可能通过模型LOD来解决问题。

  6、使用Perfhud等GPU分析工具来查找瓶颈,尤其是GPA可以实时修改查看效果,这样就可以比较高效的优化Shader。

  7、如果本身算法有问题,则可以找更高效的方法来实现同样的效率,或者有时为了效率也是可以牺牲一些效果的,也可以做Shader的LOD,不同配置下采用不同的Shader,这样在低端和高端显卡上都会有一个不错的帧率。

优化方法:

  1、纹理带宽限制。

  (1)减少不必要的大纹理。

  (2)可以动态修改纹理分辨率,去掉纹理前面几级Mip map。

  (3)尽量使用DXT格式的纹理。

  (4)避免使用非二次方的纹理。

  (5)如果不需要写颜色,那就把颜色写关闭。(Pre-z 阴影贴图等。)

  2、帧缓冲区带宽限制。

  (1)减少颜色位宽,如使用16位颜色。

  (2)减小后台缓冲区和Render Target大小。

  (3)对于特效比较多的情况,可以先渲染到一张小的纹理上去然后再Up-Sampling到主Render Target上。

  3、AGP传输瓶颈

  (1)顶点尽量使用索引条带或索引列表。

  (2)对顶点进行排序,这样可以减少顶点重复计算的次数(对结果进行Cache),使用NVTriStrip这个工作。

  (3)顶点大小应该是32(bit)的整数倍。

  (4)使用模型LOD。

  4、VS处理能力限制,一般不会成为瓶颈

  (1)使用模型LOD,减少顶点数量。

  (2)尽量减少VS指令的数量。

  (3)控制顶点纹理的使用。

  (4)能在CPU计算的在CPU计算完成后再传给VS。

  (5)方法3 中的优化也合适。

  (6)尽量使用低版本的Profile。

  5、PS处理能力限制。

  (1)尽量减少指令的长度。

  (2)尽量使用低版本的Profile。(如PS_2_a)等。

  (3)尽量使用低精度half进行计算。

  (4)利用硬件的特性来减少开销,比如如果要对纹理进行降采样,可以利用GPU的双线性来插值来实现,这样可以明显减少纹理访问的次数。

  (5)能在CPU计算的在CPU计算完成后再传给PS。

  (6)做Pre-Z。

  (7)做Shader LOD,可以在不同配置下切换。

  6、算法本身不够高效

  (1)尽量寻找更高效的算法代替,这样比一条条指令压榨要提升太多效率。

  7、Shader指令不合理,对GPU工作不是特别了解,导致写出低效的代码。[]

  (1)使用代数化简指令来减少不必要的计算。

  (2)使用能工作的最小Profile版本。

  (3)尽量使用half。

  (4)尽量不要写太通用的函数,这样可能会产生一些无用的指令。

  (5)不要乱用normalize,比如在PS中需要normalize,那么在VS中就没有必要使用。

  (6)normalize过的向量不需要计算长度(就是1)。

  (7)不要把向量放到多个Interpolator里面。

  (8)能在CPU计算的就放到CPU计算。

  (9)如果一个参数永远不变,则没必要从CPU传进来。

  (10)如果是结果是线性的,则这些计算可以放到VS计算,而没必要在PS计算。

  (11)使用下标小的interpolants。比如首先使用TEXCOORD0,再使用TEXCOORD1等。

  (12)使用纹理查找来取代复杂的函数。(目前是至少是6条算数指令:1条取纹理的关系)

  (13)Pre-z,尽量避免Pre-Z失效。具体参见NVIDIA编程指南。

  (14) 动态分支要谨慎使用,只有在大多数情况都走同一个分支时使用才有比较好的效果。

  (15)产生阴影时可以使用tex2DProj来利用硬件特性加速。

  (16)了解GPU的汇编指令,写出正确的代码。

  (17)纹理指令和算术指令交叉使用。

GPU的指令:[]

  1、mad 是一条指令 (x + 1) * 0.5 = x * 0.5 + 0.5 前面是一条add+mul指定,后面是一条mad指令,编译器并不会为我们优化。

  2、括号不要乱加,比如 x + y * 0.5 + z * 0.2 是两条指令 mad-->mad而x + (y* 0.5 + z * 0.2)是mul-->mad-->add三条指令。

  3、代数化简 (x + c) * (x-c) 三条指令 ---> x * x + (-c * c)两条指令

  4、减少不必要的mov指令。

 1   float4 vPos = float4(0,0,0,0);
 2
 3   for (int i = 0; i < 4; ++i)
 4
 5   vPos += float4 (mul (vMat[i], vInputPos), 1.0);
 6
 7   --->
 8
 9   float4 vPos = float4(0,0,0,1);
10
11   for (int i = 0; i < 4; ++i)
12
13    vPos .xyz+= mul (vMat[i], vInputPos);

  这样减少了3条mov指令。

  5、a/b 是用a * rcp(b)实现。D3D也可能使用div指定,但显式使用cp可能会产生更好的代码。(x + a) / x --> 1.0 + a * rcp(x)

  6、正确使用[branch] [flatten] [loop] [unroll]。[branch]在分支友好的情况下效率才会比较高。[unroll]在指令限制未到使用可提高效率。

  7、线性的运算放到VS里面去算,插值到PS即可。

  8、更多优化方法请参考引用[4]。

总结:

  游戏引擎是一个复杂的系统,瓶颈在不同机器上出现可能不一样,可能在CPU也可能在GPU,GPU上的瓶颈又可能出现在不同的环节。工欲善其事,必先利其器!我们要借助工具或上面提到的方法来找到程序的瓶颈,对症下药,不要盲目去优化,找到那20%的代码,首先是在算法上找优化的方法,如果确定算法是好的了,那才要开始指令级别的优化,否则是徒劳的。然后根据上面的方法基本上可以解决大多数的性能问题,当然我们还可以把多个效果结合到一起处理,因为可能有些中间结果是共用的,这样又可以省去一些额外的开销。尽最大限度的优化,做到在不同配置机器上都能流畅运行,还要在高端机器上有次世代的画面效果。

引用:

[1] http://c2.com/cgi/wiki?PrematureOptimization

[2] http://www.cnblogs.com/lancidie/archive/2011/03/29/1998830.html

[3] NVIDIA GPU Programming Guide version 2.5.0

[4] Low-level thinking in high-level shading languages.

时间: 2024-11-07 19:49:59

GPU 优化总结的相关文章

详解 CUDA By Example 中的 Julia Set 绘制GPU优化

笔者测试环境VS2019. 基本介绍 原书作者引入Julia Sets意在使用GPU加速图形的绘制.Julia Set 是指满足下式迭代收敛的复数集合 \[ Z_{n+1}=Z_{n}^2+C \] 环境配置 跑这个例子的主要困难应该在于配置环境.这个程序依赖于openGL中的glut库.由于VS2019的整个软件架构发生了很大变化,一些链接库和头文件的位置都发生了改变,因此一些文章中的配置方法失效了. 首先我们需要获取glut库的头文件以及动态链接库. 点击这里cg-toolkit获取.安装成

Unity内存优化技术测试案例

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解>电子工业出版社等. CSDN视频网址:http://edu.csdn.net/lecturer/144 Unity引擎优化技术,无非涉及到三点:CPU优化,GPU优化,内存优化. 先谈谈内存优化:大概可以分成三大部分: 1. Unity3D内部的内存 2. Mono的托管内存 3. 引入的DLL或者第

基于GPU的高分一号影像正射校正的设计与实现

一 RPC正射校正的原理 影像正射校正的方法有很多,主要包含两大类:一类是严格的几何纠正模型,另一类是近似几何纠正模型.当遥感影像的成像模型和有关参数已知时,可以根据严格的成像模型来校正图像,这种方法属于严格几何纠正,最具代表的是共线方程法.当传感器成像模型未知或者无法获取相关的辅助参数时,可以用假定的数学模型模拟成像模型,对影像实现校正,这种方法属于近似几何纠正,主要有:几何多项式纠正.有理函数法.局部区域校正等模型.本文将主要对RPC正射校正模型进行展开讨论. RPC模型将像点坐标d(lin

如何快速优化手游性能问题?从UGUI优化说起

WeTest 导读 本文作者从自身多年的Unity项目UI开发及优化的经验出发,从UGUI,CPU,GPU以及unity特有资源等几个维度,介绍了unity手游性能优化的一些方法. 在之前的文章<手游内存占用过高?如何快速定位手游内存问题>中提到,Mono内存和native内存是PSS内存主要的组成部分,mono内存更多的起到内存调用的功能,因此常常成为了开发人员优化内存的起点:而在游戏的其他的进程中,同样有很多因素影响着游戏的性能表现.本文将从UGUI的优化角度,介绍unity游戏性能优化的

GPU与GPGPU泛淡

GPU与GPGPU泛淡 GPU(Graphics Processing Unit),也即显卡,是一种专门在个人电脑.工作站.游戏机和一些移动设备(如平板电脑.智能手机等)上作图像运算工作的微处理器.它已经是个人PC和移动设备上不可或缺的芯片,有界面有显示的地方,一般就离不开它.高清电视.智能手机.个人电脑. GPU的产生是为了解决图形渲染效率的问题,但随着技术进步,GPU越来越强大,尤其是shader出现之后(这个允许我们在GPU上编程),GPU能做的事越来越多,不再局限于图形领域,也就有人动手

Unity 几种优化建议

转: http://user.qzone.qq.com/289422269/blog/1453815561?ptlang=2052 最简单的优化建议: 1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标GPU与CPU.2.如果你用U3D自带的SHADER,在表现不差的情况下选择Mobile或Unlit目录下的.它们更高效.3.尽可能共用材质.4.将不需要移动的物体设为Static,让引擎可以进行其批处理.5.尽可能不用灯光.6.动态灯光更加不要了

[Unity3D]图形渲染优化、渲染管线优化、图形性能优化

原地址:http://blog.sina.com.cn/s/blog_5b6cb9500101dmh0.html 转载请留下本文原始链接,谢谢.本文会不定期更新维护,最近更新于2013.11.09 主要内容也可以参考: http://docs.unity3d.com/Documentation/Manual/OptimizingGraphicsPerformance.html 最简单的优化建议: 1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标

unity几种优化建议

最简单的优化建议: 1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标GPU与CPU. 2.如果你用U3D自带的SHADER,在表现不差的情况下选择Mobile或Unlit目录下的.它们更高效. 3.尽可能共用材质. 4.将不需要移动的物体设为Static,让引擎可以进行其批处理. 5.尽可能不用灯光. 6.动态灯光更加不要了. 7.尝试用压缩贴图格式,或用16位代替32位. 8.如果不需要别用雾效(fog) 9.尝试用OcclusionCull

(图形渲染)优化

转载请留下本文原始链接,谢谢.本文会不定期更新维护,最近更新于2013.11.09http://blog.sina.com.cn/s/blog_5b6cb9500101dmh0.html 最简单的优化建议: PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标GPU与CPU. 如果你用U3D自带的SHADER,在表现不差的情况下选择Mobile或Unlit目录下的.它们更高效. 尽可能共用材质. 将不需要移动的物体设为Static,让引擎可以进行其批处