前面说了对我这一年多的工作进行一个总结,由于工作比较紧,加上本人比较懒,一直没能抽出时间来写,最近稍微闲下来了。先写一篇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指令。

   float4 vPos = float4(,,,);

   for (int i = ; i < ; ++i)

   vPos += float4 (mul (vMat[i], vInputPos), 1.0);

   --->

   float4 vPos = float4(,,,);

   for (int i = ; i < ; ++i)

    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.

GPU 优化总结的更多相关文章

  1. CBIR--Survey.C/GPU优化.Sys搭建

    一:CBIR综述:转自于wiki:http://zh.wikipedia.org/wiki/CBIR 参考链接:http://blog.csdn.net/kezunhai/article/detail ...

  2. GPU优化方法[转]

    CUDA优化的最终目的是:在最短的时间内,在允许的误差范围内完成给定的计算任务.在这里,“最短的时间”是指整个程序运行的时间,更侧重于计算的吞吐量,而不是单个数据的延迟.在开始考虑使用GPU和CPU协 ...

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

    笔者测试环境VS2019. 基本介绍 原书作者引入Julia Sets意在使用GPU加速图形的绘制.Julia Set 是指满足下式迭代收敛的复数集合 \[ Z_{n+1}=Z_{n}^2+C \] ...

  4. TVM优化Deep Learning GPU算子

    TVM优化Deep Learning GPU算子 高效的深度学习算子是深度学习系统的核心.通常,这些算子很难优化,需要HPC专家付出巨大的努力. 端到端张量IR / DSL堆栈TVM使这一过程变得更加 ...

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

    WeTest 导读   本文作者从自身多年的Unity项目UI开发及优化的经验出发,从UGUI,CPU,GPU以及unity特有资源等几个维度,介绍了unity手游性能优化的一些方法.   在之前的文 ...

  6. 在配有英特尔® Iris™ 显卡的系统上通过优化对 Just Cause 3 进行增强

    高端 PC 继续通过高性能显卡驱动桌面游戏. 一流的"梦想机器"基于第六代智能 英特尔® 酷睿™ 处理器i7-6700K等 CPU,通常与高端独立显卡配合使用以运行要求最严苛的游戏 ...

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

    一 RPC正射校正的原理 影像正射校正的方法有很多,主要包含两大类:一类是严格的几何纠正模型,另一类是近似几何纠正模型.当遥感影像的成像模型和有关参数已知时,可以根据严格的成像模型来校正图像,这种方法 ...

  8. Unity 几种优化建议

    转: http://user.qzone.qq.com/289422269/blog/1453815561?ptlang=2052 Unity 几种优化建议 最简单的优化建议: 1.PC平台的话保持场 ...

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

    原地址:http://blog.sina.com.cn/s/blog_5b6cb9500101dmh0.html 转载请留下本文原始链接,谢谢.本文会不定期更新维护,最近更新于2013.11.09   ...

随机推荐

  1. FZU 2171 线段树 区间更新求和

    很模板的题 在建树的时候输入 求和后更新 #include<stdio.h> #include<string.h> #include<algorithm> #inc ...

  2. GTX 680 Kepler

    http://www.nvidia.com/object/nvidia-kepler.html http://www.geforce.com/hardware/desktop-gpus/geforce ...

  3. mysq数据库再次理解

    1.表中的一条记录就是一个object,object有很多属性,对应表中的字段.object的属性对应的值就是字段值 2.外键是关联表关系用的.表关系的确立只能通过外键 但更高效的策略是,在数据库中部 ...

  4. 【IOS笔记】Resource Management in View Controllers

    Resource Management in View Controllers 视图控制器的资源管理 View controllers are an essential part of managin ...

  5. Class 实现IDisposing方法

    public class MyResourceHog : IDisposable  {   // 已经被处理过的标记   private bool _alreadyDisposed = false;  ...

  6. B-Tree indexs

    mysql_High.Performance.MySQL.3rd.Edition.Mar.2012 A B-Tree index speeds up data access because the s ...

  7. jboss4.2.3建立oracle JMS应用

    一.基本配置 1 增加oracle驱动文件,ojdbc6.jar,不能使用小于该版本的jdbc驱动,jboss-4.2.3.GA\server\default\lib 2 增加retrotransla ...

  8. jq窗口类小问题

    if ($(window).height() + $(window).scrollTop() >= $(document.body).height() - options.marginBotto ...

  9. phpcms list页实现分页

    {pc:content action="lists" catid="41" order="id ASC" num="1" ...

  10. RequestContextListener有什么用

    问题: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request att ...