Skip to content

Commit

Permalink
fix: avoid divide by zero
Browse files Browse the repository at this point in the history
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
  • Loading branch information
jerome-benoit committed May 17, 2024
1 parent abf4592 commit 6b1aff6
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 10 deletions.
18 changes: 14 additions & 4 deletions src/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const overrideBenchmarkDefaults = (benchmark, opts) => {
benchmark.warmup = opts.warmup ?? benchmark.warmup;
};

export function mergeDeepRight(target, source) {
export const mergeDeepRight = (target, source) => {
const targetClone = structuredClone(target);

for (const key in source) {
Expand All @@ -124,7 +124,16 @@ export function mergeDeepRight(target, source) {
}

return targetClone;
}
};

export const validDividend = n => {
if ('number' !== typeof n)
throw new TypeError(`expected number, got ${n.constructor.name}`);
if (n === 0 || Number.isNaN(n)) {
throw new RangeError(`Invalid dividend: ${n}`);
}
return n;
};

export async function measure(
fn,
Expand Down Expand Up @@ -226,13 +235,14 @@ const buildStats = samples => {
const time = samples.reduce((a, b) => a + b, 0);
const avg = time / samples.length;
const vr =
samples.reduce((a, b) => a + (b - avg) ** 2, 0) / (samples.length - 1);
samples.reduce((a, b) => a + (b - avg) ** 2, 0) /
validDividend(samples.length - 1);
const sd = Math.sqrt(vr);
const sem = sd / Math.sqrt(samples.length);
const critical =
tTable[(samples.length - 1 || 1).toString()] || tTable.infinity;
const moe = sem * critical;
const rmoe = (moe / avg) * 100;
const rmoe = (moe / validDividend(avg)) * 100;

return {
samples: samples.length,
Expand Down
16 changes: 10 additions & 6 deletions src/reporter/table.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { tTable, tatamiNgGroup } from '../constants.js';
import { validDividend } from '../lib.js';
import * as clr from './clr.js';
import { duration, errorMargin, iterPerSecond, speedRatio } from './fmt.js';

Expand Down Expand Up @@ -139,25 +140,28 @@ export function summary(benchmarks, { colors = true }) {
}`}\n ${clr.bold(colors, clr.cyan(colors, baseline.name))}${benchmarks
.filter(benchmark => benchmark !== baseline)
.map(benchmark => {
const ratio = benchmark.stats.avg / baseline.stats.avg;
const ratio = benchmark.stats.avg / validDividend(baseline.stats.avg);
// https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae
const ratioSd =
ratio *
Math.sqrt(
(baseline.stats.sd / baseline.stats.avg) ** 2 +
(benchmark.stats.sd / benchmark.stats.avg) ** 2,
(baseline.stats.sd / validDividend(baseline.stats.avg)) ** 2 +
(benchmark.stats.sd / validDividend(benchmark.stats.avg)) ** 2,
);
const ratioSem =
ratioSd / Math.sqrt(baseline.stats.samples + benchmark.stats.samples);
ratioSd /
validDividend(
Math.sqrt(baseline.stats.samples + benchmark.stats.samples),
);
const critical =
tTable[
(baseline.stats.samples + benchmark.stats.samples - 1 || 1).toString()
] || tTable.infinity;
const ratioMoe = ratioSem * critical;
const ratioRmoe = (ratioMoe / ratio) * 100;
const ratioRmoe = (ratioMoe / validDividend(ratio)) * 100;
return `\n ${clr[1 > ratio ? 'red' : 'green'](
colors,
1 > ratio ? speedRatio(1 / ratio) : speedRatio(ratio),
1 > ratio ? speedRatio(1 / validDividend(ratio)) : speedRatio(ratio),
)} ± ${clr.yellow(colors, errorMargin(ratioRmoe))} times ${
1 > ratio ? 'slower' : 'faster'
} than ${clr.bold(colors, clr.cyan(colors, benchmark.name))}`;
Expand Down

0 comments on commit 6b1aff6

Please sign in to comment.