blob: cac1e580485d26c0a1d72a6dbdbfbc2a0cf60e91 [file] [log] [blame]
/*
* Copyright (C) 2011, 2012 Purdue University
* Written by Gregor Richards
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
(function() {
// put benchmarks here
var benchmarks = ["amazon-chrome", "amazon-chrome-win", "amazon-firefox",
"amazon-firefox-win", "amazon-safari", "facebook-chrome",
"facebook-chrome-win", "facebook-firefox",
"facebook-firefox-win", "facebook-safari",
"google-chrome", "google-chrome-win", "google-firefox",
"google-firefox-win", "google-safari", "twitter-chrome",
"twitter-chrome-win", "twitter-firefox",
"twitter-firefox-win", "twitter-safari", "yahoo-chrome",
"yahoo-chrome-win", "yahoo-firefox", "yahoo-firefox-win",
"yahoo-safari"];
var modes = {
"*": ["urem"],
"amazon-firefox": ["urm"],
"amazon-firefox-win": ["urm"],
"google-firefox": ["uem"],
"twitter-chrome-win": ["rem"]
};
var minRuns = 23;
var keepRuns = 20;
var maxRuns = 100;
var maxCIM = 0.10;
// fixups for old engines
if (!Array.prototype.reduce) {
Array.prototype.reduce = function(f, val) {
for (var i = 0; i < this.length; i++) {
val = f(val, this[i]);
}
return val;
};
}
// all test results
var results = {};
var curRun = 0;
var curBenchmark = 0;
var curMode = 0;
function getModes(bm) {
if (bm in modes) return modes[bm];
return modes["*"];
}
// call this to either rerun or go to another page and come back
function rerun() {
try {
if (window.sessionStorage) {
// store this for the session and go 'round to another page to force uncaching on Chrome
sessionStorage.JSBNG_harnessState = JSON.stringify({
results: results,
curRun: curRun,
curBenchmark: curBenchmark,
curMode: curMode
});
window.location.href = window.location.href.replace(/\/[^\/]*$/, "/reload.html");
return;
}
} catch (ex) {}
runBenchmark();
}
// load our current state and run
function onload() {
var gob = document.getElementById("go");
try {
if (window.sessionStorage && "JSBNG_harnessState" in sessionStorage) {
var state = JSON.parse(sessionStorage.JSBNG_harnessState);
results = state.results;
curRun = state.curRun;
curBenchmark = state.curBenchmark;
curMode = state.curMode;
setTimeout(runBenchmark, 200);
gob.style.display = "none";
return;
}
} catch (ex) {}
gob.onclick = function() {
gob.style.display = "none";
setTimeout(runBenchmark, 200);
};
if (/#run/.test(window.location.href)) {
gob.style.display = "none";
setTimeout(runBenchmark, 200);
}
}
function runBenchmark() {
var output = document.getElementById("output");
var bmframe = document.getElementById("bmframe");
// should we stop?
if (curRun < minRuns) {
output.innerHTML = (curRun+1) + "/" + minRuns;
} else {
// check if our S/M is OK
var stats = handleResults(false);
output.innerHTML = (curRun+1) + " " + stats.cim + " (" + maxCIM + ")";
if (curRun >= maxRuns || stats.cim < maxCIM) {
bmframe.src = "about:blank";
bmframe.style.display = "none";
handleResults(true);
if (window.sessionStorage) delete sessionStorage.JSBNG_harnessState;
return;
}
}
// get out our benchmark and mode
var benchmark = benchmarks[curBenchmark];
var modes = getModes(benchmark);
var mode = modes[curMode];
if (++curMode >= modes.length) {
curMode = 0;
curBenchmark++;
}
if (curBenchmark >= benchmarks.length) {
curBenchmark = 0;
curRun++;
}
// make sure we have the results space
if (!(benchmark in results)) results[benchmark] = {};
if (!(mode in results[benchmark])) results[benchmark][mode] = [];
// Expose time measuring function.
if (window.performance && window.performance.now)
window.currentTimeInMS = function() { return window.performance.now() };
else if (typeof preciseTime !== 'undefined')
window.currentTimeInMS = function() { return preciseTime() * 1000; };
else
window.currentTimeInMS = function() { return Date.now(); };
// set up the receiver
if (/v/.test(mode)) {
// verification mode, we only care if there's an error
window.JSBNG_handleResult = function(res) {
if (res.error) {
if (!("errors" in results)) results.errors = [];
results.errors.push(benchmark + "." + mode + ": " + res.msg);
}
rerun();
};
} else {
window.JSBNG_handleResult = function(res) {
if (!res.error) {
results[benchmark][mode].push(res.time);
}
rerun();
};
}
// then load it
bmframe.src = benchmark + "/" + mode + ".html";
}
// handle all of our results
function handleResults(pr) {
var output = document.getElementById("output");
function print(str) {
output.appendChild(document.createTextNode(str));
output.appendChild(document.createElement("br"));
}
function printarr(arr) {
for (var i = 0; i < arr.length; i++) print(arr[i]);
}
function percent(num) {
return (num*100).toFixed(2) + "%";
}
// clear out the intermediate results
if (pr) output.innerHTML = "";
// totals
var totals = {
mean: 1,
stddev: 1,
sem: 1,
ci: 1,
runs: 0
};
// stuff to print later
var ptotals = [];
var presults = [];
var praw = [];
var spc = "\u00a0\u00a0";
var spc2 = spc + spc;
// calculate all the real results
for (var b = 0; b < benchmarks.length; b++) {
var benchmark = benchmarks[b];
var modes = getModes(benchmark);
if (pr) {
presults.push(spc + benchmark + ":");
praw.push(spc + benchmark + ":");
}
for (var m = 0; m < modes.length; m++) {
var mode = modes[m];
var bmresults = results[benchmark][mode].slice(-keepRuns);
if (bmresults.length == 0) continue;
// get the raw results
var rr = spc2 + mode + ": [";
for (var i = 0; i < bmresults.length; i++) {
if (i != 0) rr += ", ";
rr += bmresults[i];
}
rr += "]";
if (pr) praw.push(rr);
// now get the stats for this run
var bmstats = stats(bmresults);
// mul it to the totals
totals.mean *= bmstats.mean;
totals.stddev *= bmstats.stddev;
totals.sem *= bmstats.sem;
totals.ci *= bmstats.ci;
totals.runs++;
// and output it
if (pr) presults.push(spc2 + mode + ": " +
bmstats.mean.toFixed(2) + "ms ± " + percent(bmstats.cim) +
" (stddev=" + percent(bmstats.sm) + ", stderr=" +
percent(bmstats.semm) + ")");
}
if (pr) {
presults.push("");
praw.push("");
}
}
// now calculate the totals
var power = 1 / totals.runs;
totals.mean = Math.pow(totals.mean, power);
totals.stddev = Math.pow(totals.stddev, power);
totals.sm = totals.stddev / totals.mean;
totals.sem = Math.pow(totals.sem, power);
totals.semm = totals.sem / totals.mean;
totals.ci = Math.pow(totals.ci, power);
totals.cim = totals.ci / totals.mean;
ptotals.push("Final results:");
ptotals.push(spc + totals.mean.toFixed(2) + "ms ± " + percent(totals.cim) + " (lower is better)");
ptotals.push(spc + "Standard deviation = " + percent(totals.sm) + " of mean");
ptotals.push(spc + "Standard error = " + percent(totals.semm) + " of mean");
if (totals.cim >= maxCIM)
ptotals.push(spc + "WARNING: These results are not trustworthy! After " + maxRuns + " runs, 95% confidence interval is still greater than " + percent(maxCIM) + " of the mean!");
else
ptotals.push(spc + curRun + " runs");
ptotals.push("");
// if there are errors, mark those too
if ("errors" in results) {
ptotals.push("ERRORS:");
for (var i = 0; i < results.errors.length; i++) ptotals.push(spc + results.errors[i]);
ptotals.push("");
}
if (pr) {
// and print it all out
printarr(ptotals);
print("Result breakdown:");
printarr(presults);
print("Raw results:");
printarr(praw);
}
return totals;
}
// standard t-distribution for normally distributed samples
var tDistribution = [NaN, NaN, 12.71, 4.30, 3.18, 2.78, 2.57, 2.45, 2.36,
2.31, 2.26, 2.23, 2.20, 2.18, 2.16, 2.14, 2.13, 2.12, 2.11, 2.10, 2.09,
2.09, 2.08, 2.07, 2.07, 2.06, 2.06, 2.06, 2.05, 2.05, 2.05, 2.04, 2.04,
2.04, 2.03, 2.03, 2.03, 2.03, 2.03, 2.02, 2.02, 2.02, 2.02, 2.02, 2.02,
2.02, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.01, 2.00, 2.00,
2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00, 2.00,
2.00, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99,
1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99, 1.99,
1.99, 1.99, 1.99, 1.99, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98,
1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.98, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97,
1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.97, 1.96];
// t distribution
function tDist(n) {
if (n >= tDistribution.length)
return tDistribution[tDistribution.length-1];
return tDistribution[n];
}
// get statistics
function stats(results) {
var ret = {};
function sum(arr) {
return arr.reduce(function(p, c) { return p + c; }, 0);
}
// mean
ret.mean = sum(results) / results.length;
// sample stddev
ret.stddev = Math.sqrt(
sum(
results.map(function(e) { return Math.pow(e - ret.mean, 2); })
) / (results.length - 1)
);
// stddev / mean
ret.sm = ret.stddev / ret.mean;
// sample SEM (stderr)
ret.sem = ret.stddev / Math.sqrt(results.length);
// sample SEM/mean
ret.semm = ret.sem / ret.mean;
// sample 95% confidence interval range
ret.ci = tDist(results.length) * ret.sem;
// sample 95% CI / mean
ret.cim = ret.ci / ret.mean;
return ret;
}
window.onload = onload;
})();