Needs a lot of cleanup. Data has been de-duplicated, and where identical copies existed, one of them has been replaced with a symlink. Some files have been excluded, such as binaries, installers and debug dumps. Some of that may still be present.
155 lines
No EOL
5.9 KiB
JavaScript
155 lines
No EOL
5.9 KiB
JavaScript
//
|
|
// 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;
|
|
}
|
|
})(); |