1. 使用DS-5 Streamline定位瓶颈
DS-5 Streamline要求GPU驱动启用性能测试,在Mali GPU驱动中激活性能测试对性能影响微不足道。
1.1 DS-5 Streamline简介
可使用DS-5 Streamline从CPU和Mali GPU中实时收集性能计数器,然后以图形方式显示这些计数器,其主要功能如下:
? 收集计数器--从CPU和Mali GPU中
? 保存收集到的计数器数据以供回放
? 查看显示GPU活动、GPU活动和Framebuffer变化的时间线
? 以图形或表格的方式显示指定的性能计数器的值
? 观察计数器值如何变化
? 评估每一帧的性能
? 查看处理器活动的图表
? 查看堆栈跟踪
? 查看应用程序分析(Profiling)
1.2 DS-5 Streamline分析图
1) 查看下面三个图:
? GPU Vertex activity.
? GPU Fragment activity.
? <Application processor> Instruction: Executed.
2) 分析图
? 寻找具有最高和最长图形的处理器,它的使用最多;
? 如果很难找到占用太多时间的单个处理器,则问题可能在于带宽过度使用或图形管理被阻塞;
? 如果找到占用太多时间的处理器,则采取更多测量以隔离此问题;
? 如果所有图形都很忙,则应用程序充分利用了Mali GPU。
2. 通过比较定位问题区域
可以通过以下比较来定位问题区域:
? 修改分辨率
修改分辨率,然后测试帧率变化:
如果分辨率增加,而FPS下降(即分辨率增加为原来的2倍,则FPS下降为原来的1/2),则FP或带宽是性能的瓶颈;
如果FPS不随着分辨率的变化而变化,则CPU或VP是性能的瓶颈。
? 改变纹理大小
逐步减小纹理的大小,如果帧率增加,且纹理Cache命中率太低:则表明纹理太大,带宽是性能的瓶颈。
? 减少Shader的长度
如果Shader太长,它将降低帧率,深度缩短Shader并进行测试。
注:如果分辨率或FPS加倍,则FP可得到的cycles减半。
? 使用空的Fragment Shder
一个null shader什么都不做。如果使用null shader,性能快速上升,则表明Fragment Shader或带宽是性能瓶颈。
? 改变顶点数
如果减少顶点数,则FPS上升,则表明顶点数过多或带宽是性能瓶颈。
? 改变纹理位深度
当减少纹理深度时,如果FPS上升,则表明内存带宽是性能的瓶颈。
? 改变Drawing Surface位深度
当降低surface位深度,如果FPS上升,则表明内存带宽是性能的瓶颈。
? 减少draw调用
优化应用程序以减少draw调用,观察性能变化。
? 减少状态变化
优化应用程序以减少状态变化,观察性能变化。
3. 隔离具体的问题区域
当定位到问题区域之后,下一步就是找到瓶颈的具体原因。其方法如下:
3.1 应用处理器(CPU)限制了应用性能
? 应用逻辑复杂导致性能问题
如果删除draw调用和eglSwapBuffers之后,性能变化很少或没有变化,则表明瓶颈在应用逻辑,可通过OProfile来进行分析,它可区分用户态和Kernel态瓶颈问题。
? 驱动超负荷导致性能问题
如果低分辨率输出表明瓶颈在于CPU,且应用逻辑没有问题,则问题可能在于调用OpenGL ES API的方式:
1) 太多的draw调用
2) 太多的状态变化
3) 管道被阻塞
? 应用程序逻辑与驱动超负荷共同导致了性能问题
可通过DS-5或OProfile分析问题的根源所在。
3.2 顶点处理器(VP)限制了应用性能
如果顶点处理器限制了应用性能,问题可能在于以下方面:
? 太多的顶点
? Vertex Shader太长
? Vertex SHader太复杂
? 三角形设置时间过长
? 多边形列生成器单元时间过长(PLBU: Polygon List Builder Unit)
3.3 像素处理器(FP)限制了应用性能
如果像素处理器(FP)限制了应用性能,则问题可能在以下方面
1) 瓶颈:像素处理器(Fragment Processing)
? 过多的draw调用
? 需要读取太多的纹理
? 纹理cache miss过高
2) 瓶颈:像素处理程序(Fragment Shding)
? Shader太长
? Shader太复杂
? Shader太长且太慢
? Shader分支太多
3.4 内存带宽限制了应用性能
内存带宽影响一切且很难直接测量。如果一个处理器限制了性能,其它优化没有任何效果,则可能量内存带宽导致了此问题,其原因在于:
? 纹理过多或过大
? 太多的draw调用
4. 优化工作总流程
总的优化工作流程如下图所示:
首先对应用进行性能测试,以决定瓶颈所在的区域(CPU、VP、FP或内存带宽)。
? 必须在具有Mali GPU的真实的硬件上测试
? 使用DS-5 Streamline,并观察以下三个值:
1) GPU Vertex activity
2) GPU Fragment activity
3) <Application processor: CPU> Instruction: Executed
比较以上三个,找到占用时间最长且最高的图。
? 判定问题所在的区域
1) 如果能找到占用时间最长且最高的图,则问题可能出在这里
2) 如果不能看出某个处理器特别忙,但可以看到不同处理器间存在间隙,则可能管道被API阻塞了,问题在于CPU
3) 如果以上两种情况都不存在,则问题可能在于带宽问题
5. 应用处理器(CPU)优化工作流程
总的CPU优化工作流程如下图所示:
5.1 Applicatoin bound
如果应用时间太高(DS-5 Streamline中查看对应的应用进程),应用程序可能导致性能问题,因为它不能足够快地产生命令。
5.2 API bound
如果驱动时间太高(DS-5 Streanline中查看Kernel进程),驱动可能导致性能问题,因为它不能产生足够的命令。典型的原因是:没有以优化的方式调用OpenGL ES API函数,应用调用了太多的OpenGL ES API函数。
5.3 检查是否太多的draw调用?
通常上千的draw调用将导致性能严重下降,一般每帧在保持在1000次以内的draw调用较佳。使用DS-5 Streamline中的以下counter查看此值:
1) glDrawElements Statistics: Calls to glDrawElements
2) glDrawArrays Statistics: Calls to glDrawArrays
5.4 检查是否使用了VBO?
如何不使用VBO,每一帧都必须传输数据,这将限制应用的性能。在Utgard架构的Mali GPU中,可通过以下counter测量其使用情况:
1) BufferProfiling: VBO Upload Time (ms)
如果以上counter在出现尖峰的几帧之后,变为了0或值很小有一帧或多帧,表明你可能正确地使用了VBO;如果以上counter一直为0或很小,表明没有足够地或根本没有使用VBO。
5.5 检查是否有管道阻塞?
确认CPU和GPU是否同时忙,如果不是,则管道可能被阻塞了。为了避免管道阻塞,避免调用以下OpenGL ES函数:
? glReadPixels()
? glCopyTexImage()
? glTexSubImage()
5.6 检查是否有太多的状态变化?
状态变化开销相对较大,太多状态变化可能使用driver超负荷运行,从而影响性能。可通过查看以下OpenGL ES API的调用情况来查看状态变化:
? glEnable()
? glDisable()
6. GPU(Utgard)优化工作流程
6.1 顶点处理(VP)限制性能
注:在实际应用中,顶点处理(Vertex Processing)很少成为性能瓶颈。
高的顶点处理时间工作流程如下图所示:
6.1.1 检查vertex shader时间是否高?
可查看以下counter的图形是否总是高来确定存在此问题:
? Mali GPU Vertex Processor: Active cycles, vertex shader
为了查找其真正的原因,可分析以下三个counter来确定:
? Mali GPU Vertex Processor: Active cycles
? Mali GPU Vertex Processor: Active cycles, vertex shader
? Mali GPU Vertex Processor: Vertex loader cache misses
1) Shader太长?
如果以下条件都为真,表明Shader太长,需要缩短它。
? Active cycles vertex shader < Active cycles
? Vertex loader Cache misses值过高
2) Shader太复杂?
如果以下条件都为真,则表明Shader太复杂:
? Active cycles vertex shader接近Active cycles
? Vertex loader Cache misses值为低
可采用以下方法解决此问题:
? 简化Shader
? 应用算术优化
? 考虑是否可把部分工作移到CPU或FP(Fragment Processor)
3) Shader有太多的分支?
Mail GPU中的分支代价相对较低,但是太多的分析将导致Shader太长或太复杂。
6.1.2 检查顶点是否太多?
可查看以下counter的图形是否总是高来确定此问题。
?Mali GPU Vertex Processor: Vertices processed
6.1.3 检查创建多边形列表(PLBU)时间是否高?
可通过测量以下counter的值来确定Polygon List Builder Unit (PLBU)时间是否为高:
? Mali GPU Vertex Processor: Active cycles, PLBU geometry processing
如果以上counter图形总是高,则应用可能使用了太多的三角形。减少三角形数量的方法如下:
? 使用更少的对象(Use fewer objects)
? 使用简单的对象(Use simpler objects)
? 删除镶嵌的对象(De-tessellate objects)
? 裁剪掉一些三角形(Cull triangles)
6.1.4 检查被裁剪掉(culled)的原语
可以通过裁剪掉在最后的图像中不可见的三角形来减少场景中三角形的数量。可通过以下counter查看被裁剪掉的原语:
? Mali GPU Vertex Processor: Primitives culled
如果“Primitives culled”的图形低,表明应用没有使用足够的裁剪。确认“backface culling”和“depth testing”都被激活。
如果“Primitives culled”的图形高,可能有以下几方面的原因:
? 应用可能使用了太多的三角形
? 应用可能没有使用视锥裁剪
? 应用可能让Mali GPU做了太多的裁剪
6.1.5 检查是否使用了VBO?
参考本文5.4。
6.2 像素处理(FP)限制性能
像素处理器的瓶颈来源于以下两方面:
1) 纹理带宽高
2) Fragment Shader程序长
高的像素处理时间优化流程如下图所示:
6.2.1 纹理带宽高
6.2.1.1 检查纹理带宽
测量以下counters:
? Fragment Processor: Total bus reads
? Fragment Processor: Texture descriptors reads
如果以上两个counters的图形都低,则瓶颈在于Fragment Shader。
如果以上两个counters的图形都高,则瓶颈在于纹理。
6.2.1.2 检查超大的纹理
检测以下counters:
? Mali GPU Fragment Processor X: Texture cache hit count.
? Mali GPU Fragment Processor X: Texture cache miss count.
纹理cache脱靶率通常是纹理cache命中率的10%,如果高于此值,可能存在以下问题:
1) 纹理太大
2) 纹理位深度太高
3) 应用没有使用mipmapping
如果以上任意条件为真,则应用可能存在内存带宽问题。
6.2.1.3 检查压缩纹理读取
测量以下counters:
? Mali GPU Fragment Processor X: Texture cache hit count (CountA)
? Mali GPU Fragment Processor X: Compressed texture cache hit count (CountB)
针对以上两个counters比较分析如下:
1) 如果CountB是0,则没有使用压缩纹理
2) CountA-CountB(即未压缩纹理数)远远小于CountB,则表明使用的压缩纹理太少,可以考虑使用更多的压缩纹理
3) 如果压缩纹理的数量远远大于未压缩纹理的数量,则问题可能在于:
? 纹理太大
? 应用没有使用mipmapping
? 纹理太多
纹理使用大量的内存带宽,这可能导致shader不能获取到足够的数据,从而导致性能降低。
6.2.1.4 检查overdraw
检测以下counter:
? Mali GPU Fragment Processor X: Fragment passed z/stensil count (count)
overdraw factor = count/屏幕像素数(1920x1080);
1) 如果factor等于1,表明没有overdraw,但此情况一般很少,其通常为2.5,但依赖具体应用
2) 如果factor大于2.5,则性能将被影响,可使用以下技术减小overdraw:
? 启用深度测试
? 启用“back face culling”以避免渲染不可见的面
? 按照深度排序场景中的对象
? 从前到后的顺序画非透明对象
? 从后到前的顺序画透明对象
6.2.2 Fragment Shader程序长
高的Fragment Shader时间优化流程如下:
6.2.2.1 确认问题在Fragment Shader
测试以下counters:
? Mali GPU Fragment Processor X: Fragment passed z/stensil count. (CountA)
? Mali GPU Fragment Processor X: Instruction completed count.(CountB)
CountB/CountA:其结果为每个Fragment花费的平均指令数。如果此值高,它表明应用程序有较高的fragment shader time。其参考值参见:如何计算Fragment Shader的最大cycles
6.2.2.2 检查shader是否太长?
测量下面的硬件counters:
? Mali GPU Fragment Processor X: Program cache miss count. (CountA)
? Mali GPU Fragment Processor X: Program cache hit count. (CountB)
通常CountA是非常低的,且不大于CountB的0.01%。如果CountA图形高,表明Fragment Shader程序太长,需要缩短shader程序并验证。
6.2.2.3 检查shader是否太复杂?
测量下面的硬件counters:
? Mali GPU Fragment Processor X: Program cache miss count. (CountA)
? Mali GPU Fragment Processor X: Program cache hit count. (CountB)
通常CountA是非常低的,且不大于CountB的0.01%。如果CountA图形非常低,表明Fragment Shader太复杂,需要尝试以下方法:
1) 简化shader
2) 算法优化
3) 考虑是否可把ahder的部分功能移动CPU或VP
再次验证,如果对改善性能仍然无效,则看看是否shader太长且太复杂。
6.2.2.4 检查shader是否太长且太复杂?
如果已经检查过是否shader太长或复杂,且对优化没什么影响,则shader程序可能又长又复杂。对些,需要简化shader程序且缩短shader长度。
6.2.2.5 检查是否太多分支?
测量以下硬件counters:
? Mali GPU Fragment Processor X: Pipeline bubbles cycle count (CountA)
如果CountA高,表明shader可能有太多分支。
注:在Mali GPU上,分支不是一个大问题,因的它的计算量相对较小。
6.3 带宽限制性能
本节介绍如何确认带宽限制了性能,及如何减少带宽使用。
带宽限制工作流程如下图所示:
6.3.1 测试纹理cache命中与脱靶比率
纹理是内存带宽的最大用户。
测量下面的FP硬件counters:
? Mali GPU Fragment Processor X: Texture cache hit count. (CountA)
? Mali GPU Fragment Processor X: Texture cache miss count.(CountB)
通常CountA/CountB接近10,此比率越高越好,越低越糟糕。
一个低的比率表明cache使用高于正常状态,其原因可能为:
1) 使用了太大的纹理
2) 使用了太多的大纹理
3) 应用没有使用压缩纹理
4) 应用没有使用mipmap纹理
如果应用存在以上问题,则先进行了解决之后,再进行测试。
6.3.2 检查位块传输
位块传输(blitting)使用内存带宽且可能导致带宽过度使用。可查看以下counter:
? Mali EGL Software Counters: Blit Time
如果系统传输一个高分辨率的framebuffer,每秒的带宽需要几百MB。如果系统设置不正确,可能发生位块传输。
注:位块传输可能是显示系统的一部分,如果这样,位块传输是无法避免的。
6.3.3 测量可用最大带宽
为了定位谁过度使用了带宽:
? 计算出可得到的最大带宽
? 与系统的各个部分进行比较,以找到谁过度使用了带宽
如果不清楚设备的最大可用带宽,可使用一个测试程序来测量带宽,测试程序应满足以下要求:
? The highest resolution available.
? Highest bit depth possible.
? Very large, high bit depth textures.
? 16x Anti-aliasing.
? No texture compression.
? No mipmapping.
? No VSYNC.
如果测试程序运行帧率低,表明它充分使用了内存带宽。当运行测试程序时,测量以下counters:
? Mali GPU Vertex Processor: Words read, system bus.
? Mali GPU Vertex Processor: Words written, system bus.
? Mali GPU Fragment Processor X: Total bus reads.
? Mali GPU Fragment Processor X: Total bus writes.
注:如果Mali GPU有多个Fragment Processors,则需要测试每个FP。
把以上所有的测试结果加起来,然后乘以8,此结果为每秒可用的最大带宽,其单位为MB(Megabytes)。此结果包括cache使用,它可能稍稍大于系统中真正可用的最大内存带宽。
6.3.4 比较应用带宽与最大可用带宽
运行应用程序并测量以下counters:
? Mali GPU Vertex Processor: Words read, system bus.
? Mali GPU Vertex Processor: Words written, system bus.
? Mali GPU Fragment Processor X: Total bus reads.
? Mali GPU Fragment Processor X: Total bus writes.
把以上结果加起来并乘以8,其结果为应用程序使用的总带宽,其单位为MB/s。然后与6.3.3中的最大值进行比较,如果比较接近,说明应用使用了大多的内存带宽。
比较这些值,年谁最高:
1) 如果Mali GPU FP使用了太多的带宽,见下面的FP带宽限制性能
2) 如果Mali GPU VP使用了太多的带宽,见下面的VP带宽限制性能
6.3.5 FP带宽限制性能
如果应用的性能被FP带宽限制了,问题可能在于以下几方面:
? 纹理
通常读取纹理占用大量内存带宽,可减少纹理带宽的方法如下:
1) 减小纹理数量
2) 降低纹理分辨率
3) 降低纹理位深度
4) 使用mipmapping
5) 使用压缩纹理
? Overdraw
当像素被绘制在彼此之上,overdraw就发生了。它浪费了带宽,因为被绘制在其上的像素是不可见的。
? Trilinear filtering
三线性过滤需要读取多个纹理生成一个单一的象素,它使用了大量的带宽。
? Fragment Shader太复杂
复杂的Shaders拥有大量的中间状态,这些中间状态可能占满了cache内存,从而导致把中间状态刷新到主内存中。
6.3.6 VP带宽限制性能
如果VP过度占用带宽导致了性能问题,则问题可能在于:
? 太多的三角形
太多的三角形将占用大量带宽,但一般不会发生,除非场景调试复杂
如果不使用culling,也可能占用大量带宽;因为VP将处理一些从不被绘制的三角形
? 顶点Shader太复杂
复杂的Shaders拥有大量的中间状态,这些中间状态可能占满了cache内存,从而导致把中间状态刷新到主内存中。
? 读取非本地数据
如果你把从不使用的数据传递给GPU并cache起来。避免使用稀疏的顶点数组,总是把数据放在一起以提高cache的可能性。
版权声明:本文为博主原创文章,未经博主允许不得转载。