blob: d9fd78c1d505edd7e167541fa21773c54cfee509 [file] [log] [blame]
// utils.js
(function(window, undefined) {
const COLD_TIME = 2.0;
const MAX_WINDOW_TIME = 20.0;
const BEST_CONFIDENCE = 0.0005;
const ZSTAR = {
0.80: 1.28,
0.90: 1.645,
0.95: 1.96,
0.98: 2.33,
0.99: 2.58,
0.999: 3.10,
0.9998: 3.49,
}
const perf = window.perf = {};
let timers = perf.timers = [];
// Time the experiment started. Reset after warming up.
perf.firstTime = performance.now()/1000.0;
perf.lastTime = perf.firstTime;
// Time to wait until warm up is done.
perf.coldTime = perf.firstTime + COLD_TIME;
let finished = false;
perf.ntos = function(n) {
let unit = "s";
if (n < 1) {
unit = "ms";
n *= 1000;
}
const p = Math.floor(n);
let r = "" + Math.round((n-p)*100);
while (r.length < 2) r = "0" + r;
return p + "." + r + unit;
}
perf.ntoserr = function(avg, std, for_console) {
let unit = "s";
if (avg < 1) {
unit = "ms";
avg *= 1000;
std *= 1000;
}
const p = Math.floor(avg);
let r = "" + Math.round((avg-p)*100);
while (r.length < 2) r = "0" + r;
const p2 = Math.floor(std);
let r2 = "" + Math.round((std-p2)*100);
while (r2.length < 2) r2 = "0" + r2;
const plus = for_console ? "±" : "&plusmn;";
return p + "." + r + plus + p2 + "." + r2 + unit;
}
function getExperimentName() {
let name = window.location.pathname.split('/');
name = name[name.length-1].split('.')[0];
if (window.location.search == "?scroll") {
name += " scroll";
}
return name;
}
function SET(key, value) {
localStorage.setItem(key, JSON.stringify(value));
}
function GET(key, def=null) {
const v = localStorage.getItem(key);
if (v === null) return def;
return JSON.parse(v);
}
function runNextPerfTest() {
const scripts = GET("scripts", []);
const next = scripts.shift();
if (!next) return;
SET("scripts", scripts);
console.log(next);
window.location.href = next;
}
function done() {
if (finished) return;
finished = true;
let [average, conf] = perf.stats();
let res = GET("perf", []);
const name = getExperimentName();
res = res.filter(x => x[0] != name);
res.push([ name, average, conf ]);
SET("perf", res);
runNextPerfTest();
}
perf.onContinuousMode = function() {
const s = GET("scripts");
return !!(s && s.length);
}
perf.stats = function() {
let average = 0.0;
for (let i = 0; i < timers.length; ++i) {
average += timers[i];
}
average = average/timers.length;
let stddev = 0.0;
for (let i = 0; i < timers.length; ++i) {
stddev += Math.pow(timers[i] - average, 2);
}
stddev = Math.sqrt(stddev/timers.length);
const conf = ZSTAR[0.999] * stddev / Math.sqrt(timers.length);
return [average, conf];
}
let consoleTime = 0.0;
perf.measure = function() {
const t = performance.now()/1000.0;
const delta = t - perf.lastTime;
perf.lastTime = t;
if (t < perf.coldTime) {
if (perf.onContinuousMode()) {
consoleTime -= delta;
if (consoleTime <= 0.0) {
consoleTime += 5.0;
const left = perf.coldTime - perf.lastTime;
console.log(`warming up [${perf.ntos(left)}]`);
}
}
perf.firstTime = perf.lastTime = t;
requestAnimationFrame(perf.measure);
return;
}
timers.push(delta);
if (perf.onContinuousMode()) {
const time = perf.lastTime - perf.firstTime;
if (time > MAX_WINDOW_TIME) {
done();
} else {
let [average, conf] = perf.stats();
if (time > 5.0 && conf < BEST_CONFIDENCE) done();
}
consoleTime -= delta;
if (consoleTime <= 0.0) {
consoleTime += 5.0;
let [average, conf] = perf.stats();
const t = perf.lastTime - perf.firstTime;
console.log(`avg: ${perf.ntoserr(average, conf, true)} [${perf.ntos(t)}]`);
}
}
requestAnimationFrame(perf.measure);
}
perf.getResults = function() {
return GET("perf", []);
}
perf.saveBaseline = function() {
const res = perf.getResults();
const ret = {};
for (let i = 0; i < res.length; ++i) {
const p = res[i];
ret[p[0]] = [p[1], p[2]];
}
SET("baseline", ret);
}
perf.getBaseline = function() {
return GET("baseline", {});
}
perf.setupTests = function(tests) {
SET("scripts", tests);
SET("perf", []);
runNextPerfTest();
};
if (performance.memory.usedJSHeapSize == 10000000) {
console.error("Start Chrome with --disable-gpu-vsync --enable-precise-memory-info for this test");
}
})(window);