'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var now = require('performance-now'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var now__default = /*#__PURE__*/_interopDefaultLegacy(now); /** * User changeable properties of a Benchmark */ class BenchmarkProperties { /** * BenchmarkProperties constructor * * @param properties the new properties */ constructor(properties) { /** * The minimum number of cycle runs */ this.minCycles = 10; /** * The maximum number of cycle runs. * The algorithm always tries to run the maximum number of runs */ this.maxCycles = 100; /** * The maximum time a test can run, in seconds. * Note that minCycles has priority over this setting. */ this.maxTime = 15; /** * The Benchmark name */ this.name = undefined; //If properties is an object if (properties === Object(properties)) for (let property_name of Object.keys(properties)) // Only update properties that exist if (this.hasOwnProperty(property_name)) this[property_name] = properties[property_name]; } } /** * The main class. * A Benchmark consists of a group of tests that are run and compared regarding the time performances. */ class Benchmark { /** * Benchmark constructor * * @param properties This Benchmark properties used to create a properties object */ constructor(properties) { /** * This Benchmark properties */ this.properties = null; /** * The tests added by the user, to be run */ this.tests = []; /** * The results obtained in the previous run of this benchmark. * Consists of a list of pairs test name - test stats */ this.results = []; /** * Function to run on the beginning of this benchmark */ this.onBegin = (benchmark) => { }; /** * Function to run on the end of the benchmark (on the end of all tests) */ this.onTestBegin = (benchmark, test) => { }; /** * Function to run on the end of the benchmark (on the end of all tests) */ this.onTestEnd = (benchmark, test) => { }; /** * Function to run on the end of the benchmark (on the end of all tests) */ this.onEnd = (benchmark) => { }; this.properties = new BenchmarkProperties(properties); } /** * Get the @BenchmarkProperties used in this Benchmark, as an object */ getProperties() { return this.properties; } /** * Get the tests that perform on this Benchmark */ getTests() { return this.tests; } /** * Get the results previously obtained on this Benchmark * @return list of pairs test name - test stats */ getResults() { return this.results; } /** * Add a new test to this Benchmark * * @param testName the test name * @param fn The function to run on this test * @return the created @Test */ add(test) { this.tests.push(test); return this; } ; /** * Add an event to this Benchmark. Possibilities: * - 'onBegin' * - 'onTestBegin' * - 'onTestEnd' * - 'onEnd' * * @param eventName The name of the event to be altered * @param fn The function that will run when the event is called */ on(eventName, fn) { if (eventName.substr(0, 2) == 'on' && this.hasOwnProperty(eventName)) this[eventName] = fn; return this; } /** * Run this Benchmark list of @Test * * @return results obtained as a list of pairs test name - test stats */ run() { this.onBegin(this); for (let test of this.tests) { this.onTestBegin(this, test); test.run(this.getProperties()); this.results.push([test.name, test.getStats()]); this.onTestEnd(this, test); } this.onEnd(this); return this.results; } ; } class Mean { calculate(values) { return values.reduce((acc, val) => acc + val, 0) / values.length; } } class StandardDeviation { calculate(values) { const N = values.length; const mean = values.reduce((acc, val) => acc + val, 0) / N; return Math.sqrt(values.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0) / N); } } class Count { calculate(values) { return values.length; } } class Maximum { calculate(values) { return Math.max(...values); } } class Minimum { calculate(values) { return Math.min(...values); } } /** * The stats resultant of running a test */ class Stats { /** * Constructor * * @param cycleTimes list of obtained performance times of each cycle */ constructor(cycleTimes) { this.mean = new Mean().calculate(cycleTimes); this.std = new StandardDeviation().calculate(cycleTimes); this.count = new Count().calculate(cycleTimes); this.max = new Maximum().calculate(cycleTimes); this.min = new Minimum().calculate(cycleTimes); } } /** * A test represents a code that shall be run and afterwards compared regarding the performance */ class Test { /** * @param name The test name * @param fn The function to be run */ constructor(name, fn) { this.name = name; this.fn = fn; /** * The measured times for the ran cycles, in milliseconds */ this.cycleTimes = []; /** * The stats obtained by running this test */ this.stats = null; /** * Function to run on the begin of the test */ this.onBegin = (test) => { }; /** * Function to run after before running each cycle */ this.onCycleBegin = (test) => { }; /** * Function to run after each cycle completes */ this.onCycleEnd = (test) => { }; /** * Function to run on the end of all test cycles */ this.onEnd = (test) => { }; } /** * Add an event to this Test. Possibilities: * - 'onBegin' * - 'onCycleBegin' * - 'onCycleEnd' * - 'onEnd' * * @param eventName The name of the event to be altered * @param fn The function that will run when the event is called */ on(eventName, fn) { if (eventName.substr(0, 2) == 'on' && this.hasOwnProperty(eventName)) this[eventName] = fn; return this; } /** * Gets the test baseline time (the time it takes to measure the times) */ getBaselineTime() { const startTime = now__default['default'](); const endTime = now__default['default'](); return endTime - startTime; } /** * Run this test according to the given testProperties * * @param testProperties the testProperties to use on this test. Similar to a @BenchmarkProperties object */ run(testProperties) { // Times are measured in milliseconds let totalTime = 0; const { minCycles, maxCycles, maxTime } = testProperties; const maxTimeMS = maxTime * Test.SECONDS_TO_MILLISECONDS; const baselineTime = this.getBaselineTime(); this.onBegin(this); while (this.cycleTimes.length < minCycles || (totalTime < maxTimeMS && this.cycleTimes.length < maxCycles)) { this.onCycleBegin(this); // Measure performance const startTime = now__default['default'](); this.fn(); const endTime = now__default['default'](); // Remove baseline times from performance testing let testTime = (endTime - startTime - baselineTime); // Because of small deviations testTime = testTime < 0 ? 0 : testTime; // Save times this.cycleTimes.push(testTime); totalTime += testTime; this.onCycleEnd(this); } this.stats = new Stats(this.cycleTimes); this.onEnd(this); } ; /** * Get the stats obtained by running this test * * @return @Stats instance */ getStats() { return this.stats; } } Test.SECONDS_TO_MILLISECONDS = 1000; exports.Benchmark = Benchmark; exports.BenchmarkProperties = BenchmarkProperties; exports.Stats = Stats; exports.Test = Test;