//// Created by Seiji Emery on 9/4/15// Copyright 2015 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//// Simple benchmarking library. See the test scripts for example usage.//(function () { // Runs multiple 'test cases' (benchmarks that get invoked w/ varying iteration counts) function TestRunner () { this._testCases = {}; this._testResults = []; } this.TestRunner = TestRunner; // Add a test case. Define behavior by declaratively calling .before, .after, and .run w/ impls. TestRunner.prototype.addTestCase = function (name) { var testcase = new TestCase(name); this._testCases[name] = testcase; return testcase; } // Runs a function n times. Uses context object so it runs sandboxed. function runTimedWithIterations (f, context, numIterations) { var start = new Date().getTime(); while (numIterations --> 0) { f.apply(context); } var end = new Date().getTime(); return end - start; } function fmtSecs (secs) { if (secs >= 1.0) { return ""+secs.toFixed(3)+" secs"; } else if (secs >= 1e-3) { return ""+(secs * 1e3).toFixed(3)+" ms"; } else if (secs >= 1e-6) { return ""+(secs * 1e6).toFixed(3)+" µs"; } else { return ""+(secs * 1e9).toFixed(3)+" ns"; } } function avg(samples) { return samples.length ? samples.reduce(function(a, b){return a+b;}, 0.0) / samples.length : 0.0; } // Runs a named performance test for multiple iterations, and optionally calculates an average. // @param name: the test name registered with addTest // @param iterations: a list of iteration sequences. eg. [10, 100, 1000] => runs for 10, then 100, then 1000 iterations // and prints the timing info for each (in ms) // @param iterationsForAvg: optional // @param samplesForAvg: optional // After running iterations, will compute an average if iterationsForAvg and samplesForAvg are both set: // average := // collects n samples by running the testcase n times with i iterations // where n = samplesForAvg, i = iterationsForAvg // we then average the samples, and print that. // TestRunner.prototype.runPerfTestWithIterations = function (name, iterations, iterationsForAvg, samplesForAvg) { if (!this._testCases[name]) { print("No test case with name '" + name + "'!"); } var testcase = this._testCases[name]; var runAverages = []; // secs var noErrors = true; iterations.forEach(function(n, i) { var sandbox = {}; try { if (testcase.setupFunction) { testcase.setupFunction.apply(sandbox); } var dt = runTimedWithIterations(testcase.runFunction, sandbox, n); if (testcase.teardownFunction) { testcase.teardownFunction.apply(sandbox); } this._testResults.push(""+name+" with "+n+" iterations: "+dt+" ms"); } catch (e) { this._testResults.push("Testcase '"+name+"':"+i+" ("+n+") failed with error: "+e); noErrors = false; } }, this); this.writeResultsToLog(); if (noErrors && iterationsForAvg && samplesForAvg) { try { var samples = []; for (var n = samplesForAvg; n --> 0; ) { var sandbox = {}; if (testcase.setupFunction) { testcase.setupFunction.apply(sandbox); } var dt = runTimedWithIterations(testcase.runFunction, sandbox, iterationsForAvg); if (testcase.teardownFunction) { testcase.teardownFunction.apply(sandbox); } samples.push(dt / iterationsForAvg * 1e-3); // dt in ms } print(" average: " + ((avg(samples) * 1e6).toFixed(3)) + " µs") // print("\t(" + samplesForAvg + " samples with " + iterationsForAvg + " iterations)"); } catch (e) { print("Error while calculating average: " + e); return; } } } // Runs all registered tests w/ iteration + average parameters TestRunner.prototype.runAllTestsWithIterations = function (iterations, iterationsForAvg, samplesForAvg) { Object.keys(this._testCases).forEach(function(name) { this.runPerfTestWithIterations(name, iterations, iterationsForAvg, samplesForAvg); }, this); } // Dump results to the debug log. You don't need to call this. TestRunner.prototype.writeResultsToLog = function () { var s = ' ' + this._testResults.join('\n '); this._testResults = []; // print(s); s.split('\n').forEach(function(line) { print(line); }); } // Implements a benchmark test case, that has optional setup = teardown code, and a (non-optional) run function. // setup + teardown get called once, and run gets called for n sequential iterations (see TestRunner.runTestWithIterations) // setup, run, and teardown all get applied to the same sandboxed this object, so use that for storing test data for each run. function TestCase (name) { this.name = name; this.setupFunction = null; this.runFunction = null; this.teardownFunction = null; } this.TestCase = TestCase; TestCase.prototype.before = function (f) { this.setupFunction = f; return this; } TestCase.prototype.after = function (f) { this.teardownFunction = f; return this; } TestCase.prototype.run = function (f) { this.runFunction = f; }})();
//
// Created by Seiji Emery on 9/4/15
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Simple benchmarking library. See the test scripts for example usage.
//
(function () {
// Runs multiple 'test cases' (benchmarks that get invoked w/ varying iteration counts)
function TestRunner () {
this._testCases = {};
this._testResults = [];
}
this.TestRunner = TestRunner;
// Add a test case. Define behavior by declaratively calling .before, .after, and .run w/ impls.
TestRunner.prototype.addTestCase = function (name) {
var testcase = new TestCase(name);
this._testCases[name] = testcase;
return testcase;
}
// Runs a function n times. Uses context object so it runs sandboxed.
function runTimedWithIterations (f, context, numIterations) {
var start = new Date().getTime();
while (numIterations --> 0) {
f.apply(context);
}
var end = new Date().getTime();
return end - start;
}
function fmtSecs (secs) {
if (secs >= 1.0) {
return ""+secs.toFixed(3)+" secs";
} else if (secs >= 1e-3) {
return ""+(secs * 1e3).toFixed(3)+" ms";
} else if (secs >= 1e-6) {
return ""+(secs * 1e6).toFixed(3)+" µs";
} else {
return ""+(secs * 1e9).toFixed(3)+" ns";
}
}
function avg(samples) {
return samples.length ?
samples.reduce(function(a, b){return a+b;}, 0.0) / samples.length : 0.0;
}
// Runs a named performance test for multiple iterations, and optionally calculates an average.
// @param name: the test name registered with addTest
// @param iterations: a list of iteration sequences. eg. [10, 100, 1000] => runs for 10, then 100, then 1000 iterations
// and prints the timing info for each (in ms)
// @param iterationsForAvg: optional
// @param samplesForAvg: optional
// After running iterations, will compute an average if iterationsForAvg and samplesForAvg are both set:
// average :=
// collects n samples by running the testcase n times with i iterations
// where n = samplesForAvg, i = iterationsForAvg
// we then average the samples, and print that.
//
TestRunner.prototype.runPerfTestWithIterations = function (name, iterations, iterationsForAvg, samplesForAvg) {
if (!this._testCases[name]) {
print("No test case with name '" + name + "'!");
}
var testcase = this._testCases[name];
var runAverages = []; // secs
var noErrors = true;
iterations.forEach(function(n, i) {
var sandbox = {};
try {
if (testcase.setupFunction) {
testcase.setupFunction.apply(sandbox);
}
var dt = runTimedWithIterations(testcase.runFunction, sandbox, n);
if (testcase.teardownFunction) {
testcase.teardownFunction.apply(sandbox);
}
this._testResults.push(""+name+" with "+n+" iterations: "+dt+" ms");
} catch (e) {
this._testResults.push("Testcase '"+name+"':"+i+" ("+n+") failed with error: "+e);
noErrors = false;
}
}, this);
this.writeResultsToLog();
if (noErrors && iterationsForAvg && samplesForAvg) {
try {
var samples = [];
for (var n = samplesForAvg; n --> 0; ) {
var sandbox = {};
if (testcase.setupFunction) {
testcase.setupFunction.apply(sandbox);
}
var dt = runTimedWithIterations(testcase.runFunction, sandbox, iterationsForAvg);
if (testcase.teardownFunction) {
testcase.teardownFunction.apply(sandbox);
}
samples.push(dt / iterationsForAvg * 1e-3); // dt in ms
}
print(" average: " + ((avg(samples) * 1e6).toFixed(3)) + " µs")
// print("\t(" + samplesForAvg + " samples with " + iterationsForAvg + " iterations)");
} catch (e) {
print("Error while calculating average: " + e);
return;
}
}
}
// Runs all registered tests w/ iteration + average parameters
TestRunner.prototype.runAllTestsWithIterations = function (iterations, iterationsForAvg, samplesForAvg) {
Object.keys(this._testCases).forEach(function(name) {
this.runPerfTestWithIterations(name, iterations, iterationsForAvg, samplesForAvg);
}, this);
}
// Dump results to the debug log. You don't need to call this.
TestRunner.prototype.writeResultsToLog = function () {
var s = ' ' + this._testResults.join('\n ');
this._testResults = [];
// print(s);
s.split('\n').forEach(function(line) {
print(line);
});
}
// Implements a benchmark test case, that has optional setup = teardown code, and a (non-optional) run function.
// setup + teardown get called once, and run gets called for n sequential iterations (see TestRunner.runTestWithIterations)
// setup, run, and teardown all get applied to the same sandboxed this object, so use that for storing test data for each run.
function TestCase (name) {
this.name = name;
this.setupFunction = null;
this.runFunction = null;
this.teardownFunction = null;
}
this.TestCase = TestCase;
TestCase.prototype.before = function (f) {
this.setupFunction = f;
return this;
}
TestCase.prototype.after = function (f) {
this.teardownFunction = f;
return this;
}
TestCase.prototype.run = function (f) {
this.runFunction = f;
}
})();