127 lines
4.2 KiB
JavaScript
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;
|
|
}
|