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&index=25&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> adb shell dumpsys gfxinfo <PACKAGE_NAME> 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>adb shell dumpsys gfxinfo <PACKAGE_NAME> 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 <PACKAGE_NAME> 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 & 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&list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&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 (>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 (>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 & 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 & layout phases of the rendering pipeline, check 262 out the <a href= 263 "https://www.youtube.com/watch?v=we6poP0kw6E&list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&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&list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&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 (>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 (>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&index=24&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&index=27&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>adb shell dumpsys gfxinfo <PACKAGE_NAME> 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 & 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