| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011, 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 /** Accessors for our Singleton variables. */ | |
| 6 BenchmarkSuite get BENCHMARK_SUITE() { | |
| 7 if (BenchmarkSuite._ONLY == null) { | |
| 8 BenchmarkSuite._ONLY = new BenchmarkSuite._internal(); | |
| 9 } | |
| 10 return BenchmarkSuite._ONLY; | |
| 11 } | |
| 12 | |
| 13 BenchmarkView get BENCHMARK_VIEW() { | |
| 14 if (BenchmarkView._ONLY == null) { | |
| 15 BenchmarkView._ONLY = new BenchmarkView._internal(); | |
| 16 } | |
| 17 return BenchmarkView._ONLY; | |
| 18 } | |
| 19 | |
| 20 /** The superclass from which all benchmarks inherit from. */ | |
| 21 class BenchmarkBase { | |
| 22 /** Benchmark name. */ | |
| 23 final String name; | |
| 24 | |
| 25 const BenchmarkBase(String name) : this.name = name; | |
| 26 | |
| 27 /** | |
| 28 * The benchmark code. | |
| 29 * This function is not used, if both [warmup] and [exercise] are overwritten. | |
| 30 */ | |
| 31 void run() { } | |
| 32 | |
| 33 /** Runs a short version of the benchmark. By default invokes [run] once. */ | |
| 34 void warmup() { | |
| 35 run(); | |
| 36 } | |
| 37 | |
| 38 /** Exercices the benchmark. By default invokes [run] 10 times. */ | |
| 39 void exercise() { | |
| 40 for (int i = 0; i < 10; i++) { | |
| 41 run(); | |
| 42 } | |
| 43 } | |
| 44 | |
| 45 /** Not measured setup code executed prior to the benchmark runs. */ | |
| 46 void setup() { } | |
| 47 | |
| 48 /** Not measures teardown code executed after the benchark runs. */ | |
| 49 void teardown() { } | |
| 50 | |
| 51 /** | |
| 52 * Measures the score for this benchmark by executing it repeately until | |
| 53 * time minimum has been reached. | |
| 54 */ | |
| 55 static double measureFor(Function f, int timeMinimum) { | |
| 56 int time = 0; | |
| 57 int iter = 0; | |
| 58 Stopwatch watch = new Stopwatch(); | |
| 59 watch.start(); | |
| 60 int elapsed = 0; | |
| 61 while (elapsed < timeMinimum || iter < 32) { | |
| 62 f(); | |
| 63 elapsed = watch.elapsedInMs(); | |
| 64 iter++; | |
| 65 } | |
| 66 return (1000.0 * iter) / elapsed; | |
| 67 } | |
| 68 | |
| 69 /** | |
| 70 * Measures the score for the benchmark and returns it. | |
| 71 * We measure iterations / sec (so bigger = better!). | |
| 72 */ | |
| 73 double measure() { | |
| 74 setup(); | |
| 75 // Warmup for at least 1000ms. Discard result. | |
| 76 measureFor(() { this.warmup(); }, 1000); | |
| 77 // Run the benchmark for at least 1000ms. | |
| 78 double result = measureFor(() { this.exercise(); }, 1000); | |
| 79 teardown(); | |
| 80 return result; | |
| 81 } | |
| 82 | |
| 83 void report() { | |
| 84 num score = measure(); | |
| 85 Map<String, int> normalizingDict = {'Smoketest': 100}; | |
| 86 window.console.log(name + " " + score.toString()); | |
| 87 score = score / normalizingDict[name]; | |
| 88 BENCHMARK_SUITE.updateIndividualScore(name, score); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 /** The controller class that runs all of the benchmarks. */ | |
| 93 class BenchmarkSuite { | |
| 94 /** The set of benchmarks that have yet to run. */ | |
| 95 List<Function> benchmarks; | |
| 96 | |
| 97 /** | |
| 98 * The set of scores from the benchmarks that have already run. (Used for | |
| 99 * calculating the Geometric mean). | |
| 100 */ | |
| 101 List<num> scores; | |
| 102 | |
| 103 /** The total number of benchmarks we will be running. */ | |
| 104 int totalBenchmarks; | |
| 105 | |
| 106 /** Singleton pattern: There's only one BenchmarkSuite. */ | |
| 107 static BenchmarkSuite _ONLY = null; | |
| 108 | |
| 109 BenchmarkSuite._internal() { | |
| 110 scores = []; | |
| 111 benchmarks = [() => Smoketest.main()]; | |
| 112 totalBenchmarks = benchmarks.length; | |
| 113 } | |
| 114 | |
| 115 /** Run all of the benchmarks that we have in our benchmarks list. */ | |
| 116 runBenchmarks() { | |
| 117 runBenchmarksHelper(benchmarks); | |
| 118 } | |
| 119 | |
| 120 /** | |
| 121 * Run the remaining benchmarks in our list. We chain the calls providing | |
| 122 * little breaks for the main page to gain control, so we don't force the | |
| 123 * entire page to hang the whole time. | |
| 124 */ | |
| 125 runBenchmarksHelper(List<Function> remainingBenchmarks) { | |
| 126 // Remove the last benchmark, and run it. | |
| 127 var benchmark = remainingBenchmarks.removeLast(); | |
| 128 benchmark(); | |
| 129 if (remainingBenchmarks.length > 0) { | |
| 130 /* Provide small breaks between each benchmark, so that the browser | |
| 131 doesn't get unhappy about long running scripts, and so the user | |
| 132 can regain control of the UI to kill the page as needed. */ | |
| 133 window.setTimeout(() => runBenchmarksHelper(remainingBenchmarks), 25); | |
| 134 } else if (remainingBenchmarks.length == 0) { | |
| 135 // We've run all of the benchmarks. Update the page with the score. | |
| 136 BENCHMARK_VIEW.setScore(geometricMean(scores)); | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 /** Store the results of a single benchmark run. */ | |
| 141 updateIndividualScore(String name, num score) { | |
| 142 scores.add(score); | |
| 143 BENCHMARK_VIEW.incrementProgress(name, score, totalBenchmarks); | |
| 144 } | |
| 145 | |
| 146 /** Computes the geometric mean of a set of numbers. */ | |
| 147 geometricMean(numbers) { | |
| 148 num log = 0; | |
| 149 for (num n in numbers) { | |
| 150 log += Math.log(n); | |
| 151 } | |
| 152 return Math.pow(Math.E, log / numbers.length); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 /** Controls how results are displayed to the user, by updating the HTML. */ | |
| 157 class BenchmarkView { | |
| 158 | |
| 159 /** The number of benchmarks that have finished executing. */ | |
| 160 int numCompleted = 0; | |
| 161 | |
| 162 /** Singleton pattern: There's only one BenchmarkSuite. */ | |
| 163 static BenchmarkView _ONLY = null; | |
| 164 | |
| 165 BenchmarkView._internal(); | |
| 166 | |
| 167 /** Update the page HTML to show the calculated score. */ | |
| 168 setScore(num score) { | |
| 169 String newScore = formatScore(score * 100.0); | |
| 170 Element status = document.query("#status"); | |
| 171 status.innerHTML = "Score: $newScore <br>"; | |
| 172 } | |
| 173 | |
| 174 /** | |
| 175 * Update the page HTML to show how much progress we've made through the | |
| 176 * benchmarks. | |
| 177 */ | |
| 178 incrementProgress(String name, num score, num totalBenchmarks) { | |
| 179 String newScore = formatScore(score * 100.0); | |
| 180 Element results = document.query("#results"); | |
| 181 results.innerHTML += "$name: $newScore <br>"; | |
| 182 | |
| 183 Element status = document.query("#status"); | |
| 184 numCompleted++; | |
| 185 // Slightly incorrect (truncating) percentage, but this is just to show | |
| 186 // the user we're making progress. | |
| 187 num percentage = 100 * numCompleted ~/ totalBenchmarks; | |
| 188 status.innerHTML = "Running: $percentage% completed."; | |
| 189 } | |
| 190 | |
| 191 /** | |
| 192 * Rounds the score to have at least three significant digits (hopefully) | |
| 193 * helping readability of the scores. | |
| 194 */ | |
| 195 String formatScore(num value) { | |
| 196 if (value > 100) { | |
| 197 return value.toStringAsFixed(0); | |
| 198 } else { | |
| 199 return value.toStringAsFixed(2); | |
| 200 } | |
| 201 } | |
| 202 } | |
| OLD | NEW |