Unreviewed, fix build failure
[WebKit-https.git] / PerformanceTests / JSBench / harness.js
1 /*
2  * Copyright (C) 2011, 2012 Purdue University
3  * Written by Gregor Richards
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  * 
9  * 1. Redistributions of source code must retain the above copyright notice,
10  *    this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  * 
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 (function() {
29     // put benchmarks here
30     var benchmarks = ["amazon-chrome", "amazon-chrome-win", "amazon-firefox",
31                       "amazon-firefox-win", "amazon-safari", "facebook-chrome",
32                       "facebook-chrome-win", "facebook-firefox",
33                       "facebook-firefox-win", "facebook-safari",
34                       "google-chrome", "google-chrome-win", "google-firefox",
35                       "google-firefox-win", "google-safari", "twitter-chrome",
36                       "twitter-chrome-win", "twitter-firefox",
37                       "twitter-firefox-win", "twitter-safari", "yahoo-chrome",
38                       "yahoo-chrome-win", "yahoo-firefox", "yahoo-firefox-win",
39                       "yahoo-safari"];
40     var modes = {
41         "*": ["urem"],
42         "amazon-firefox": ["urm"],
43         "amazon-firefox-win": ["urm"],
44         "google-firefox": ["uem"],
45         "twitter-chrome-win": ["rem"]
46     };
47     var minRuns = 23;
48     var keepRuns = 20;
49     var maxRuns = 100;
50     var maxCIM = 0.10;
51
52     // fixups for old engines
53     if (!Array.prototype.reduce) {
54         Array.prototype.reduce = function(f, val) {
55             for (var i = 0; i < this.length; i++) {
56                 val = f(val, this[i]);
57             }
58             return val;
59         };
60     }
61
62     // all test results
63     var results = {};
64
65     var curRun = 0;
66     var curBenchmark = 0;
67     var curMode = 0;
68
69     function getModes(bm) {
70         if (bm in modes) return modes[bm];
71         return modes["*"];
72     }
73
74     // call this to either rerun or go to another page and come back
75     function rerun() {
76         try {
77             if (window.sessionStorage) {
78                 // store this for the session and go 'round to another page to force uncaching on Chrome
79                 sessionStorage.JSBNG_harnessState = JSON.stringify({
80                     results: results,
81                     curRun: curRun,
82                     curBenchmark: curBenchmark,
83                     curMode: curMode
84                 });
85                 window.location.href = window.location.href.replace(/\/[^\/]*$/, "/reload.html");
86                 return;
87             }
88         } catch (ex) {}
89         runBenchmark();
90     }
91
92     // load our current state and run
93     function onload() {
94         var gob = document.getElementById("go");
95         try {
96             if (window.sessionStorage && "JSBNG_harnessState" in sessionStorage) {
97                 var state = JSON.parse(sessionStorage.JSBNG_harnessState);
98                 results = state.results;
99                 curRun = state.curRun;
100                 curBenchmark = state.curBenchmark;
101                 curMode = state.curMode;
102                 setTimeout(runBenchmark, 200);
103                 gob.style.display = "none";
104                 return;
105             }
106         } catch (ex) {}
107         gob.onclick = function() {
108             gob.style.display = "none";
109             setTimeout(runBenchmark, 200);
110         };
111         if (/#run/.test(window.location.href)) {
112             gob.style.display = "none";
113             setTimeout(runBenchmark, 200);
114         }
115     }
116
117     function runBenchmark() {
118         var output = document.getElementById("output");
119         var bmframe = document.getElementById("bmframe");
120
121         // should we stop?
122         if (curRun < minRuns) {
123             output.innerHTML = (curRun+1) + "/" + minRuns;
124         } else {
125             // check if our S/M is OK
126             var stats = handleResults(false);
127             output.innerHTML = (curRun+1) + " " + stats.cim + " (" + maxCIM + ")";
128             if (curRun >= maxRuns || stats.cim < maxCIM) {
129                 bmframe.src = "about:blank";
130                 bmframe.style.display = "none";
131                 handleResults(true);
132                 if (window.sessionStorage) delete sessionStorage.JSBNG_harnessState;
133                 return;
134             }
135         }
136
137         // get out our benchmark and mode
138         var benchmark = benchmarks[curBenchmark];
139         var modes = getModes(benchmark);
140         var mode = modes[curMode];
141         if (++curMode >= modes.length) {
142             curMode = 0;
143             curBenchmark++;
144         }
145         if (curBenchmark >= benchmarks.length) {
146             curBenchmark = 0;
147             curRun++;
148         }
149
150         // make sure we have the results space
151         if (!(benchmark in results)) results[benchmark] = {};
152         if (!(mode in results[benchmark])) results[benchmark][mode] = [];
153
154         // Expose time measuring function.
155         if (window.performance && window.performance.now)
156             window.currentTimeInMS = function() { return window.performance.now() };
157         else if (typeof preciseTime !== 'undefined')
158             window.currentTimeInMS = function() { return preciseTime() * 1000; };
159         else
160             window.currentTimeInMS = function() { return Date.now(); };
161
162         // set up the receiver
163         if (/v/.test(mode)) {
164             // verification mode, we only care if there's an error
165             window.JSBNG_handleResult = function(res) {
166                 if (res.error) {
167                     if (!("errors" in results)) results.errors = [];
168                     results.errors.push(benchmark + "." + mode + ": " + res.msg);
169                 }
170                 rerun();
171             };
172         } else {
173             window.JSBNG_handleResult = function(res) {
174                 if (!res.error) {
175                     results[benchmark][mode].push(res.time);
176                 }
177                 rerun();
178             };
179         }
180
181         // then load it
182         bmframe.src = benchmark + "/" + mode + ".html";
183     }
184
185     // handle all of our results
186     function handleResults(pr) {
187         var output = document.getElementById("output");
188         function print(str) {
189             output.appendChild(document.createTextNode(str));
190             output.appendChild(document.createElement("br"));
191         }
192         function printarr(arr) {
193             for (var i = 0; i < arr.length; i++) print(arr[i]);
194         }
195         function percent(num) {
196             return (num*100).toFixed(2) + "%";
197         }
198
199         // clear out the intermediate results
200         if (pr) output.innerHTML = "";
201
202         // totals
203         var totals = {
204             mean: 1,
205             stddev: 1,
206             sem: 1,
207             ci: 1,
208             runs: 0
209         };
210
211         // stuff to print later
212         var ptotals = [];
213         var presults = [];
214         var praw = [];
215         var spc = "\u00a0\u00a0";
216         var spc2 = spc + spc;
217
218         // calculate all the real results
219         for (var b = 0; b < benchmarks.length; b++) {
220             var benchmark = benchmarks[b];
221             var modes = getModes(benchmark);
222             if (pr) {
223                 presults.push(spc + benchmark + ":");
224                 praw.push(spc + benchmark + ":");
225             }
226
227             for (var m = 0; m < modes.length; m++) {
228                 var mode = modes[m];
229                 var bmresults = results[benchmark][mode].slice(-keepRuns);
230                 if (bmresults.length == 0) continue;
231
232                 // get the raw results
233                 var rr = spc2 + mode + ": [";
234                 for (var i = 0; i < bmresults.length; i++) {
235                     if (i != 0) rr += ", ";
236                     rr += bmresults[i];
237                 }
238                 rr += "]";
239                 if (pr) praw.push(rr);
240
241                 // now get the stats for this run
242                 var bmstats = stats(bmresults);
243
244                 // mul it to the totals
245                 totals.mean *= bmstats.mean;
246                 totals.stddev *= bmstats.stddev;
247                 totals.sem *= bmstats.sem;
248                 totals.ci *= bmstats.ci;
249                 totals.runs++;
250
251                 // and output it
252                 if (pr) presults.push(spc2 + mode + ": " +
253                     bmstats.mean.toFixed(2) + "ms ± " + percent(bmstats.cim) +
254                     " (stddev=" + percent(bmstats.sm) + ", stderr=" +
255                     percent(bmstats.semm) + ")");
256             }
257
258             if (pr) {
259                 presults.push("");
260                 praw.push("");
261             }
262         }
263
264         // now calculate the totals
265         var power = 1 / totals.runs;
266         totals.mean = Math.pow(totals.mean, power);
267         totals.stddev = Math.pow(totals.stddev, power);
268         totals.sm = totals.stddev / totals.mean;
269         totals.sem = Math.pow(totals.sem, power);
270         totals.semm = totals.sem / totals.mean;
271         totals.ci = Math.pow(totals.ci, power);
272         totals.cim = totals.ci / totals.mean;
273         ptotals.push("Final results:");
274         ptotals.push(spc + totals.mean.toFixed(2) + "ms ± " + percent(totals.cim) + " (lower is better)");
275         ptotals.push(spc + "Standard deviation = " + percent(totals.sm) + " of mean");
276         ptotals.push(spc + "Standard error = " + percent(totals.semm) + " of mean");
277         if (totals.cim >= maxCIM)
278             ptotals.push(spc + "WARNING: These results are not trustworthy! After " + maxRuns + " runs, 95% confidence interval is still greater than " + percent(maxCIM) + " of the mean!");
279         else
280             ptotals.push(spc + curRun + " runs");
281         ptotals.push("");
282
283         // if there are errors, mark those too
284         if ("errors" in results) {
285             ptotals.push("ERRORS:");
286             for (var i = 0; i < results.errors.length; i++) ptotals.push(spc + results.errors[i]);
287             ptotals.push("");
288         }
289
290         if (pr) {
291             // and print it all out
292             printarr(ptotals);
293             print("Result breakdown:");
294             printarr(presults);
295             print("Raw results:");
296             printarr(praw);
297         }
298
299         return totals;
300     }
301
302     // standard t-distribution for normally distributed samples
303     var tDistribution = [NaN, NaN, 12.71, 4.30, 3.18, 2.78, 2.57, 2.45, 2.36,
304     2.31, 2.26, 2.23, 2.20, 2.18, 2.16, 2.14, 2.13, 2.12, 2.11, 2.10, 2.09,
305     2.09, 2.08, 2.07, 2.07, 2.06, 2.06, 2.06, 2.05, 2.05, 2.05, 2.04, 2.04,
306     2.04, 2.03, 2.03, 2.03, 2.03, 2.03, 2.02, 2.02, 2.02, 2.02, 2.02, 2.02,
307     2.02, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.00, 2.00,
308     2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00,
309     2.00, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99,
310     1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99,
311     1.99, 1.99, 1.99, 1.99, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
312     1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
313     1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
314     1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
315     1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
316     1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.97, 1.97, 1.97, 1.97, 1.97,
317     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
318     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
319     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
320     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
321     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
322     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
323     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
324     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
325     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
326     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
327     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
328     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
329     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
330     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
331     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
332     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
333     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
334     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
335     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
336     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
337     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
338     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
339     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
340     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
341     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
342     1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.96];
343
344     // t distribution
345     function tDist(n) {
346         if (n >= tDistribution.length)
347             return tDistribution[tDistribution.length-1];
348         return tDistribution[n];
349     }
350
351     // get statistics
352     function stats(results) {
353         var ret = {};
354
355         function sum(arr) {
356             return arr.reduce(function(p, c) { return p + c; }, 0);
357         }
358
359         // mean
360         ret.mean = sum(results) / results.length;
361
362         // sample stddev
363         ret.stddev = Math.sqrt(
364             sum(
365                 results.map(function(e) { return Math.pow(e - ret.mean, 2); })
366             ) / (results.length - 1)
367         );
368
369         // stddev / mean
370         ret.sm = ret.stddev / ret.mean;
371
372         // sample SEM (stderr)
373         ret.sem = ret.stddev / Math.sqrt(results.length);
374
375         // sample SEM/mean
376         ret.semm = ret.sem / ret.mean;
377
378         // sample 95% confidence interval range
379         ret.ci = tDist(results.length) * ret.sem;
380
381         // sample 95% CI / mean
382         ret.cim = ret.ci / ret.mean;
383
384         return ret;
385     }
386
387     window.onload = onload;
388 })();