Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library test.timing; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:io'; | |
| 9 import 'dart:math'; | |
| 10 | |
| 11 import 'package:path/path.dart'; | |
| 12 | |
| 13 import '../integration/integration_test_methods.dart'; | |
| 14 import '../integration/integration_tests.dart'; | |
| 15 | |
| 16 /** | |
| 17 * The abstract class [TimingTest] defines the behavior of objects that measure | |
| 18 * the time required to perform some sequence of server operations. | |
| 19 */ | |
| 20 abstract class TimingTest extends IntegrationTestMixin { | |
| 21 /** | |
| 22 * The connection to the analysis server. | |
| 23 */ | |
| 24 Server server; | |
| 25 | |
| 26 /** | |
| 27 * The temporary directory in which source files can be stored. | |
| 28 */ | |
| 29 Directory sourceDirectory; | |
| 30 | |
| 31 /** | |
| 32 * A flag indicating whether the teardown process should skip sending a | |
| 33 * "server.shutdown" request because the server is known to have already | |
| 34 * shutdown. | |
| 35 */ | |
| 36 bool skipShutdown = false; | |
| 37 | |
| 38 /** | |
| 39 * The number of times the test will be performed in order to warm up the VM. | |
| 40 */ | |
| 41 static final int DEFAULT_WARMUP_COUNT = 10; | |
| 42 | |
| 43 /** | |
| 44 * The number of times the test will be performed in order to compute a time. | |
| 45 */ | |
| 46 static final int DEFAULT_TIMING_COUNT = 10; | |
| 47 | |
| 48 /** | |
| 49 * The file suffix used to identify Dart files. | |
| 50 */ | |
| 51 static final String DART_SUFFIX = '.dart'; | |
| 52 | |
| 53 /** | |
| 54 * The file suffix used to identify HTML files. | |
| 55 */ | |
| 56 static final String HTML_SUFFIX = '.html'; | |
| 57 | |
| 58 /** | |
| 59 * The amount of time to give the server to respond to a shutdown request | |
| 60 * before forcibly terminating it. | |
| 61 */ | |
| 62 static const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 5); | |
| 63 | |
| 64 /** | |
| 65 * Initialize a newly created test. | |
| 66 */ | |
| 67 TimingTest(); | |
| 68 | |
| 69 /** | |
| 70 * Return the number of iterations that should be performed in order to warm | |
| 71 * up the VM. | |
| 72 */ | |
| 73 int get warmupCount => DEFAULT_WARMUP_COUNT; | |
| 74 | |
| 75 /** | |
| 76 * Return the number of iterations that should be performed in order to | |
| 77 * compute a time. | |
| 78 */ | |
| 79 int get timingCount => DEFAULT_TIMING_COUNT; | |
| 80 | |
| 81 /** | |
| 82 * Perform any operations that need to be performed once before any iterations . | |
| 83 */ | |
| 84 Future oneTimeSetUp() { | |
| 85 initializeInttestMixin(); | |
| 86 server = new Server(); | |
| 87 sourceDirectory = Directory.systemTemp.createTempSync('analysisServer'); | |
| 88 Completer serverConnected = new Completer(); | |
| 89 onServerConnected.listen((_) { | |
| 90 serverConnected.complete(); | |
| 91 }); | |
| 92 return server.start(dispatchNotification).then((params) { | |
| 93 server.exitCode.then((_) { | |
| 94 skipShutdown = true; | |
| 95 }); | |
| 96 return serverConnected.future; | |
| 97 }); | |
| 98 } | |
| 99 | |
| 100 /** | |
| 101 * Perform any operations that need to be performed before each iteration. | |
| 102 */ | |
| 103 Future setUp(); | |
| 104 | |
| 105 /** | |
| 106 * Perform any operations that part of a single iteration. It is the execution | |
| 107 * of this method that will be measured. | |
| 108 */ | |
| 109 Future perform(); | |
| 110 | |
| 111 /** | |
| 112 * Perform any operations that need to be performed after each iteration. | |
| 113 */ | |
| 114 Future tearDown(); | |
| 115 | |
| 116 /** | |
| 117 * Perform any operations that need to be performed once after all iterations. | |
| 118 */ | |
| 119 Future oneTimeTearDown() { | |
| 120 return _shutdownIfNeeded().then((_) { | |
| 121 sourceDirectory.deleteSync(recursive: true); | |
| 122 }); | |
| 123 } | |
| 124 | |
| 125 /** | |
| 126 * Return a future that will complete with a timing result representing the | |
| 127 * number of milliseconds required to perform the operation the specified | |
| 128 * number of times. | |
| 129 */ | |
| 130 Future<TimingResult> run() { | |
| 131 List<int> times = new List<int>(); | |
| 132 return oneTimeSetUp().then((_) { | |
| 133 return _repeat(warmupCount, null).then((_) { | |
| 134 return _repeat(timingCount, times).then((_) { | |
| 135 return oneTimeTearDown().then((_) { | |
| 136 return new Future.value(new TimingResult(times)); | |
| 137 }); | |
| 138 }); | |
| 139 }); | |
| 140 }); | |
| 141 } | |
| 142 | |
| 143 /** | |
| 144 * Convert the given [relativePath] to an absolute path, by interpreting it | |
| 145 * relative to [sourceDirectory]. On Windows any forward slashes in | |
| 146 * [relativePath] are converted to backslashes. | |
| 147 */ | |
| 148 String sourcePath(String relativePath) { | |
| 149 return join(sourceDirectory.path, relativePath.replaceAll('/', separator)); | |
| 150 } | |
| 151 | |
| 152 /** | |
| 153 * Write a source file with the given absolute [pathname] and [contents]. | |
| 154 * | |
| 155 * If the file didn't previously exist, it is created. If it did, it is | |
| 156 * overwritten. | |
| 157 * | |
| 158 * Parent directories are created as necessary. | |
| 159 */ | |
| 160 void writeFile(String pathname, String contents) { | |
| 161 new Directory(dirname(pathname)).createSync(recursive: true); | |
| 162 new File(pathname).writeAsStringSync(contents); | |
| 163 } | |
| 164 | |
| 165 /** | |
| 166 * Return the number of nanoseconds that have elapsed since the given | |
| 167 * [stopwatch] was last stopped. | |
| 168 */ | |
| 169 int _elapsedNanoseconds(Stopwatch stopwatch) { | |
| 170 return (stopwatch.elapsedTicks * 1000000000) ~/ stopwatch.frequency; | |
| 171 } | |
| 172 | |
| 173 /** | |
| 174 * Repeatedly execute this test [count] times, adding timing information to | |
| 175 * the given list of [times] if it is non-`null`. | |
| 176 */ | |
| 177 Future _repeat(int count, List<int> times) { | |
| 178 Stopwatch stopwatch = new Stopwatch(); | |
| 179 return setUp().then((_) { | |
| 180 stopwatch..reset..start(); | |
|
scheglov
2014/10/03 16:46:14
..reset() ?
Brian Wilkerson
2014/10/03 17:17:37
Left over from an earlier timing framework that I
| |
| 181 return perform().then((_) { | |
| 182 stopwatch.stop(); | |
| 183 if (times != null) { | |
| 184 times.add(_elapsedNanoseconds(stopwatch)); | |
| 185 } | |
| 186 return tearDown().then((_) { | |
| 187 if (count > 0) { | |
| 188 return _repeat(count - 1, times); | |
| 189 } else { | |
| 190 return new Future.value(); | |
| 191 } | |
| 192 }); | |
| 193 }); | |
| 194 }); | |
| 195 } | |
| 196 | |
| 197 /** | |
| 198 * Shut the server down unless [skipShutdown] is `true`. | |
| 199 */ | |
| 200 Future _shutdownIfNeeded() { | |
| 201 if (skipShutdown) { | |
| 202 return new Future.value(); | |
| 203 } | |
| 204 // Give the server a short time to comply with the shutdown request; if it | |
| 205 // doesn't exit, then forcibly terminate it. | |
| 206 Completer processExited = new Completer(); | |
| 207 sendServerShutdown(); | |
| 208 return server.exitCode.timeout(SHUTDOWN_TIMEOUT, onTimeout: () { | |
| 209 return server.kill(); | |
| 210 }); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 /** | |
| 215 * Instances of the class [TimingResult] represent the timing information | |
| 216 * gathered while executing a given timing test. | |
| 217 */ | |
| 218 class TimingResult { | |
| 219 /** | |
| 220 * The amount of time spent executing each test, in nanoseconds. | |
| 221 */ | |
| 222 List<int> times; | |
| 223 | |
| 224 /** | |
| 225 * The number of nanoseconds in a millisecond. | |
| 226 */ | |
| 227 static int NANOSECONDS_PER_MILLISECOND = 1000000; | |
| 228 | |
| 229 /** | |
| 230 * Initialize a newly created timing result. | |
| 231 */ | |
| 232 TimingResult(this.times); | |
| 233 | |
| 234 /** | |
| 235 * The average amount of time spent executing a single iteration, in | |
| 236 * milliseconds. | |
| 237 */ | |
| 238 int get averageTime { | |
| 239 return totalTime ~/ times.length; | |
| 240 } | |
| 241 | |
| 242 /** | |
| 243 * The maximum amount of time spent executing a single iteration, in | |
| 244 * milliseconds. | |
| 245 */ | |
| 246 int get maxTime { | |
| 247 int maxTime = 0; | |
| 248 int count = times.length; | |
| 249 for (int i = 0; i < count; i++) { | |
| 250 maxTime = max(maxTime, times[i]); | |
| 251 } | |
| 252 return maxTime ~/ NANOSECONDS_PER_MILLISECOND; | |
| 253 } | |
| 254 | |
| 255 /** | |
| 256 * The minimum amount of time spent executing a single iteration, in | |
| 257 * milliseconds. | |
| 258 */ | |
| 259 int get minTime { | |
| 260 int minTime = 0xFFFFFFFF; | |
|
Paul Berry
2014/10/03 16:58:35
0xFFFFFFFF nanoseconds ~= 4 seconds. I don't thin
Brian Wilkerson
2014/10/03 17:17:37
Done
| |
| 261 int count = times.length; | |
| 262 for (int i = 0; i < count; i++) { | |
| 263 minTime = min(minTime, times[i]); | |
| 264 } | |
| 265 return minTime ~/ NANOSECONDS_PER_MILLISECOND; | |
| 266 } | |
| 267 | |
| 268 /** | |
| 269 * The standard deviation of the times. | |
| 270 */ | |
| 271 double get standardDeviation { | |
| 272 return computeStandardDeviation(toMilliseconds(times)); | |
| 273 } | |
| 274 | |
| 275 /** | |
| 276 * The total amount of time spent executing the test, in milliseconds. | |
| 277 */ | |
| 278 int get totalTime { | |
| 279 int totalTime = 0; | |
| 280 int count = times.length; | |
| 281 for (int i = 0; i < count; i++) { | |
| 282 totalTime += times[i]; | |
| 283 } | |
| 284 return totalTime ~/ NANOSECONDS_PER_MILLISECOND; | |
| 285 } | |
| 286 | |
| 287 /** | |
| 288 * Compute the standard deviation of the given set of [values]. | |
| 289 */ | |
| 290 double computeStandardDeviation(List<int> values) { | |
| 291 int count = values.length; | |
| 292 double sumOfValues = 0.0; | |
| 293 for (int i = 0; i < count; i++) { | |
| 294 sumOfValues += values[i]; | |
| 295 } | |
| 296 double average = sumOfValues / count; | |
| 297 double sumOfDiffSquared = 0.0; | |
| 298 for (int i = 0; i < count; i++) { | |
| 299 double diff = values[i] - average; | |
| 300 sumOfDiffSquared += diff * diff; | |
| 301 } | |
| 302 // If this were a sample we would divide by (count - 1). | |
| 303 return sqrt((sumOfDiffSquared / count)); | |
|
Paul Berry
2014/10/03 16:58:35
Using (count - 1) is correct in this circumstance.
Brian Wilkerson
2014/10/03 17:17:37
Done
| |
| 304 } | |
| 305 | |
| 306 /** | |
| 307 * Convert the given [times], expressed in nanoseconds, to times expressed in | |
| 308 * milliseconds. | |
| 309 */ | |
| 310 List<int> toMilliseconds(List<int> times) { | |
| 311 int count = times.length; | |
| 312 List<int> convertedValues = new List<int>(); | |
| 313 for (int i = 0; i < count; i++) { | |
| 314 convertedValues.add(times[i] ~/ NANOSECONDS_PER_MILLISECOND); | |
| 315 } | |
| 316 return convertedValues; | |
| 317 } | |
| 318 } | |
| OLD | NEW |