1page.title=测试显示性能
2page.image=images/cards/card-test-performance_2x.png
3page.keywords=性能, fps, 工具
4
5@jd:body
6
7
8<div id="qv-wrapper">
9  <div id="qv">
10    <h2>本文内容</h2>
11      <ol>
12        <li><a href="#measure">测量 UI 性能</a>
13          <ul>
14            <li><a href="#aggregate">聚合帧统计信息</a></li>
15            <li><a href="#timing-info">精确的帧时间信息</a></li>
16            <li><a href="#timing-dump">简单的帧时间转储</a></li>
17            <li><a href="#collection-window">控制统计信息收集的时段</a></li>
18            <li><a href="#diagnose">诊断性能回归</a></li>
19            <li><a href="#resources">其他资源</a></li>
20          </ul>
21        </li>
22        <li><a href="#automate">自动化 UI 性能测试</a>
23          <ul>
24            <li><a href="#ui-tests">设置 UI 测试</a></li>
25            <li><a href="#automated-tests">设置自动化 UI 测试</a></li>
26            <li><a href="#triage">分类并解决观察到的问题</a></li>
27          </ul>
28        </li>
29      </ol>
30  </div>
31</div>
32
33
34<p>
35  用户界面 (UI) 性能测试可确保您的应用不仅满足其功能要求,同时确保用户与应用之间的交互顺畅无比,能够以每秒连续 60 帧(<a href="https://www.youtube.com/watch?v=CaMTIgxCSqU&amp;index=25&amp;list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE">为什么选择 60fps?</a>)的帧速运行,而不会出现任何帧丢失或延迟的现象,也就是我们通常所说的“卡顿”。<em></em>
36
37
38本文档介绍可用于测量 UI 性能的工具并提出一种将 UI 性能测量集成到测试实践中的方法。
39
40
41</p>
42
43
44<h2 id="measure">测量 UI 性能</h2>
45
46<p>
47  为了改善性能,您首先必须能够测量系统性能,然后诊断并识别可能来自管道各个部分的问题。
48
49
50</p>
51
52<p>
53  <a href="https://source.android.com/devices/tech/debug/dumpsys.html">dumpsys</a> 是一种在设备上运行并转储感兴趣的系统服务状态信息的 Android 工具。<em></em>
54
55通过向 dumpsys 传递“gfxinfo”命令,可以提供 logcat 格式的输出,其中包含有关与录制阶段期间发生的动画帧相关的性能信息。
56
57<em></em>
58</p>
59
60<pre>
61&gt; adb shell dumpsys gfxinfo &lt;PACKAGE_NAME&gt;
62</pre>
63
64<p>
65  此命令可以生成多个不同表达形式的帧时间数据。
66</p>
67
68<h3 id="aggregate">聚合帧统计信息</h3>
69
70<p>
71  借助 M 预览版,该命令可将在整个进程生命周期中收集的帧数据的聚合分析打印输出到 logcat。
72例如:
73</p>
74
75<pre class="noprettyprint">
76Stats since: 752958278148ns
77Total frames rendered: 82189
78Janky frames: 35335 (42.99%)
7990th percentile: 34ms
8095th percentile: 42ms
8199th percentile: 69ms
82Number Missed Vsync: 4706
83Number High input latency: 142
84Number Slow UI thread: 17270
85Number Slow bitmap uploads: 1542
86Number Slow draw: 23342
87</pre>
88
89<p>
90  这些高级统计信息可以较高水平地传达应用的呈现性能及其在多个帧之间的稳定性。
91
92</p>
93
94
95<h3 id="timing-info">精确的帧时间信息</h3>
96
97<p>
98  M 预览版附带提供了一个适用于 gfxinfo 的新命令,即:framestats,该命令根据最近的帧提供非常详细的帧时间信息,让您能够更准确地查出并调试问题。<em></em>
99
100
101</p>
102
103<pre>
104&gt;adb shell dumpsys gfxinfo &lt;PACKAGE_NAME&gt; framestats
105</pre>
106
107<p>
108  此命令根据应用生成的最后 120 帧,打印输出带有纳秒时间戳的帧时间信息。以下是来自 adb dumpsys gfxinfo &lt;软件包名称&gt; framestats 的原始输出示例:
109
110
111</p>
112
113<pre class="noprettyprint">
1140,49762224585003,49762241251670,9223372036854775807,0,49762257627204,49762257646058,49762257969704,49762258002100,49762265541631,49762273951162,49762300914808,49762303675954,
1150,49762445152142,49762445152142,9223372036854775807,0,49762446678818,49762446705589,49762447268818,49762447388037,49762453551527,49762457134131,49762474889027,49762476150120,
1160,49762462118845,49762462118845,9223372036854775807,0,49762462595381,49762462619287,49762462919964,49762462968454,49762476194547,49762476483454,49762480214964,49762480911527,
1170,49762479085548,49762479085548,9223372036854775807,0,49762480066370,49762480099339,49762481013089,49762481085850,49762482232152,49762482478350,49762485657620,49762486116683,
118</pre>
119
120<p>
121  每行输出均代表应用生成的一帧。每行都有固定的列数,用于描述帧生成管道的每个阶段所花的时间。
122下文将详细描述此格式,包括每列代表的具体内容。
123
124</p>
125
126
127<h4 id="fs-data-format">Framestats 数据格式</h4>
128
129<p>
130  由于数据块是 CSV 格式的输出,因此将其粘贴到所选的电子表格工具或使用脚本进行收集和解析非常简单。
131下表解释了输出数据列的格式。
132所有时间戳均以纳秒计。
133</p>
134
135<ul>
136  <li>标志
137    <ul>
138      <li>“标志”列带有“0”的行可以通过从 FRAME_COMPLETED 列中减去 INTENDED_VSYNC 列计算得出总帧时间。
139
140      </li>
141
142      <li>该列为非零值的行将被忽略,因为其对应的帧已被确定为偏离正常性能,其布局和绘制时间预计超过 16 毫秒。
143
144可能出现这种情况有如下几个原因:
145        <ul>
146          <li>窗口布局发生变化(例如,应用的第一帧或在旋转后)
147
148          </li>
149
150          <li>此外,如果帧的某些值包含无意义的时间戳,则也可能跳过该帧。
151例如,如果帧的运行速度超过 60fps,或者如果屏幕上的所有内容最终都准确无误,则可能跳过该帧,这不一定表示应用中存在问题。
152
153
154          </li>
155        </ul>
156      </li>
157    </ul>
158  </li>
159
160  <li>INTENDED_VSYNC
161    <ul>
162      <li>帧的预期起点。如果此值不同于 VSYNC,则表示 UI 线程中发生的工作使其无法及时响应垂直同步信号。
163
164
165      </li>
166    </ul>
167  </li>
168
169  <li>VSYNC
170    <ul>
171      <li>所有垂直同步侦听器中使用的时间值和帧绘图(Choreographer 帧回调、动画、View.getDrawingTime() 等等)
172
173      </li>
174
175      <li>如需进一步了解 VSYNC 及其对应用产生的影响,请观看<a href="https://www.youtube.com/watch?v=1iaHxmfZGGc&amp;list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&amp;index=23">了解 VSYNC</a> 视频。
176
177
178      </li>
179    </ul>
180  </li>
181
182  <li>OLDEST_INPUT_EVENT
183    <ul>
184      <li>输入队列中最早输入事件的时间戳或 Long.MAX_VALUE(如果帧没有输入事件)。
185
186      </li>
187
188      <li>此值主要用于平台工作,对应用开发者的作用有限。
189
190      </li>
191    </ul>
192  </li>
193
194  <li>NEWEST_INPUT_EVENT
195    <ul>
196      <li>输入队列中最新输入事件的时间戳或 0(如果帧没有输入事件)。
197
198      </li>
199
200      <li>此值主要用于平台工作,对应用开发者的作用有限。
201
202      </li>
203
204      <li>但是,可以通过查看 (FRAME_COMPLETED - NEWEST_INPUT_EVENT) 大致了解应用增加的延迟时间。
205
206      </li>
207    </ul>
208  </li>
209
210  <li>HANDLE_INPUT_START
211    <ul>
212      <li>将输入事件分派给应用的时间戳。
213      </li>
214
215      <li>通过观察此时间戳与 ANIMATION_START 之间的时差,可以测量应用处理输入事件所花的时间。
216
217      </li>
218
219      <li>如果这个数字较高(&gt; 2 毫秒),则表明应用处理 View.onTouchEvent() 等输入事件所花的时间太长,这意味着此工作需要进行优化或转交给其他线程。
220
221请注意,有些情况下(例如,启动新Activity或类似活动的点击事件),这个数字较大是预料之中并且可以接受的。
222
223
224      </li>
225    </ul>
226  </li>
227
228  <li>ANIMATION_START
229    <ul>
230      <li>在 Choreographer 中注册的动画运行的时间戳。
231      </li>
232
233      <li>通过观察此时间戳与 PERFORM_TRANVERSALS_START 之间的时差,可以确定评估正在运行的所有动画(ObjectAnimator、ViewPropertyAnimator 和通用转换)所需的时间。
234
235
236      </li>
237
238      <li>如果这个数字较高(&gt; 2 毫秒),请检查您的应用是否编写了任何自定义动画,或检查 ObjectAnimator 在对哪些字段设置动画并确保它们适用于动画。
239
240
241      </li>
242
243      <li>如需了解有关 Choreographer 的更多信息,请观看<a href="https://developers.google.com/events/io/sessions/325418001">利弊</a>视频。
244
245      </li>
246    </ul>
247  </li>
248
249  <li>PERFORM_TRAVERSALS_START
250    <ul>
251      <li>如果您从此值中扣除 DRAW_START,则可推断出完成布局和测量阶段所需的时间(请注意,在滚动或动画期间,您会希望此时间接近于零)。
252
253
254      </li>
255
256      <li>如需了解有关呈现管道的测量和布局阶段的更多信息,请观看<a href="https://www.youtube.com/watch?v=we6poP0kw6E&amp;list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&amp;index=27">失效、布局和性能</a>视频。
257
258
259      </li>
260    </ul>
261  </li>
262
263  <li>DRAW_START
264    <ul>
265      <li>performTraversals 绘制阶段的开始时间。这是记录任何失效视图的显示列表的起点。
266
267      </li>
268
269      <li>此时间与 SYNC_START 之间的时差就是对树中的所有失效视图调用 View.draw() 所需的时间。
270
271      </li>
272
273      <li>如需了解有关绘图模型的详细信息,请参阅<a href="{@docRoot}guide/topics/graphics/hardware-accel.html#hardware-model">硬件加速</a>或<a href="https://www.youtube.com/watch?v=we6poP0kw6E&amp;list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&amp;index=27">失效、布局和性能</a>视频。
274
275
276      </li>
277    </ul>
278  </li>
279
280  <li>SYNC_START
281    <ul>
282      <li>绘制同步阶段的开始时间。
283      </li>
284
285      <li>如果此时间与 ISSUE_DRAW_COMMANDS_START 之间的时差较大(约 &gt; 0.4 毫秒),则通常表示绘制了大量必须上传到 GPU 的新位图。
286
287
288      </li>
289
290      <li>如需进一步了解同步阶段,请观看 <a href="https://www.youtube.com/watch?v=VzYkVL1n4M8&amp;index=24&amp;list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu">GPU 呈现模式分析</a>视频。
291
292      </li>
293    </ul>
294  </li>
295
296  <li>ISSUE_DRAW_COMMANDS_START
297    <ul>
298      <li>硬件呈现器开始向 GPU 发出绘图命令的时间。
299      </li>
300
301      <li>此时间与 FRAME_COMPLETED 之间的时差让您可以大致了解应用生成的 GPU 工作量。
302绘制过度或呈现效果不佳等问题都会在此显示出来。
303
304      </li>
305    </ul>
306  </li>
307
308  <li>SWAP_BUFFERS
309    <ul>
310      <li>调用 eglSwapBuffers 的时间,此调用不属于平台工作,相对乏味。
311
312      </li>
313    </ul>
314  </li>
315
316  <li>FRAME_COMPLETED
317    <ul>
318      <li>全部完成!处理此帧所花的总时间可以通过执行 FRAME_COMPLETED - INTENDED_VSYNC 计算得出。
319
320      </li>
321    </ul>
322  </li>
323
324</ul>
325
326<p>
327  您可以通过不同的方法使用此数据。一种简单却有用的可视化方式就是在不同的延迟时段中显示帧时间 (FRAME_COMPLETED - INTENDED_VSYNC) 分布的直方图(参见下图)。
328
329此图直观地表明,大部分帧非常有效,截止时间远低于 16 毫秒(显示为红色),但是少数帧明显超出了截止时间。
330
331我们可以观察此直方图中的变化趋势,了解所产生的整体变化或新异常值。
332此外,您还可以根据数据中的多个时间戳绘制表示输入延迟、布局所用时间或其他类似关注指标的图形。
333
334
335</p>
336
337<img src="{@docRoot}preview/images/perf-test-framestats.png">
338
339
340<h3 id="timing-dump">简单的帧时间转储</h3>
341
342<p>
343  如果在“开发者选项”中将 <strong>GPU 呈现模式分析</strong>设置为<strong>在 adb shell dumpsys gfxinfo 中</strong>,则 <code>adb shell dumpsys gfxinfo</code> 命令会打印输出最近 120 帧的时间信息,这些信息分为几个不同的类别,其相应的值以制表符分隔。
344
345
346这些数据可以用于大致表明绘图管道的哪些部分可能速度较慢。
347
348</p>
349
350<p>
351  与上述 <a href="#fs-data-format">framestats</a> 类似,将其粘贴到所选的电子表格工具或使用脚本进行收集和解析同样非常简单。
352
353下图详细显示了应用生成的许多帧的具体时间分布。
354
355</p>
356
357<img src="{@docRoot}preview/images/perf-test-frame-latency.png">
358
359<p>
360  运行 gfxinfo、复制输出、将其粘贴到电子表格应用并将数据绘制成堆积条形图的结果。
361
362</p>
363
364<p>
365  每个垂直条均代表一个动画帧;其高度代表计算该动画帧所需的毫秒数。
366垂直条的每个彩色分段均代表呈现管道的一个不同阶段,因此您可以看到应用的哪些部分可能会出现瓶颈。
367
368如需了解有关呈现管道的详细信息,请参阅<a href="https://www.youtube.com/watch?v=we6poP0kw6E&amp;index=27&amp;list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE">失效、布局和性能</a>视频。
369
370
371</p>
372
373
374<h3 id="collection-window">控制统计信息收集的时段</h3>
375
376<p>
377  Framestats 和简单的帧计时均可在极短的时间内(相当于约呈现 2 秒)收集数据。
378要精确控制此时间范围(例如,将数据限制于特定动画),您可以重置所有计数器并汇总收集的统计信息。
379
380
381</p>
382
383<pre>
384&gt;adb shell dumpsys gfxinfo &lt;PACKAGE_NAME&gt; reset
385</pre>
386
387<p>
388  这也可以与转储命令结合使用来定期进行收集和重置,从而持续捕获时间范围不到 2 秒的帧。
389
390
391</p>
392
393
394<h3 id="diagnose">诊断性能回归</h3>
395
396<p>
397  要查出问题并保持应用运行状况良好,第一步最好是识别回归。
398但是,dumpsys 仅确定是否存在问题及其相对严重性。
399您仍需诊断性能问题的具体原因并找到适当的解决方法。
400为此,我们强烈建议您使用 <a href="{@docRoot}tools/help/systrace.html">systrace</a> 工具。
401
402</p>
403
404
405<h3 id="resources">其他资源</h3>
406
407<p>
408  如需了解有关 Android 呈现管道的工作原理、可能存在的常见问题以及如何解决这些问题的详细信息,以下其他资源可能对您有所帮助:
409
410
411</p>
412
413<ul>
414  <li>呈现性能 101
415  </li>
416  <li>为什么选择 60fps?
417  </li>
418  <li>Android UI 和 GPU
419  </li>
420  <li>失效、布局和性能
421  </li>
422  <li>使用 Systrace 分析 UI 性能
423  </li>
424</ul>
425
426
427<h2 id="automate">自动化 UI 性能测试</h2>
428
429<p>
430  UI 性能测试方法之一是让测试人员对目标应用执行一系列用户操作,并目视检查是否存在卡顿现象,或花费大量时间使用工具驱动型方法来查明是否存在卡顿现象。
431
432但是,这种人工方法充满风险:人为感知帧率变化的能力参差不齐,并且此过程又费时、繁琐且易于出错。
433
434
435</p>
436
437<p>
438  更为有效的方法是记录并分析自动化 UI 测试中的关键性能指标。
439Android M 开发者预览版包括新的日志记录功能。利用这些功能,您可以轻松确定应用动画中的卡顿数量和严重性,您还可以使用这些功能构建严格的流程,用于确定当前性能并跟踪未来的性能目标。
440
441
442
443</p>
444
445<p>
446  本文将向您介绍一种使用这些数据自动化性能测试的推荐方法。
447
448</p>
449
450<p>
451  此方法主要分为两个关键操作。首先,确定测试对象和测试方法;其次,设置和维护自动化测试环境。
452
453
454</p>
455
456
457<h3 id="ui-tests">设置 UI 测试</h3>
458
459<p>
460  在开始进行自动化测试之前,您必须做出一些较高层次的决策,以便准确了解测试空间和可能存在的需求。
461
462</p>
463
464<h4>
465  识别要测试的关键动画/流程
466</h4>
467
468<p>
469  请记住,流畅的动画中断时,用户对低劣性能的感触最深。
470因此,在识别要测试的 UI 操作类型时,集中精力处理用户最常见或对用户体验最为重要的关键动画非常有用。
471
472例如,以下是对识别有所帮助的一些常见场景:
473</p>
474
475<ul>
476  <li>滚动主要的 ListView 或 RecyclerView
477  </li>
478
479  <li>异步等待周期内的动画
480  </li>
481
482  <li>可能涉及位图加载/操纵的所有操作
483  </li>
484
485  <li>包括 Alpha 值混合处理在内的动画
486  </li>
487
488  <li>使用画布绘制的自定义视图
489  </li>
490</ul>
491
492<p>
493  与团队中的工程师、设计师和产品经理开展合作,优先处理测试覆盖范围内的这些关键产品动画。
494
495</p>
496
497<h4>
498  定义未来的目标并予以跟踪
499</h4>
500
501<p>
502  从较高层面来看,确定具体的性能目标、专注于编写测试并收集相关数据至关重要。
503例如:
504</p>
505
506<ul>
507  <li>您是否只是首次想要开始跟踪 UI 性能以了解详情?
508  </li>
509
510  <li>您是否想要防止未来可能引入的性能回归?
511  </li>
512
513  <li>您当前是否有 90% 的帧运行顺畅且希望在本季度达到 98%?
514  </li>
515
516  <li>您是否有 98% 的帧运行顺畅且不希望出现性能回归?
517  </li>
518
519  <li>提高低端设备上的性能是否为您的目标?
520  </li>
521</ul>
522
523<p>
524  在所有这些情况下,您都将需要进行历史跟踪,以显示多个应用版本中的性能。
525
526</p>
527
528<h4>
529  识别用于测试的设备
530</h4>
531
532<p>
533  应用性能因其所在设备而异。某些设备的内存可能更少,GPU 功能略弱或 CPU 芯片速度较慢。
534这意味着动画在一套硬件上表现良好,但在其他硬件上可能并非如此,而且可能因为其他管道部分中出现的瓶颈表现更为糟糕。
535
536因此,考虑到用户可能会看到的这种变化,请选取各种设备(包括当前的高端设备、低端设备、平板电脑等)来执行测试。
537
538找出 CPU 性能、RAM、屏幕密度、尺寸等方面的变化。
539在高端设备上顺利通过的测试在低端设备上可能会失败。
540
541</p>
542
543<h4>
544  基本的 UI 测试框架
545</h4>
546
547<p>
548  <a href="{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a> 和 <a href="{@docRoot}training/testing/ui-testing/espresso-testing.html">Espresso</a> 等测试套件是为了帮助自动化用户使用应用过程中的操作而构建。
549
550这些套件是模拟用户与您的设备进行交互的简单框架。
551要使用这些框架,您需要有效创建通过一组用户操作运行的独特脚本,并在设备中演示这些脚本。
552
553
554</p>
555
556<p>
557  通过结合这些自动化测试以及 <code>dumpsys gfxinfo</code>,您可以快速创建可再生成的系统,然后您可使用此系统执行测试并测量特定条件的性能信息。
558
559
560</p>
561
562
563<h3 id="automated-tests">设置自动化 UI 测试</h3>
564
565<p>
566  您能够执行 UI 测试并拥有从单一测试收集数据的管道后,下一个重要步骤就是采用可以在多种设备上多次执行该测试的框架,并汇总生成的性能数据,以供开发团队做进一步分析。
567
568
569
570</p>
571
572<h4>
573  测试自动化框架
574</h4>
575
576<p>
577  有一点值得注意,UI 测试框架(例如,<a href="{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a>)直接在目标设备/模拟器上运行,
578而性能信息收集则是由主机通过 ADB 发送命令来驱动 dumpsys gfxinfo 完成的。<em></em>
579为帮助桥接这些单独实体的自动化,我们开发了 <a href="{@docRoot}tools/help/monkeyrunner_concepts.html">MonkeyRunner</a> 框架;这是一款在主机上运行的脚本编写系统,可以向一组连接设备发出命令并从中接收数据。
580
581
582
583</p>
584
585<p>
586  为正确自动化 UI 性能测试而构建一套脚本时,至少应当能够利用 monkeyRunner 完成以下任务:
587
588</p>
589
590<ul>
591  <li>向一个或多个目标设备或模拟器加载并启动所需的 APK。
592  </li>
593
594  <li>启动 UI Automator 的 UI 测试并允许执行该测试
595  </li>
596
597  <li>通过 dumpsys gfxinfo 收集性能信息。<em></em><em></em>
598  </li>
599
600  <li>汇总信息并以有效的方式重新向开发者显示。
601  </li>
602</ul>
603
604
605<h3 id="triage">分类并解决观察到的问题</h3>
606
607<p>
608  一旦确定问题模式或回归,下一步就是识别和应用修复。
609如果自动化测试框架保持帧的精确时间分解,则可帮助您审查最近的可疑代码/布局更改(出现回归时),或在切换到人工调查时缩小分析的系统范围。
610
611
612对于人工调查,<a href="{@docRoot}tools/help/systrace.html">systrace</a> 是一个很好的着手点,它可显示有关呈现管道的每个阶段、系统中的每个线程和核心以及您定义的任何自定义事件标记的精确时间信息。
613
614
615</p>
616
617<h4>
618  准确分析时间
619</h4>
620
621<p>
622  需要注意的是,获取和测量呈现性能所需的时间并非易事。
623就其本质而言,这些数字不具有确定性,往往会随系统状态、可用内存量、热节流以及太阳耀斑到达地面的最后时间而波动。
624
625问题在于,尽管同一测试可以运行两次,但获得的结果可能略有不同,它们彼此接近,却并不完全相同。
626
627
628</p>
629
630<p>
631  以这种方式准确收集和分析数据意味着多次运行同一测试,且以平均值或中值的形式累积结果(为了简单起见,我们将其称为“批处理”)。这可为您提供测试性能的粗略近似值,而无需确切的时间。
632
633
634
635</p>
636
637<p>
638  您可对代码更改前和更改后的应用均使用批处理,了解这些更改对性能产生的相对影响。
639如果更改前批处理的平均帧率大于更改后批处理的平均帧率,则此特定更改通常可为您带来全面的性能优势。
640
641
642</p>
643
644<p>
645  这意味着您执行的任何自动化 UI 测试均应考虑这一概念以及测试期间可能出现的任何异常。
646例如,如果应用性能因某些设备问题(不是由应用引起)骤降,则您可能需要重新运行批处理以便获得更精确的时间。
647
648
649
650</p>
651
652<p>
653  那么,在获得更有意义的测量结果之前,您应运行多少次测试?至少应运行 10 次,次数越多(例如 50 或 100 次)获得的结果更精确(当然,您现在是牺牲时间换取精确度)
654
655
656</p>
657