Index: client/tests/client/samples/smoketest/BenchmarkBase.dart |
=================================================================== |
--- client/tests/client/samples/smoketest/BenchmarkBase.dart (revision 0) |
+++ client/tests/client/samples/smoketest/BenchmarkBase.dart (revision 0) |
@@ -0,0 +1,197 @@ |
+// Copyright (c) 2011, 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. |
+ |
+/** Accessors for our Singleton variables. */ |
+BenchmarkSuite get BENCHMARK_SUITE() { |
+ if (BenchmarkSuite._ONLY == null) { |
+ BenchmarkSuite._ONLY = new BenchmarkSuite._internal(); |
+ } |
+ return BenchmarkSuite._ONLY; |
+} |
+ |
+BenchmarkView get BENCHMARK_VIEW() { |
+ if (BenchmarkView._ONLY == null) { |
+ BenchmarkView._ONLY = new BenchmarkView._internal(); |
+ } |
+ return BenchmarkView._ONLY; |
+} |
+ |
+/** The superclass from which all benchmarks inherit from. */ |
+class BenchmarkBase { |
+ /** Benchmark name. */ |
+ final String name; |
+ |
+ const BenchmarkBase(String name) : this.name = name; |
+ |
+ /** |
+ * The benchmark code. |
+ * This function is not used, if both [warmup] and [exercise] are overwritten. |
+ */ |
+ void run() { } |
+ |
+ /** Runs a short version of the benchmark. By default invokes [run] once. */ |
+ void warmup() { |
+ run(); |
+ } |
+ |
+ /** Exercices the benchmark. By default invokes [run] 10 times. */ |
+ void exercise() { |
+ for (int i = 0; i < 10; i++) { |
+ run(); |
+ } |
+ } |
+ |
+ /** Not measured setup code executed prior to the benchmark runs. */ |
+ void setup() { } |
+ |
+ /** Not measures teardown code executed after the benchark runs. */ |
+ void teardown() { } |
+ |
+ /** |
+ * Measures the score for this benchmark by executing it repeately until |
+ * time minimum has been reached. |
+ */ |
+ static double measureFor(Function f, int timeMinimum) { |
+ int time = 0; |
+ int iter = 0; |
+ Stopwatch watch = new Stopwatch(); |
+ watch.start(); |
+ int elapsed = 0; |
+ while (elapsed < timeMinimum || iter < 32) { |
+ f(); |
+ elapsed = watch.elapsedInMs(); |
+ iter++; |
+ } |
+ return (1000.0 * iter) / elapsed; |
+ } |
+ |
+ /** |
+ * Measures the score for the benchmark and returns it. |
+ * We measure iterations / sec (so bigger = better!). |
+ */ |
+ double measure() { |
+ setup(); |
+ // Warmup for at least 1000ms. Discard result. |
+ measureFor(() { this.warmup(); }, 1000); |
+ // Run the benchmark for at least 1000ms. |
+ double result = measureFor(() { this.exercise(); }, 1000); |
+ teardown(); |
+ return result; |
+ } |
+ |
+ void report() { |
+ num score = measure(); |
+ Map<String, int> normalizingDict = {'Smoketest': 100}; |
+ score = score / normalizingDict[name]; |
+ BENCHMARK_SUITE.updateIndividualScore(name, score); |
+ } |
+} |
+ |
+/** The controller class that runs all of the benchmarks. */ |
+class BenchmarkSuite { |
+ /** The set of benchmarks that have yet to run. */ |
+ List<Function> benchmarks; |
+ |
+ /** |
+ * The set of scores from the benchmarks that have already run. (Used for |
+ * calculating the Geometric mean). |
+ */ |
+ List<num> scores; |
+ |
+ /** The total number of benchmarks we will be running. */ |
+ int totalBenchmarks; |
+ |
+ /** Singleton pattern: There's only one BenchmarkSuite. */ |
+ static BenchmarkSuite _ONLY = null; |
+ |
+ BenchmarkSuite._internal() { |
+ scores = []; |
+ benchmarks = [() => Smoketest.main()]; |
+ totalBenchmarks = benchmarks.length; |
+ } |
+ |
+ /** Run all of the benchmarks that we have in our benchmarks list. */ |
+ runBenchmarks() { |
+ runBenchmarksHelper(benchmarks); |
+ } |
+ |
+ /** |
+ * Run the remaining benchmarks in our list. We chain the calls providing |
+ * little breaks for the main page to gain control, so we don't force the |
+ * entire page to hang the whole time. |
+ */ |
+ runBenchmarksHelper(List<Function> remainingBenchmarks) { |
+ // Remove the last benchmark, and run it. |
+ var benchmark = remainingBenchmarks.removeLast(); |
+ benchmark(); |
+ if (remainingBenchmarks.length > 0) { |
+ /* Provide small breaks between each benchmark, so that the browser |
+ doesn't get unhappy about long running scripts, and so the user |
+ can regain control of the UI to kill the page as needed. */ |
+ window.setTimeout(() => runBenchmarksHelper(remainingBenchmarks), 25); |
+ } else if (remainingBenchmarks.length == 0) { |
+ // We've run all of the benchmarks. Update the page with the score. |
+ BENCHMARK_VIEW.setScore(geometricMean(scores)); |
+ } |
+ } |
+ |
+ /** Store the results of a single benchmark run. */ |
+ updateIndividualScore(String name, num score) { |
+ scores.add(score); |
+ BENCHMARK_VIEW.incrementProgress(name, score, totalBenchmarks); |
+ } |
+ |
+ /** Computes the geometric mean of a set of numbers. */ |
+ geometricMean(numbers) { |
+ num log = 0; |
+ for (num n in numbers) { |
+ log += Math.log(n); |
+ } |
+ return Math.pow(Math.E, log / numbers.length); |
+ } |
+} |
+ |
+/** Controls how results are displayed to the user, by updating the HTML. */ |
+class BenchmarkView { |
+ |
+ /** The number of benchmarks that have finished executing. */ |
+ int numCompleted = 0; |
+ |
+ /** Singleton pattern: There's only one BenchmarkSuite. */ |
+ static BenchmarkView _ONLY = null; |
+ |
+ BenchmarkView._internal(); |
+ |
+ /** Update the page HTML to show the calculated score. */ |
+ setScore(num score) { |
+ String newScore = formatScore(score * 100.0); |
+ Element body = document.queryAll("body")[0]; |
+ body.nodes.add( |
+ new Element.html("<p id='testResultScore'>Score: $newScore</p>")); |
+ } |
+ |
+ /** |
+ * Update the page HTML to show how much progress we've made through the |
+ * benchmarks. |
+ */ |
+ incrementProgress(String name, num score, num totalBenchmarks) { |
+ String newScore = formatScore(score * 100.0); |
+ numCompleted++; |
+ // Slightly incorrect (truncating) percentage, but this is just to show |
+ // the user we're making progress. |
+ num percentage = 100 * numCompleted ~/ totalBenchmarks; |
+ } |
+ |
+ /** |
+ * Rounds the score to have at least three significant digits (hopefully) |
+ * helping readability of the scores. |
+ */ |
+ String formatScore(num value) { |
+ if (value > 100) { |
+ return value.toStringAsFixed(0); |
+ } else { |
+ return value.toStringAsFixed(2); |
+ } |
+ } |
+} |