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 |