@monstermann/tinybench-pretty-printer
Version:
Customizable pretty-printer for tinybench benchmarks
383 lines (366 loc) • 11 kB
JavaScript
// src/tinybenchPrinter.ts
import { cli, markdown } from "@monstermann/tables";
// src/columns/name.ts
function name(options = {}) {
return {
header: options?.header ?? "name",
headerStyle: options?.headerStyle ?? ["bold"],
headerAlignment: options?.headerAlignment ?? "center",
rowAlignment: options?.rowAlignment ?? "left",
rowStyle: options?.rowStyle ?? [],
content({ task }) {
return task.name;
}
};
}
// src/columns/summary.ts
function summary(options = {}) {
const method = options.method ?? "%";
const fastestTitle = options.fastestTitle || "\u{1F947}";
return {
header: options?.header ?? "summary",
headerStyle: options?.headerStyle ?? ["bold"],
headerAlignment: options?.headerAlignment ?? "center",
rowAlignment: options?.rowAlignment ?? (method === "%" ? "center" : "right"),
rowStyle: options?.rowStyle ?? [],
content({ task, fastestTask, formatNumber: formatNumber2 }) {
const fastestHz = fastestTask.result.hz;
const hz = task.result.hz;
if (hz === fastestHz)
return fastestTitle;
switch (method) {
case "x":
return `${formatNumber2(fastestHz / hz)}x slower`;
case "%":
return `${formatNumber2((hz - fastestHz) / fastestHz * 100)}%`;
default:
return "";
}
}
};
}
// src/columns/ops.ts
function ops(options = {}) {
return {
header: options?.header ?? "ops/sec",
headerStyle: options?.headerStyle ?? ["bold"],
headerAlignment: options?.headerAlignment ?? "center",
rowAlignment: options?.rowAlignment ?? "right",
rowStyle: options?.rowStyle ?? ["blue"],
content({ task, tasks, formatCount }) {
return formatCount(
options.method ?? "shortest",
task.result.hz,
tasks.map((task2) => task2.result.hz)
);
}
};
}
// src/columns/time.ts
function time(options = {}) {
return {
header: options?.header ?? "time/op",
headerStyle: options?.headerStyle ?? ["bold"],
headerAlignment: options?.headerAlignment ?? "center",
rowAlignment: options?.rowAlignment ?? "right",
rowStyle: options?.rowStyle ?? ["yellow"],
content({ task, tasks, formatDuration }) {
return formatDuration(
options.method ?? "shortest",
task.result.mean,
tasks.map((task2) => task2.result.mean)
);
}
};
}
// src/columns/margin.ts
function margin(options = {}) {
return {
header: options?.header ?? "margin",
headerStyle: options?.headerStyle ?? ["bold"],
headerAlignment: options?.headerAlignment ?? "center",
rowAlignment: options?.rowAlignment ?? "center",
rowStyle: options?.rowStyle ?? ["magenta"],
content({ task, locales }) {
return `\xB1${task.result.rme.toLocaleString(locales, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%`;
}
};
}
// src/columns/samples.ts
function samples(options = {}) {
return {
header: options?.header ?? "samples",
headerStyle: options?.headerStyle ?? ["bold"],
headerAlignment: options?.headerAlignment ?? "center",
rowAlignment: options?.rowAlignment ?? "right",
rowStyle: options?.rowStyle ?? ["magenta"],
content({ task, tasks, formatCount }) {
return formatCount(
options.method ?? "shortest",
task.result.samples.length,
tasks.map((task2) => task2.result.samples.length)
);
}
};
}
// src/helpers/sortTasks.ts
function sortTasks(tasks, method) {
switch (method) {
case false:
return tasks;
case void 0:
return tasks.sort((a, b) => b.result.hz - a.result.hz);
case "asc":
return tasks.sort((a, b) => a.result.hz - b.result.hz);
case "desc":
return tasks.sort((a, b) => b.result.hz - a.result.hz);
default:
return method(tasks);
}
}
// src/helpers/mapRecord.ts
function mapRecord(record, map) {
const entries = Object.entries(record);
const mappedEntries = entries.map(([name2, value]) => [name2, map(value, name2)]);
return Object.fromEntries(mappedEntries);
}
// src/helpers/getFastestTask.ts
function getFastestTask(tasks) {
const [first] = tasks;
return first ? tasks.reduce((a, b) => a.result.hz < b.result.hz ? b : a, first) : void 0;
}
// src/helpers/getSlowestTask.ts
function getSlowestTask(tasks) {
const [first] = tasks;
return first ? tasks.reduce((a, b) => a.result.hz < b.result.hz ? a : b, first) : void 0;
}
// src/helpers/formatNumber.ts
function formatNumber(value, locales) {
return value.toLocaleString(locales, getPrecision(value));
}
function getPrecision(value) {
if (value < 1)
return { maximumSignificantDigits: 2 };
if (value < 10)
return { maximumSignificantDigits: 1 };
return { maximumFractionDigits: 0 };
}
// src/helpers/createNumberFormatter.ts
var createNumberFormatter = (locales) => (value) => formatNumber(value, locales);
// src/helpers/mean.ts
function mean(values) {
return values.reduce((a, b) => a + b, 0) / values.length;
}
// src/helpers/createDurationFormatter.ts
function createDurationFormatter(locales) {
return function formatDuration(method, value, values) {
switch (method) {
case "shortest":
return formatDuration(resolveMethod(value), value, values);
case "highest":
return formatDuration(resolveMethod(Math.max(...values)), value, values);
case "lowest":
return formatDuration(resolveMethod(Math.min(...values)), value, values);
case "mean":
return formatDuration(resolveMethod(mean(values)), value, values);
case "nanoseconds":
return `${formatNumber(value * 1e6, locales)}ns`;
case "microseconds":
return `${formatNumber(value * 1e3, locales)}\xB5s`;
case "milliseconds":
return `${formatNumber(value, locales)}ms`;
case "seconds":
return `${formatNumber(value / 1e3, locales)}s `;
}
};
}
function resolveMethod(ms) {
if (ms / 1e3 >= 1)
return "seconds";
if (ms >= 1)
return "milliseconds";
if (ms * 1e3 >= 1)
return "microseconds";
return "nanoseconds";
}
// src/helpers/createCountFormatter.ts
function createCountFormatter(locales) {
return function formatCount(method, value, values) {
switch (method) {
case "none":
return `${formatNumber(value, locales)} `;
case "shortest":
return formatCount(resolveMethod2(value), value, values);
case "highest":
return formatCount(resolveMethod2(Math.max(...values)), value, values);
case "lowest":
return formatCount(resolveMethod2(Math.min(...values)), value, values);
case "mean":
return formatCount(resolveMethod2(mean(values)), value, values);
case "thousands":
return `${formatNumber(value / 1e3, locales)}K`;
case "millions":
return `${formatNumber(value / 1e6, locales)}M`;
case "billions":
return `${formatNumber(value / 1e9, locales)}B`;
}
};
}
function resolveMethod2(value) {
if (value >= 1e9)
return "billions";
if (value >= 1e6)
return "millions";
if (value >= 1e3)
return "thousands";
return "none";
}
// src/tinybenchPrinter.ts
var TinybenchPrinterImpl = class _TinybenchPrinterImpl {
config;
constructor(config) {
this.config = config;
}
merge(config) {
return new _TinybenchPrinterImpl({
...this.config,
...config
});
}
order(order) {
return this.merge({ order });
}
column(name2, column) {
return this.merge({
columns: { ...this.config.columns, [name2]: column }
});
}
name(options) {
return this.column("name", name(options));
}
summary(options) {
return this.column("summary", summary(options));
}
ops(options) {
return this.column("ops", ops(options));
}
time(options) {
return this.column("time", time(options));
}
margin(options) {
return this.column("margin", margin(options));
}
samples(options) {
return this.column("samples", samples(options));
}
locales(locales) {
return this.merge({ locales });
}
sort(sort) {
return this.merge({ sort });
}
maxWidth(maxWidth) {
return this.merge({ maxWidth });
}
stdout(stdout) {
return this.merge({ stdout });
}
padding(padding) {
return this.merge({ padding });
}
borders(borders) {
return this.merge({ borders });
}
borderStyle(borderStyle) {
return this.merge({ borderStyle });
}
useHeader(useHeader) {
return this.merge({ useHeader });
}
useTopBorder(useTopBorder) {
return this.merge({ useTopBorder });
}
useBottomBorder(useBottomBorder) {
return this.merge({ useBottomBorder });
}
useLeftBorder(useLeftBorder) {
return this.merge({ useLeftBorder });
}
useRightBorder(useRightBorder) {
return this.merge({ useRightBorder });
}
useDividerBorder(useDividerBorder) {
return this.merge({ useDividerBorder });
}
useHeaderSeparator(useHeaderSeparator) {
return this.merge({ useHeaderSeparator });
}
useRowSeparator(useRowSeparator) {
return this.merge({ useRowSeparator });
}
prepareTableConfig(bench) {
const {
sort,
locales,
columns,
order,
...rest
} = this.config;
let tasks = bench.tasks.filter((task) => !!task.result).filter((task) => !task.result.error);
if (!tasks.length)
return;
tasks = sortTasks(tasks, sort);
const fastestTask = getFastestTask(tasks);
const slowestTask = getSlowestTask(tasks);
const formatNumber2 = createNumberFormatter(locales);
const formatDuration = createDurationFormatter(locales);
const formatCount = createCountFormatter(locales);
const rows = tasks.map((task) => mapRecord(columns, (column) => column.content({
task,
tasks,
fastestTask,
slowestTask,
formatNumber: formatNumber2,
formatDuration,
formatCount,
locales
})));
return {
...rest,
rows,
columns: order,
headerTitles: mapRecord(columns, (column) => column.header),
headerAlignments: mapRecord(columns, (column) => column.headerAlignment),
headerStyles: mapRecord(columns, (column) => column.headerStyle),
columnAlignments: mapRecord(columns, (column) => column.rowAlignment),
columnStyles: mapRecord(columns, (column) => column.rowStyle)
};
}
toCli(bench) {
const config = this.prepareTableConfig(bench);
if (!config)
return "";
return cli.createTable(config);
}
toMarkdown(bench) {
const config = this.prepareTableConfig(bench);
if (!config)
return "";
return markdown.createTable(config);
}
};
var tinybenchPrinter = new TinybenchPrinterImpl({
order: ["name", "summary", "ops", "time", "margin", "samples"],
columns: {
name: name(),
summary: summary(),
ops: ops(),
time: time(),
margin: margin(),
samples: samples()
}
});
export {
TinybenchPrinterImpl,
tinybenchPrinter
};