mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
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;
|
|
}
|
|
})(); |