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. 优化工作总流程

    总的优化工作流程例如以下图所看到的:

Mali GPU 使用 pytorch mali gpu driver_Mali GPU 使用 pytorch

首先相应用进行性能測试。以决定瓶颈所在的区域(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优化工作流程例如以下图所看到的:

Mali GPU 使用 pytorch mali gpu driver_Mali GPU 使用 pytorch_02

5.1 Applicatoin bound

      假设应用时间太高(DS-5 Streamline中查看相应的应用进程)。应用程序可能导致性能问题,由于它不能足够快地产生命令。

5.2 API bound

      假设驱动时间太高(DS-5 Streanline中查看Kernel进程)。驱动可能导致性能问题,由于它不能产生足够的命令。典型的原因是:没有以优化的方式调用OpenGL ES API函数,应用调用了太多的OpenGL ES API函数。

5.3 检查是否太多的draw调用?

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)非常少成为性能瓶颈。

      高的顶点处理时间工作流程例如以下图所看到的:

Mali GPU 使用 pytorch mali gpu driver_应用性能_03

6.1.1 检查vertex shader时间是否高?

是否总是高来确定存在此问题:

• 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程序长

高的像素处理时间优化流程例如以下图所看到的:

Mali GPU 使用 pytorch mali gpu driver_应用程序_04

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时间优化流程例如以下:

Mali GPU 使用 pytorch mali gpu driver_压缩纹理_05

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长度。


6.2.2.5 检查是否太多分支?

  測量下面硬件counters:

   • Mali GPU Fragment Processor X: Pipeline bubbles cycle count (CountA)

  假设CountA高。表明shader可能有太多分支。

  注:在Mali GPU上。分支不是一个大问题,因的它的计算量相对较小。


6.3 带宽限制性能

   本节介绍怎样确认带宽限制了性能。及怎样降低带宽使用。

   带宽限制工作流程例如以下图所看到的:

Mali GPU 使用 pytorch mali gpu driver_Mali GPU 使用 pytorch_06

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的可能性。