UNPKG

cronometro

Version:

Simple benchmarking suite powered by HDR histograms.

150 lines (149 loc) 4.31 kB
import { Tracker } from "./tracker.js"; function noOp() { } function noSetup(cb) { cb(); } function handleTestIteration(context, error) { if (error) { context.tracker.error = error; context.callback(context.tracker.results); return; } const { tracker, total, errorThreshold } = context; tracker.track(context.start); context.executed++; const executed = context.executed; let stop = false; if (errorThreshold > 0) { const completedPercentage = Math.floor((executed / total) * 10_000); if (completedPercentage >= 1000 && completedPercentage % 500 === 0) { const standardErrorPercentage = tracker.standardError / tracker.histogram.mean; if (standardErrorPercentage < errorThreshold) { stop = true; } } } if (stop || executed > total) { context.callback(tracker.results); return; } process.nextTick(() => { runTestIteration(context); }); } function runTestIteration(context) { try { context.start = process.hrtime.bigint(); const callResult = context.test(context.handler); if (callResult && typeof callResult.then === 'function') { callResult.then(() => { context.handler(null); }, context.handler); } else if (context.test.length === 0) { context.handler(null); } } catch (error) { if (context.test.length === 0) { context.handler(error); return; } throw error; } } function beforeCallback(testContext, err) { if (err) { testContext.callback({ success: false, error: err, size: 0, min: 0, max: 0, mean: 0, stddev: 0, percentiles: {}, standardError: 0 }); return; } process.nextTick(() => { runTestIteration(testContext); }); } function afterCallback(result, notifier, cb, err) { let notifierCode = result.success ? 0 : 1; if (err) { notifier({ success: false, error: err, size: 0, min: 0, max: 0, mean: 0, stddev: 0, percentiles: {}, standardError: 0 }); notifierCode = 1; } else { notifier(result); } cb(notifierCode); } export function runWorker(context, notifier, cb) { const { warmup, tests, index, iterations, errorThreshold } = context; const testToRun = tests?.[index]; if (!testToRun) { throw new Error('No test code exported from the worker thread'); } const [name, testDefinition] = testToRun; let test = noOp; let before = noSetup; let after = noSetup; if (typeof testDefinition === 'function') { test = testDefinition; } else { if (typeof testDefinition.test === 'function') { test = testDefinition.test; } if (typeof testDefinition.before === 'function') { before = testDefinition.before; } if (typeof testDefinition.after === 'function') { after = testDefinition.after; } } const testContext = { name, test, errorThreshold, total: iterations - 1, executed: 0, tracker: new Tracker(), start: BigInt(0), handler: noOp, notifier, callback(result) { if (warmup) { context.warmup = false; runWorker(context, notifier, cb); return; } const callback = afterCallback.bind(null, result, notifier, cb); const afterResponse = after(callback); if (afterResponse && typeof afterResponse.then === 'function') { afterResponse.then(callback, callback); } } }; testContext.handler = handleTestIteration.bind(null, testContext); const callback = beforeCallback.bind(null, testContext); const beforeResponse = before(callback); if (beforeResponse && typeof beforeResponse.then === 'function') { beforeResponse.then(callback, callback); } }