如何使用“GPU 呈现模式”进行卡顿问题定位

工具介绍

Android 包含一些设备上的开发者选项,可帮助您直观地查看您的应用可能会在何处遇到界面渲染问题,如执行不必要的渲染工作,或执行长时间的线程和 GPU 操作。

其中,GPU 呈现模式(GPU 渲染模式)工具,可以显示渲染流水线的每个阶段渲染前一帧所用的相对时间。这些信息有助于您确定流水线中的瓶颈所在,从而了解应该优化哪些方面来提高应用的渲染性能。

注意,不同厂商对该工具的命名略有区别。

界面展示及使用

GPU 呈现模式分析工具以滚动直方图的形式直观地显示渲染界面窗口帧所花费的时间(以每帧 16 毫秒的速度作为对比基准)。

在性能较低的 GPU 上,可用的填充率(GPU 填充帧缓冲区的速度)可能很低。随着绘制一帧所需的像素数的增加,GPU 可能需要更长的时间来处理新命令,并要求系统的其余任务等待,直到它跟上进度。此分析工具可帮助您确定 GPU 何时因尝试绘制像素而不堪重负,或何时因大量的过度绘制而被拖累。

启动 GPU 呈现模式分析工具

在Android 4.1(API 级别 16)或更高版本的设备上,执行以下步骤开启工具:

  1. 启动开发者选项;
  2. 在“监控”部分,找到“GPU呈现模式分析”(不同厂商命名有所区别);
  3. 点击“GPU呈现模式分析”,弹出页面中,选择“在屏幕上显示为条形图”即可。

这时,GPU 呈现模式工具已经开启了,接下来,我们可以打开我们要测试的APP来进行观察测试了。

视觉呈现

GPU 渲染模式分析工具以图表(以颜色编码的直方图)的形式显示各个阶段及其相对时间。

Android 6.0(API 级别 23)上显示的彩色部分:

图. 放大的 GPU 渲染模式分析图形。

下面是有关输出的几点注意事项:

  • 对于每个可见应用,该工具将显示一个图形。

  • 沿水平轴的每个竖条代表一个帧,每个竖条的高度表示渲染该帧所花的时间(以毫秒为单位)。

  • 水平绿线表示 16 毫秒。要实现每秒 60 帧,代表每个帧的竖条需要保持在此线以下。当竖条超出此线时,可能会使动画出现暂停。

  • 该工具通过加宽对应的竖条并降低透明度来突出显示超出 16 毫秒阈值的帧。

  • 每个竖条都有与渲染管道中某个阶段对应的彩色区段。区段数因设备的 API 级别不同而异。

颜色块含义

GPU 渲染模式分析图表的图例:

Android 6.0 及更高版本的设备时分析器输出中某个竖条的每个区段含义:

表 . Android 6.0 及更高版本中的竖条区段。

4.0(API 级别 14)和 5.0(API 级别 21)之间的 Android 版本具有蓝色、紫色、红色和橙色区段。低于 4.0 的 Android 版本只有蓝色、红色和橙色区段。下表显示的是 Android 4.0 和 5.0 中的竖条区段。

表 . Android 4.0 和 5.0 中的竖条区段。

卡顿性能分析

GPU 呈现模式工具,很直观的为我们展示了 APP 运行时每一帧的耗时详情。我们只需要关注代表每一帧的柱状图的颜色详情,就可以分析出卡顿的原因了。

我们来看每个阶段的含义及发生了什么样的操作。

输入处理

输入处理阶段测量的是应用处理输入事件所用的时间。该指标表示应用执行输入事件回调给出的代码所用的时间。

此段耗时较长的原因?

此区域中的值较高通常是由于输入处理程序事件回调中的工作过多或过复杂导致的。 由于这些回调总是发生在主线程上,因此该问题的解决方法主要是直接优化工作,或者将工作转移到其他线程。

另外值得注意的是,此阶段可能出现 RecyclerView 滚动。当 RecyclerView 处理轻触事件时,它会立即滚动。因此,它可能会膨胀或填充新的项目视图。由于这个原因,尽可能加快此操作的执行速度就非常重要。TraceView 或 Systrace 等分析工具可以帮助我们进一步调查相关问题。

动画

动画阶段显示的是评估在该帧中运行的所有 Animator 所用的时间。最常见的 Animator 有 ObjectAnimator、ViewPropertyAnimator 和 Transitions。

此段耗时较长的原因?

造成此区域中的值较高的原因,通常是因动画的某种属性更改而执行的工作。例如,投掷动画会滚动 ListView 或 RecyclerView,从而导致大量的视图膨胀和填充。

测量/布局

为了在屏幕上绘制视图项,Android 会在视图层次结构的布局和视图中执行两项特定操作。

首先,系统会测量视图项。每个视图和布局都包含描述对象在屏幕上的尺寸的具体数据。有些视图具有确切的尺寸,而有些视图的尺寸则可以根据父级布局容器的尺寸进行调整。

然后,系统会对视图项进行布局。系统计算出子视图的尺寸后,就可以进行布局,调整视图在屏幕上的尺寸和位置。

系统不仅会对要绘制的视图进行测量和布局,还会逐层向上对这些视图的父级层次结构执行测量和布局,一直到根视图。

此段耗时较长的原因?

如果 APP 在这方面对每帧花费的时间较多,通常是由于需要布局的视图数量过多,或者出现了其他问题(例如在层次结构的错误位置发生了 Double Taxation 等)。在这两种情况下,要解决性能问题,都需要改善视图层次结构的性能。

