Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(261)

Unified Diff: pkg/analysis_server/test/timing/timing_framework.dart

Issue 625973002: Initial timing framework and test for server (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_server/test/timing/timing_framework.dart
diff --git a/pkg/analysis_server/test/timing/timing_framework.dart b/pkg/analysis_server/test/timing/timing_framework.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c9940a87d81a96feafc1585c162ffa5c1323a6b7
--- /dev/null
+++ b/pkg/analysis_server/test/timing/timing_framework.dart
@@ -0,0 +1,318 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library test.timing;
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:math';
+
+import 'package:path/path.dart';
+
+import '../integration/integration_test_methods.dart';
+import '../integration/integration_tests.dart';
+
+/**
+ * The abstract class [TimingTest] defines the behavior of objects that measure
+ * the time required to perform some sequence of server operations.
+ */
+abstract class TimingTest extends IntegrationTestMixin {
+ /**
+ * The connection to the analysis server.
+ */
+ Server server;
+
+ /**
+ * The temporary directory in which source files can be stored.
+ */
+ Directory sourceDirectory;
+
+ /**
+ * A flag indicating whether the teardown process should skip sending a
+ * "server.shutdown" request because the server is known to have already
+ * shutdown.
+ */
+ bool skipShutdown = false;
+
+ /**
+ * The number of times the test will be performed in order to warm up the VM.
+ */
+ static final int DEFAULT_WARMUP_COUNT = 10;
+
+ /**
+ * The number of times the test will be performed in order to compute a time.
+ */
+ static final int DEFAULT_TIMING_COUNT = 10;
+
+ /**
+ * The file suffix used to identify Dart files.
+ */
+ static final String DART_SUFFIX = '.dart';
+
+ /**
+ * The file suffix used to identify HTML files.
+ */
+ static final String HTML_SUFFIX = '.html';
+
+ /**
+ * The amount of time to give the server to respond to a shutdown request
+ * before forcibly terminating it.
+ */
+ static const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 5);
+
+ /**
+ * Initialize a newly created test.
+ */
+ TimingTest();
+
+ /**
+ * Return the number of iterations that should be performed in order to warm
+ * up the VM.
+ */
+ int get warmupCount => DEFAULT_WARMUP_COUNT;
+
+ /**
+ * Return the number of iterations that should be performed in order to
+ * compute a time.
+ */
+ int get timingCount => DEFAULT_TIMING_COUNT;
+
+ /**
+ * Perform any operations that need to be performed once before any iterations.
+ */
+ Future oneTimeSetUp() {
+ initializeInttestMixin();
+ server = new Server();
+ sourceDirectory = Directory.systemTemp.createTempSync('analysisServer');
+ Completer serverConnected = new Completer();
+ onServerConnected.listen((_) {
+ serverConnected.complete();
+ });
+ return server.start(dispatchNotification).then((params) {
+ server.exitCode.then((_) {
+ skipShutdown = true;
+ });
+ return serverConnected.future;
+ });
+ }
+
+ /**
+ * Perform any operations that need to be performed before each iteration.
+ */
+ Future setUp();
+
+ /**
+ * Perform any operations that part of a single iteration. It is the execution
+ * of this method that will be measured.
+ */
+ Future perform();
+
+ /**
+ * Perform any operations that need to be performed after each iteration.
+ */
+ Future tearDown();
+
+ /**
+ * Perform any operations that need to be performed once after all iterations.
+ */
+ Future oneTimeTearDown() {
+ return _shutdownIfNeeded().then((_) {
+ sourceDirectory.deleteSync(recursive: true);
+ });
+ }
+
+ /**
+ * Return a future that will complete with a timing result representing the
+ * number of milliseconds required to perform the operation the specified
+ * number of times.
+ */
+ Future<TimingResult> run() {
+ List<int> times = new List<int>();
+ return oneTimeSetUp().then((_) {
+ return _repeat(warmupCount, null).then((_) {
+ return _repeat(timingCount, times).then((_) {
+ return oneTimeTearDown().then((_) {
+ return new Future.value(new TimingResult(times));
+ });
+ });
+ });
+ });
+ }
+
+ /**
+ * Convert the given [relativePath] to an absolute path, by interpreting it
+ * relative to [sourceDirectory]. On Windows any forward slashes in
+ * [relativePath] are converted to backslashes.
+ */
+ String sourcePath(String relativePath) {
+ return join(sourceDirectory.path, relativePath.replaceAll('/', separator));
+ }
+
+ /**
+ * Write a source file with the given absolute [pathname] and [contents].
+ *
+ * If the file didn't previously exist, it is created. If it did, it is
+ * overwritten.
+ *
+ * Parent directories are created as necessary.
+ */
+ void writeFile(String pathname, String contents) {
+ new Directory(dirname(pathname)).createSync(recursive: true);
+ new File(pathname).writeAsStringSync(contents);
+ }
+
+ /**
+ * Return the number of nanoseconds that have elapsed since the given
+ * [stopwatch] was last stopped.
+ */
+ int _elapsedNanoseconds(Stopwatch stopwatch) {
+ return (stopwatch.elapsedTicks * 1000000000) ~/ stopwatch.frequency;
+ }
+
+ /**
+ * Repeatedly execute this test [count] times, adding timing information to
+ * the given list of [times] if it is non-`null`.
+ */
+ Future _repeat(int count, List<int> times) {
+ Stopwatch stopwatch = new Stopwatch();
+ return setUp().then((_) {
+ 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
+ return perform().then((_) {
+ stopwatch.stop();
+ if (times != null) {
+ times.add(_elapsedNanoseconds(stopwatch));
+ }
+ return tearDown().then((_) {
+ if (count > 0) {
+ return _repeat(count - 1, times);
+ } else {
+ return new Future.value();
+ }
+ });
+ });
+ });
+ }
+
+ /**
+ * Shut the server down unless [skipShutdown] is `true`.
+ */
+ Future _shutdownIfNeeded() {
+ if (skipShutdown) {
+ return new Future.value();
+ }
+ // Give the server a short time to comply with the shutdown request; if it
+ // doesn't exit, then forcibly terminate it.
+ Completer processExited = new Completer();
+ sendServerShutdown();
+ return server.exitCode.timeout(SHUTDOWN_TIMEOUT, onTimeout: () {
+ return server.kill();
+ });
+ }
+}
+
+/**
+ * Instances of the class [TimingResult] represent the timing information
+ * gathered while executing a given timing test.
+ */
+class TimingResult {
+ /**
+ * The amount of time spent executing each test, in nanoseconds.
+ */
+ List<int> times;
+
+ /**
+ * The number of nanoseconds in a millisecond.
+ */
+ static int NANOSECONDS_PER_MILLISECOND = 1000000;
+
+ /**
+ * Initialize a newly created timing result.
+ */
+ TimingResult(this.times);
+
+ /**
+ * The average amount of time spent executing a single iteration, in
+ * milliseconds.
+ */
+ int get averageTime {
+ return totalTime ~/ times.length;
+ }
+
+ /**
+ * The maximum amount of time spent executing a single iteration, in
+ * milliseconds.
+ */
+ int get maxTime {
+ int maxTime = 0;
+ int count = times.length;
+ for (int i = 0; i < count; i++) {
+ maxTime = max(maxTime, times[i]);
+ }
+ return maxTime ~/ NANOSECONDS_PER_MILLISECOND;
+ }
+
+ /**
+ * The minimum amount of time spent executing a single iteration, in
+ * milliseconds.
+ */
+ int get minTime {
+ 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
+ int count = times.length;
+ for (int i = 0; i < count; i++) {
+ minTime = min(minTime, times[i]);
+ }
+ return minTime ~/ NANOSECONDS_PER_MILLISECOND;
+ }
+
+ /**
+ * The standard deviation of the times.
+ */
+ double get standardDeviation {
+ return computeStandardDeviation(toMilliseconds(times));
+ }
+
+ /**
+ * The total amount of time spent executing the test, in milliseconds.
+ */
+ int get totalTime {
+ int totalTime = 0;
+ int count = times.length;
+ for (int i = 0; i < count; i++) {
+ totalTime += times[i];
+ }
+ return totalTime ~/ NANOSECONDS_PER_MILLISECOND;
+ }
+
+ /**
+ * Compute the standard deviation of the given set of [values].
+ */
+ double computeStandardDeviation(List<int> values) {
+ int count = values.length;
+ double sumOfValues = 0.0;
+ for (int i = 0; i < count; i++) {
+ sumOfValues += values[i];
+ }
+ double average = sumOfValues / count;
+ double sumOfDiffSquared = 0.0;
+ for (int i = 0; i < count; i++) {
+ double diff = values[i] - average;
+ sumOfDiffSquared += diff * diff;
+ }
+ // If this were a sample we would divide by (count - 1).
+ 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
+ }
+
+ /**
+ * Convert the given [times], expressed in nanoseconds, to times expressed in
+ * milliseconds.
+ */
+ List<int> toMilliseconds(List<int> times) {
+ int count = times.length;
+ List<int> convertedValues = new List<int>();
+ for (int i = 0; i < count; i++) {
+ convertedValues.add(times[i] ~/ NANOSECONDS_PER_MILLISECOND);
+ }
+ return convertedValues;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698