1page.title=Testing Display Performance
2page.article=true
3page.image=images/cards/card-test-performance_2x.png
4page.keywords=performance, fps, tools
5
6@jd:body
7
8
9<div id="tb-wrapper">
10  <div id="tb">
11    <h2>In this document</h2>
12      <ol>
13        <li><a href="#measure">Measuring UI Performance</a>
14          <ul>
15            <li><a href="#aggregate">Aggregate frame stats</a></li>
16            <li><a href="#timing-info">Precise frame timing info</a></li>
17            <li><a href="#timing-dump">Simple frame timing dump</a></li>
18            <li><a href="#collection-window">Controlling the window of stat collection</a></li>
19            <li><a href="#diagnose">Diagnosing performance regressions</a></li>
20            <li><a href="#resources">Additional resources</a></li>
21          </ul>
22        </li>
23        <li><a href="#automate">Automating UI Perfomance Tests</a>
24          <ul>
25            <li><a href="#ui-tests">Setting up UI tests</a></li>
26            <li><a href="#automated-tests">Setting up automated UI testing</a></li>
27            <li><a href="#triage">Triaging and fixing observed problems</a></li>
28          </ul>
29        </li>
30      </ol>
31
32  </div>
33</div>
34
35
36<p>
37  User interface (UI) performance testing ensures that your app not only meets its functional
38  requirements, but that user interactions with your app are buttery smooth, running at a
39  consistent 60 frames per second (<a href=
40  "https://www.youtube.com/watch?v=CaMTIgxCSqU&amp;index=25&amp;list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE">why
41  60fps?</a>), without any dropped or delayed frames, or as we like to call it, <em>jank</em>. This
42  document explains tools available to measure UI performance, and lays out an approach to
43  integrate UI performance measurements into your testing practices.
44</p>
45
46
47<h2 id="measure">Measuring UI Performance</h2>
48
49<p>
50  In order to improve performance you first need the ability to measure the performance of
51  your system, and then diagnose and identify problems that may arrive from various parts of your
52  pipeline.
53</p>
54
55<p>
56  <em><a href="https://source.android.com/devices/tech/debug/dumpsys.html">dumpsys</a></em> is an
57  Android tool that runs on the device and dumps interesting information about the status of system
58  services. Passing the <em>gfxinfo</em> command to dumpsys provides an output in logcat with
59  performance information relating to frames of animation that are occurring during the recording
60  phase.
61</p>
62
63<pre>
64&gt; adb shell dumpsys gfxinfo &lt;PACKAGE_NAME&gt;
65</pre>
66
67<p>
68  This command can produce multiple different variants of frame timing data.
69</p>
70
71<h3 id="aggregate">Aggregate frame stats</h3>
72
73<p>
74  With the M Preview the command prints out aggregated analysis of frame data to logcat, collected
75  across the entire lifetime of the process. For example:
76</p>
77
78<pre class="noprettyprint">
79Stats since: 752958278148ns
80Total frames rendered: 82189
81Janky frames: 35335 (42.99%)
8290th percentile: 34ms
8395th percentile: 42ms
8499th percentile: 69ms
85Number Missed Vsync: 4706
86Number High input latency: 142
87Number Slow UI thread: 17270
88Number Slow bitmap uploads: 1542
89Number Slow draw: 23342
90</pre>
91
92<p>
93  These high level statistics convey at a high level the rendering performance of the app, as well
94  as its stability across many frames.
95</p>
96
97
98<h3 id="timing-info">Precise frame timing info</h3>
99
100<p>
101  With the M Preview comes a new command for gfxinfo, and that’s <em>framestats</em> which provides
102  extremely detailed frame timing information from recent frames, so that you can track down and
103  debug problems more accurately.
104</p>
105
106<pre>
107&gt;adb shell dumpsys gfxinfo &lt;PACKAGE_NAME&gt; framestats
108</pre>
109
110<p>
111  This command prints out frame timing information, with nanosecond timestamps, from the last 120
112  frames produced by the app. Below is example raw output from adb dumpsys gfxinfo
113  &lt;PACKAGE_NAME&gt; framestats:
114</p>
115
116<pre class="noprettyprint">
1170,27965466202353,27965466202353,27965449758000,27965461202353,27965467153286,27965471442505,27965471925682,27965474025318,27965474588547,27965474860786,27965475078599,27965479796151,27965480589068,
1180,27965482993342,27965482993342,27965465835000,27965477993342,27965483807401,27965486875630,27965487288443,27965489520682,27965490184380,27965490568703,27965491408078,27965496119641,27965496619641,
1190,27965499784331,27965499784331,27965481404000,27965494784331,27965500785318,27965503736099,27965504201151,27965506776568,27965507298443,27965507515005,27965508405474,27965513495318,27965514061984,
1200,27965516575320,27965516575320,27965497155000,27965511575320,27965517697349,27965521276151,27965521734797,27965524350474,27965524884536,27965525160578,27965526020891,27965531371203,27965532114484,
121</pre>
122
123<p>
124  Each line of this output represents a frame produced by the app. Each line has a fixed number of
125  columns describing time spent in each stage of the frame-producing pipeline. The next section
126  describes this format in detail, including what each column represents.
127</p>
128
129
130<h4 id="fs-data-format">Framestats data format</h4>
131
132<p>
133  Since the block of data is output in CSV format, it's very straightforward to paste it to your
134  spreadsheet tool of choice, or collect and parse with a script. The following table explains the
135  format of the output data columns. All timestamps are in nanoseconds.
136</p>
137
138<ul>
139  <li>FLAGS
140    <ul>
141      <li>Rows with a ‘0’ for the FLAGS column can have their total frame time computed by
142      subtracting the INTENDED_VSYNC column from the FRAME_COMPLETED column.
143      </li>
144
145      <li>If this is non-zero the row should be ignored, as the frame has been determined as being
146      an outlier from normal performance, where it is expected that layout &amp; draw take longer
147      than 16ms. Here are a few reasons this could occur:
148        <ul>
149          <li>The window layout changed (such as the first frame of the application or after a
150          rotation)
151          </li>
152
153          <li>It is also possible the frame was skipped in which case some of the values will have
154          garbage timestamps. A frame can be skipped if for example it is out-running 60fps or if
155          nothing on-screen ended up being dirty, this is not necessarily a sign of a problem in
156          the app.
157          </li>
158        </ul>
159      </li>
160    </ul>
161  </li>
162
163  <li>INTENDED_VSYNC
164    <ul>
165      <li>The intended start point for the frame. If this value is different from VSYNC, there
166      was work occurring on the UI thread that prevented it from responding to the vsync signal
167      in a timely fashion.
168      </li>
169    </ul>
170  </li>
171
172  <li>VSYNC
173    <ul>
174      <li>The time value that was used in all the vsync listeners and drawing for the frame
175      (Choreographer frame callbacks, animations, View.getDrawingTime(), etc…)
176      </li>
177
178      <li>To understand more about VSYNC and how it influences your application, check out the
179      <a href=
180      "https://www.youtube.com/watch?v=1iaHxmfZGGc&amp;list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&amp;index=23">
181        Understanding VSYNC</a> video.
182      </li>
183    </ul>
184  </li>
185
186  <li>OLDEST_INPUT_EVENT
187    <ul>
188      <li>The timestamp of the oldest input event in the input queue, or Long.MAX_VALUE if
189      there were no input events for the frame.
190      </li>
191
192      <li>This value is primarily intended for platform work and has limited usefulness to app
193      developers.
194      </li>
195    </ul>
196  </li>
197
198  <li>NEWEST_INPUT_EVENT
199    <ul>
200      <li>The timestamp of the newest input event in the input queue, or 0 if there were no
201      input events for the frame.
202      </li>
203
204      <li>This value is primarily intended for platform work and has limited usefulness to app
205      developers.
206      </li>
207
208      <li>However it’s possible to get a rough idea of how much latency the app is adding by
209      looking at (FRAME_COMPLETED - NEWEST_INPUT_EVENT).
210      </li>
211    </ul>
212  </li>
213
214  <li>HANDLE_INPUT_START
215    <ul>
216      <li>The timestamp at which input events were dispatched to the application.
217      </li>
218
219      <li>By looking at the time between this and ANIMATION_START it is possible to measure how
220      long the application spent handling input events.
221      </li>
222
223      <li>If this number is high (&gt;2ms), this indicates the app is spending an unusually
224      long time processing input events, such as View.onTouchEvent(), which may indicate this
225      work needs to be optimized, or offloaded to a different thread. Note that there are some
226      scenarios, such as click events that launch new activities or similar, where it is
227      expected and acceptable that this number is large.
228      </li>
229    </ul>
230  </li>
231
232  <li>ANIMATION_START
233    <ul>
234      <li>The timestamp at which animations registered with Choreographer were run.
235      </li>
236
237      <li>By looking at the time between this and PERFORM_TRANVERSALS_START it is possible to
238      determine how long it took to evaluate all the animators (ObjectAnimator,
239      ViewPropertyAnimator, and Transitions being the common ones) that are running.
240      </li>
241
242      <li>If this number is high (&gt;2ms), check to see if your app has written any custom
243      animators or what fields ObjectAnimators are animating and ensure they are appropriate
244      for an animation.
245      </li>
246
247      <li>To learn more about Choreographer, check out the <a href=
248      "https://www.youtube.com/watch?v=Q8m9sHdyXnE">For Butter or Worse</a>
249      video.
250      </li>
251    </ul>
252  </li>
253
254  <li>PERFORM_TRAVERSALS_START
255    <ul>
256      <li>If you subtract out DRAW_START from this value, you can extract how long the layout
257      &amp; measure phases took to complete. (note, during a scroll, or animation, you would
258      hope this should be close to zero..)
259      </li>
260
261      <li>To learn more about the measure &amp; layout phases of the rendering pipeline, check
262      out the <a href=
263      "https://www.youtube.com/watch?v=we6poP0kw6E&amp;list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&amp;index=27">
264        Invalidations, Layouts and Performance</a> video
265      </li>
266    </ul>
267  </li>
268
269  <li>DRAW_START
270    <ul>
271      <li>The time at which the draw phase of performTraversals started. This is the start
272      point of recording the display lists of any views that were invalidated.
273      </li>
274
275      <li>The time between this and SYNC_START is how long it took to call View.draw() on all
276      the invalidated views in the tree.
277      </li>
278
279      <li>For more information on the drawing model, see <a href=
280      "{@docRoot}guide/topics/graphics/hardware-accel.html#hardware-model">Hardware Acceleration</a>
281      or the <a href=
282      "https://www.youtube.com/watch?v=we6poP0kw6E&amp;list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&amp;index=27">
283        Invalidations, Layouts and Performance</a> video
284      </li>
285    </ul>
286  </li>
287
288  <li>SYNC_QUEUED
289    <ul>
290      <li>The time at which a sync request was sent to the RenderThread.
291      </li>
292
293      <li>This marks the point at which a message to start the sync
294      phase was sent to the RenderThread. If the time between this and
295      SYNC_START is substantial (&gt;0.1ms or so), it means that
296      the RenderThread was busy working on a different frame. Internally
297      this is used to differentiate between the frame doing too much work
298      and exceeding the 16ms budget and the frame being stalled due to
299      the previous frame exceeding the 16ms budget.
300      </li>
301    </ul>
302  </li>
303
304  <li>SYNC_START
305    <ul>
306      <li>The time at which the sync phase of the drawing started.
307      </li>
308
309      <li>If the time between this and ISSUE_DRAW_COMMANDS_START is substantial (&gt;0.4ms or
310      so), it typically indicates a lot of new Bitmaps were drawn which must be uploaded to the
311      GPU.
312      </li>
313
314      <li>To understand more about the sync phase, check out the <a href=
315      "https://www.youtube.com/watch?v=VzYkVL1n4M8&amp;index=24&amp;list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu">
316        Profile GPU Rendering</a> video
317      </li>
318    </ul>
319  </li>
320
321  <li>ISSUE_DRAW_COMMANDS_START
322    <ul>
323      <li>The time at which the hardware renderer started issuing drawing commands to the GPU.
324      </li>
325
326      <li>The time between this and FRAME_COMPLETED gives a rough idea of how much GPU work the
327      app is producing. Problems like too much overdraw or inefficient rendering effects show
328      up here.
329      </li>
330    </ul>
331  </li>
332
333  <li>SWAP_BUFFERS
334    <ul>
335      <li>The time at which eglSwapBuffers was called, relatively uninteresting outside of
336      platform work.
337      </li>
338    </ul>
339  </li>
340
341  <li>FRAME_COMPLETED
342    <ul>
343      <li>All done! The total time spent working on this frame can be computed by doing
344      FRAME_COMPLETED - INTENDED_VSYNC.
345      </li>
346    </ul>
347  </li>
348
349</ul>
350
351<p>
352  You can use this data in different ways. One simple but useful visualization is a
353  histogram showing the distribution of frames times (FRAME_COMPLETED - INTENDED_VSYNC) in
354  different latency buckets, see figure below. This graph tells us at a glance that most
355  frames were very good - well below the 16ms deadline (depicted in red), but a few frames
356  were significantly over the deadline. We can look at changes in this histogram over time
357  to see wholesale shifts or new outliers being created. You can also graph input latency,
358  time spent in layout, or other similar interesting metrics based on the many timestamps
359  in the data.
360</p>
361
362<img src="{@docRoot}images/performance-testing/perf-test-framestats.png">
363
364
365<h3 id="timing-dump">Simple frame timing dump</h3>
366
367<p>
368  If <strong>Profile GPU rendering</strong> is set to <strong>In adb shell dumpsys gfxinfo</strong>
369  in Developer Options, the <code>adb shell dumpsys gfxinfo</code> command prints out timing
370  information for the most recent 120 frames, broken into a few different categories with
371  tab-separated-values. This data can be useful for indicating which parts of the drawing pipeline
372  may be slow at a high level.
373</p>
374
375<p>
376  Similar to <a href="#fs-data-format">framestats</a> above, it's very
377  straightforward to paste it to your spreadsheet tool of choice, or collect and parse with
378  a script. The following graph shows a breakdown of where many frames produced by the app
379  were spending their time.
380</p>
381
382<img src="{@docRoot}images/performance-testing/perf-test-frame-latency.png">
383
384<p>
385  The result of running gfxinfo, copying the output, pasting it into a spreadsheet
386  application, and graphing the data as stacked bars.
387</p>
388
389<p>
390  Each vertical bar represents one frame of animation; its height represents the number of
391  milliseconds it took to compute that frame of animation. Each colored segment of the bar
392  represents a different stage of the rendering pipeline, so that you can see what parts of
393  your application may be creating a bottleneck. For more information on understanding the
394  rendering pipeline, and how to optimize for it, see the <a href=
395  "https://www.youtube.com/watch?v=we6poP0kw6E&amp;index=27&amp;list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE">
396  Invalidations Layouts and Performance</a> video.
397</p>
398
399
400<h3 id="collection-window">Controlling the window of stat collection</h3>
401
402<p>
403  Both the framestats and simple frame timings gather data over a very short window - about
404  two seconds worth of rendering. In order to precisely control this window of time - for
405  example, to constrain the data to a particular animation - you can reset all counters,
406  and aggregate statistics gathered.
407</p>
408
409<pre>
410&gt;adb shell dumpsys gfxinfo &lt;PACKAGE_NAME&gt; reset
411</pre>
412
413<p>
414  This can also be used in conjunction with the dumping commands themselves to collect and
415  reset at a regular cadence, capturing less-than-two-second windows of frames
416  continuously.
417</p>
418
419
420<h3 id="diagnose">Diagnosing performance regressions</h3>
421
422<p>
423  Identification of regressions is a good first step to tracking down problems, and
424  maintaining high application health. However, dumpsys just identifies the existence and
425  relative severity of problems. You still need to diagnose the particular cause of the
426  performance problems, and find appropriate ways to fix them. For that, it’s highly
427  recommended to use the <a href="{@docRoot}tools/help/systrace.html">systrace</a> tool.
428</p>
429
430
431<h3 id="resources">Additional resources</h3>
432
433<p>
434  For more information on how Android’s rendering pipeline works, common problems that you
435  can find there, and how to fix them, some of the following resources may be useful to
436  you:
437</p>
438
439<ul>
440  <li>
441    <a class="external-link" href="https://www.youtube.com/watch?v=HXQhu6qfTVU">
442      Rendering Performance 101</a>
443  </li>
444  <li>
445    <a class="external-link" href="https://www.youtube.com/watch?v=CaMTIgxCSqU">
446      Why 60fps?</a>
447  </li>
448  <li>
449    <a class="external-link" href="https://www.youtube.com/watch?v=WH9AFhgwmDw">
450      Android, UI, and the GPU</a>
451  </li>
452  <li>
453    <a class="external-link" href="https://www.youtube.com/watch?v=we6poP0kw6E">
454      Invalidations, Layouts, and Performance</a>
455  </li>
456  <li>
457    <a href="{@docRoot}studio/profile/systrace.html">
458      Analyzing UI Performance with Systrace</a>
459  </li>
460</ul>
461
462
463<h2 id="automate">Automating UI Perfomance Tests</h2>
464
465<p>
466  One approach to UI Performance testing is to simply have a human tester perform a set of
467  user operations on the target app, and either visually look for jank, or spend an very
468  large amount of time using a tool-driven approach to find it. But this manual approach is
469  fraught with peril - human ability to perceive frame rate changes varies tremendously,
470  and this is also time consuming, tedious, and error prone.
471</p>
472
473<p>
474  A more efficient approach is to log and analyze key performance metrics from automated UI
475  tests. The Android M developer preview includes new logging capabilities which make it
476  easy to determine the amount and severity of jank in your application’s animations, and
477  that can be used to build a rigorous process to determine your current performance and
478  track future performance objectives.
479</p>
480
481<p>
482  This article walks you through a recommended approach to using that data to automate your
483  performance testing.
484</p>
485
486<p>
487  This is mostly broken down into two key actions. Firstly, identifying what you're
488  testing, and how you’re testing it. and Secondly, setting up, and maintaining an
489  automated testing environment.
490</p>
491
492
493<h3 id="ui-tests">Setting up UI tests</h3>
494
495<p>
496  Before you can get started with automated testing, it’s important to determine a few high
497  level decisions, in order to properly understand your test space, and needs you may have.
498</p>
499
500<h4>
501  Identify key animations / flows to test
502</h4>
503
504<p>
505  Remember that bad performance is most visible to users when it interrupts a smooth
506  animation. As such, when identifying what types of UI actions to test for, it’s useful to
507  focus on the key animations that users see most, or are most important to their
508  experience. For example, here are some common scenarios that may be useful to identify:
509</p>
510
511<ul>
512  <li>Scrolling a primary ListView or RecyclerView
513  </li>
514
515  <li>Animations during async wait cycles
516  </li>
517
518  <li>Any animation that may have bitmap loading / manipulation in it
519  </li>
520
521  <li>Animations including Alpha Blending
522  </li>
523
524  <li>Custom View drawing with Canvas
525  </li>
526</ul>
527
528<p>
529  Work with engineers, designers, and product managers on your team to prioritize these key
530  product animations for test coverage.
531</p>
532
533<h4>
534  Define your future objectives and track against them
535</h4>
536
537<p>
538  From a high-level, it may be critical to identify your specific performance goals, and
539  focus on writing tests, and collecting data around them. For example:
540</p>
541
542<ul>
543  <li>Do you just want to begin tracking UI performance for the first time to learn more?
544  </li>
545
546  <li>Do you want to prevent regressions that might be introduced in the future?
547  </li>
548
549  <li>Are you at 90% of smooth frames today and want to get to 98% this quarter?
550  </li>
551
552  <li>Are you at 98% smooth frames and don’t want to regress?
553  </li>
554
555  <li>Is your goal to improve performance on low end devices?
556  </li>
557</ul>
558
559<p>
560  In all of these cases, you’ll want historical tracking which shows performance across
561  multiple versions of your application.
562</p>
563
564<h4>
565  Identify devices to test on
566</h4>
567
568<p>
569  Application performance varies depending on the device it's running on. Some devices may
570  contain less memory, less powerful GPUs, or slower CPU chips. This means that animations
571  which may perform well on one set of hardware, may not on others, and worse, may be a
572  result of a bottleneck in a different part of the pipeline. So, to account for this
573  variation in what a user might see, pick a range of devices to execute tests on, both
574  current high end devices, low end devices, tablets, etc. Look for variation in CPU
575  performance, RAM, screen density, size, and so on. Tests that pass on a high end device
576  may fail on a low end device.
577</p>
578
579<h4>
580  Basic frameworks for UI Testing
581</h4>
582
583<p>
584  Tool suites, like <a href=
585  "{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a> and
586  <a href="{@docRoot}training/testing/ui-testing/espresso-testing.html">Espresso</a>, are
587  built to help automate the action of a user moving through your application. These are simple
588  frameworks which mimic user interaction with your device. To use these frameworks, you
589  effectively create unique scripts, which run through a set of user-actions, and play them
590  out on the device itself.
591</p>
592
593<p>
594  By combining these automated tests, alongside <code>dumpsys gfxinfo</code> you can quickly
595  create a reproducible system that allows you to execute a test, and measure the
596  performance information of that particular condition.
597</p>
598
599
600<h3 id="automated-tests">Setting up automated UI testing</h3>
601
602<p>
603  Once you have the ability to execute a UI test, and a pipeline to gather the data from a
604  single test, the next important step is to embrace a framework which can execute that
605  test multiple times, across multiple devices, and aggregate the resulting performance
606  data for further analysis by your development team.
607</p>
608
609<h4>
610  A framework for test automation
611</h4>
612
613<p>
614  It’s worth noting that UI testing frameworks (like <a href=
615  "{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a>)
616  run on the target device/emulator directly. While performance gathering information done
617  by <em>dumpsys gfxinfo</em> is driven by a host machine, sending commands over ADB. To
618  help bridge the automation of these separate entities, <a href=
619  "{@docRoot}tools/help/monkeyrunner_concepts.html">MonkeyRunner</a> framework was
620  developed; A scripting system that runs on your host machine, which can issue commands to
621  a set of connected devices, as well as receive data from them.
622</p>
623
624<p>
625  Building a set of scripts for proper automation of UI Performance testing, at a minimum,
626  should be able to utilize monkeyRunner to accomplish the following tasks:
627</p>
628
629<ul>
630  <li>Load &amp; Launch a desired APK to a target device, devices, or emulator.
631  </li>
632
633  <li>Launch a UI Automator UI test, and allow it to be executed
634  </li>
635
636  <li>Collect performance information through <em>dumpsys gfxinfo</em><em>.</em>
637  </li>
638
639  <li>Aggregate information and display it back in a useful fashion to the developer.
640  </li>
641</ul>
642
643
644<h3 id="triage">Triaging and fixing observed problems</h3>
645
646<p>
647  Once problem patterns or regressions are identified, the next step is identifying and
648  applying the fix. If your automated test framework preserves precise timing breakdowns
649  for frames, it can help you scrutinize recent suspicious code/layout changes (in the case
650  of regression), or narrow down the part of the system you’re analyzing when you switch to
651  manual investigation. For manual investigation, <a href=
652  "{@docRoot}tools/help/systrace.html">systrace</a> is a great place to start, showing
653  precise timing information about every stage of the rendering pipeline, every thread and
654  core in the system, as well as any custom event markers you define.
655</p>
656
657<h4>
658  Properly profiling temporal timings
659</h4>
660
661<p>
662  It is important to note the difficulties in obtaining and measuring timings that come from
663  rendering performance. These numbers are, by nature, non deterministic, and often
664  fluctuate depending on the state of the system, amount of memory available, thermal
665  throttling, and the last time a sun flare hit your area of the earth. The point is that
666  you can run the same test, twice and get slightly different numbers that may be close to
667  each other, but not exact.
668</p>
669
670<p>
671  Properly gathering and profiling data in this manner means running the same test,
672  multiple times, and accumulating the results as an average, or median value. (for the
673  sake of simplicity, let’s call this a ‘batch’) This gives you the rough approximation of
674  the performance of the test, while not needing exact timings.
675</p>
676
677<p>
678  Batches can be used between code changes to see the relative impact of those changes on
679  performance. If the average frame rate for the pre-change Batch is larger than the
680  post-change batch, then you generally have an overall win wrt performance for that
681  particular change.
682</p>
683
684<p>
685  This means that any Automated UI testing you do should take this concept into
686  consideration, and also account for any anomalies that might occur during a test. For
687  example, if your application performance suddenly dips, due to some device issue (that
688  isn’t caused by your application) then you may want to re-run the batch in order to get
689  less chaotic timings.
690</p>
691
692<p>
693  So, how many times should you run a test, before the measurements become meaningful? 10
694  times should be the minimum, with higher numbers like 50 or 100 yielding more accurate
695  results (of course, you’re now trading off time for accuracy)
696</p>
697