Files
handlebars.js/tests/bench/report.mjs
T
Igor Savin bf93bafeca Modernize benchmarks (#2132)
* Introduce benchmarks
2026-03-17 23:43:46 +01:00

127 lines
4.2 KiB
JavaScript

import { writeFileSync, mkdirSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
// ─── Formatting ──────────────────────────────────────────────────────────────
export function formatNs(ns) {
if (ns < 1000) return `${ns.toFixed(0)}ns`;
if (ns < 1_000_000) return `${(ns / 1000).toFixed(2)}µs`;
return `${(ns / 1_000_000).toFixed(2)}ms`;
}
export function formatOps(ops) {
if (ops >= 1_000_000) return `${(ops / 1_000_000).toFixed(2)}M`;
if (ops >= 1000) return `${(ops / 1000).toFixed(2)}K`;
return ops.toFixed(0);
}
function taskToRow(task) {
const { latency, throughput } = task.result;
const toNs = (ms) => ms * 1_000_000;
return {
name: task.name,
opsSec: formatOps(throughput.mean),
avg: formatNs(toNs(latency.mean)),
p50: formatNs(toNs(latency.p50)),
p75: formatNs(toNs(latency.p75)),
p99: formatNs(toNs(latency.p99)),
rme: latency.rme.toFixed(2),
samples: latency.samplesCount,
};
}
function completedTasks(bench) {
return bench.tasks.filter((t) => t.result?.state === 'completed');
}
// ─── Console output ──────────────────────────────────────────────────────────
export function printResults(bench) {
for (const task of bench.tasks) {
if (!task.result || task.result.state !== 'completed') {
console.error(
` FAILED: ${task.name}`,
task.result?.error || 'no result'
);
}
}
const results = completedTasks(bench).map((task) => {
const row = taskToRow(task);
return {
Name: row.name,
'ops/sec': row.opsSec,
avg: row.avg,
p50: row.p50,
p75: row.p75,
p99: row.p99,
'±%': row.rme,
samples: row.samples,
};
});
console.table(results);
}
export function printSectionHeader(title) {
const padded = ` ${title} `;
const width = Math.max(padded.length + 4, 59);
const inner = padded.padEnd(width - 2);
console.log(`${'─'.repeat(width - 2)}`);
console.log(`${inner}`);
console.log(`${'─'.repeat(width - 2)}`);
console.log();
}
// ─── Markdown report ─────────────────────────────────────────────────────────
export function saveMarkdownReport(
sections,
{ label, compileConfig, execConfig, date }
) {
const resultsDir = join(__dirname, 'results');
mkdirSync(resultsDir, { recursive: true });
const timestamp = date.toISOString().replace(/[:.]/g, '-').slice(0, 19);
const labelSlug = label ? `-${label.replace(/[^a-zA-Z0-9-_]/g, '_')}` : '';
const filename = `bench-${timestamp}${labelSlug}.md`;
const filepath = join(resultsDir, filename);
const lines = [];
const labelStr = label ? ` [${label}]` : '';
lines.push(`# Handlebars Benchmark Results${labelStr}`);
lines.push('');
lines.push(`- **Date:** ${date.toISOString()}`);
if (label) lines.push(`- **Label:** ${label}`);
lines.push(`- **Node:** ${process.version}`);
lines.push(`- **Platform:** ${process.platform} ${process.arch}`);
lines.push(
`- **Compile config:** warmup=${compileConfig.warmupIterations}, minIterations=${compileConfig.iterations}, time=${compileConfig.time}ms`
);
lines.push(
`- **Exec config:** warmup=${execConfig.warmupIterations}, minIterations=${execConfig.iterations}, time=${execConfig.time}ms`
);
lines.push('');
for (const { title, bench } of sections) {
lines.push(`## ${title}`);
lines.push('');
lines.push(
'| Benchmark | ops/sec | avg | p50 | p75 | p99 | ±% | samples |'
);
lines.push('|---|---|---|---|---|---|---|---|');
for (const row of completedTasks(bench).map(taskToRow)) {
lines.push(
`| ${row.name} | ${row.opsSec} | ${row.avg} | ${row.p50} | ${row.p75} | ${row.p99} | ${row.rme} | ${row.samples} |`
);
}
lines.push('');
}
writeFileSync(filepath, lines.join('\n'));
return filepath;
}