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