此外,添加到 onLayout(boolean, int, int, int, int) 或 onMeasure(int, int) 的代码也可能会导致性能问题。Traceview 和 Systrace 可以帮助我们检查调用堆栈,以找出代码可能存在的问题。

绘制

绘制阶段将视图的渲染操作(如绘制背景或文本)转换为一系列原生绘制命令。系统会将这些命令捕获到一个显示列表中。

“绘制”条记录了将这些命令捕获到显示列表中所用的时间,包括这一帧中需要在屏幕上更新的所有视图对应的命令。测量的时间涵盖了您添加到应用中的界面对象的所有代码。此类代码的示例包括 onDraw()、dispatchDraw() 以及属于 Drawable 类的子类的各种 draw() 方法。

此段耗时较长的原因?

简单地说,我们可以将该指标理解为针对每个失效视图运行所有 onDraw() 调用所用的时间。该测量值涵盖了将绘制命令分配给可能存在的子项和可绘制对象所用的任何时间。因此,当我们看到此竖条出现峰值时,可能是因为大量视图突然失效了。失效意味着必须重新生成视图的显示列表。或者,一些自定义视图的 onDraw() 方法中存在某种极其复杂的逻辑,也可能会导致此时间很长。

同步/上传

“同步和上传”指标表示在当前帧中将位图对象从 CPU 内存传输到 GPU 内存所需的时间。

作为两种不同的处理器,CPU 和 GPU 将不同的 RAM 区域用于执行处理。当我们在 Android 中绘制位图时,系统会先将位图传输到 GPU 内存,然后 GPU 才能将其渲染到屏幕上。之后,GPU 会缓存位图,以便系统无需再次传输数据,除非纹理被移出 GPU 纹理缓存。

此段耗时较长的原因?

帧的所有资源都必须位于 GPU 内存中才能用来绘制帧。这意味着,该指标的值较高可能表示需要加载大量小型资源或少量大型资源。一种常见的情况是应用要显示一个接近屏幕尺寸的位图。另一种情况是应用要显示大量缩略图。

要减小该值,您可以采用以下技巧:

  • 确保位图的分辨率不会比位图的显示尺寸大很多。例如,您的应用应避免将 1024×1024 的图片显示为 48×48 的图片。
  • 利用 prepareToDraw() 在下一个同步阶段之前异步预上传位图。

发出命令

“发出命令”分段表示的是发出将显示列表绘制到屏幕上所需的全部命令所需的时间。

为了将显示列表绘制到屏幕上,系统会向 GPU 发送必要的命令。通常,系统会通过 OpenGL ES API 执行此操作。

此过程需要一些时间,因为在将命令发送给 GPU 之前,系统会对每个命令执行最后的转换和裁剪。然后 GPU 会计算最终的命令,使得 GPU 端的开销增加。这些命令包括最后的转换和额外的裁剪。

此段耗时较长的原因?

在此阶段花费的时间直接反映了系统在给定帧中渲染的显示列表的复杂程度和数量。例如,如果有大量绘制操作,特别是在每个绘制基元都有较少的固有开销的情况下,将会使得这项时间增加。 例如:

    for (int i = 0; i < 1000; i++) {
        canvas.drawPoint()
    }

发出以上命令的开销会远远大于发出以下命令的开销:

    canvas.drawPoints(thousandPointArray);

发出命令与实际绘制显示列表之间并不总是存在 1 对 1 的关系。“发出命令”阶段捕获的是将绘制命令发送到 GPU 所用的时间,而“绘制”指标表示的是将已发出的命令捕获到显示列表所用的时间。

之所以会出现这种差异,是因为系统会尽可能地缓存显示列表。因此,在某些情况下,滚动、转换或动画会要求系统重新发送显示列表,但不必实际重新构建它(即重新捕获绘制命令)。因此,我们可能会看到“发出命令”条较高,但“绘制命令”条并不高。

处理/交换缓冲区

当 Android 将其所有显示列表提交给 GPU 后,系统会发出最后一条命令,告诉图形驱动程序它已完成当前帧的处理。此时,驱动程序即可将更新后的图像显示到屏幕上。

此段耗时较长的原因?

有一点必须要注意:GPU 与 CPU 是并行工作的。Android 系统向 GPU 发出绘制命令,然后继续执行下一个任务。GPU 从队列中读取并处理这些绘制命令。

如果 CPU 发出命令的速度快于 GPU 处理命令的速度,这两个处理器之间的通信队列就会被占满。出现这种情况时,CPU 会阻塞并等待,直到队列中有位置来放置下一个命令。这种队列占满状态通常出现在“交换缓冲区”阶段,因为此时已提交了整个帧的命令。

缓解此问题的关键是降低 GPU 工作的复杂度,就像我们在“发出命令”阶段所做的那样。

其他

除了渲染系统执行其工作所用的时间外,主线程上还会执行一些与渲染无关的工作。这些工作耗费的时间被报告为“其他时间”。“其他时间”通常表示可能在两个连续渲染帧之间的界面线程上执行的工作。

此段耗时较长的原因?

如果该值很高,则表示我们的应用可能包含应在其他线程上执行的回调、Intent 或其他工作。通过方法跟踪或 Systrace 等工具可以查看主线程上运行的任务。此信息可帮助我们有针对性地改进性能。

ps:喜欢,就点个赞吧~

本文地址:https://blog.csdn.net/u011578734/article/details/109640946