Skip to content

Commit bc7bead

Browse files
authored
feat: plot benchmark results (#66)
1. Add argument-baed options to table.mjs, letting users configure file format and/or output file path. 2. Support CSV output 3. Add a Jupyter notebook for plotting results
1 parent 8658c9f commit bc7bead

File tree

4 files changed

+506
-28
lines changed

4 files changed

+506
-28
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
/target
22
node_modules
3+
4+
# Benchmark Outputs
5+
*.csv
6+
*.png

pnpm-lock.yaml

+7-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

table.ipynb

+385
Large diffs are not rendered by default.

table.mjs

+110-15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
/**
2+
* @file table.mjs
3+
* @description Generate a table from criterion output.
4+
*
5+
* Usage:
6+
* pnpm table [options]
7+
*
8+
* # Options
9+
* -f,--format <format> Output format. 'md' or 'csv'. Default: 'md'
10+
* -o,--output <path> Output file path. Prints to stdout if not set.
11+
*/
112
import fs from 'node:fs'
213
import { markdownTable } from 'markdown-table'
314

@@ -24,29 +35,113 @@ async function readData() {
2435

2536
return data
2637
}
38+
/**
39+
* @param {string[]} argv
40+
*/
41+
function parseArgs(argv) {
42+
const opts = {
43+
/**
44+
* output format. Markdown or CSV.
45+
* @type {'markdown' | 'csv'}
46+
*/
47+
format: 'markdown',
48+
/**
49+
* Path to output file. `null` prints to stdout.
50+
* @type {string | null}
51+
*/
52+
output: null,
53+
};
2754

28-
async function main() {
55+
for(let arg = argv.shift(); arg; arg = argv.shift()) {
56+
switch(arg) {
57+
case '-f':
58+
case '--format': {
59+
60+
const format = argv.shift()?.trim()?.toLowerCase();
61+
if (!format) throw new TypeError('--format flag requires an argument');
62+
switch(format) {
63+
case 'md':
64+
case 'markdown':
65+
opts.format = 'markdown';
66+
break;
67+
case 'csv':
68+
opts.format = 'csv';
69+
break;
70+
default:
71+
throw new TypeError(`Invalid format '${format}', expected 'md' or 'csv'`);
72+
}
73+
break;
74+
}
75+
76+
case '-o':
77+
case '--output': {
78+
opts.output = argv.shift();
79+
break;
80+
}
81+
82+
// in case someone runs `pnpm table -- --format csv`
83+
case '--':
84+
continue
85+
}
86+
}
87+
88+
return opts;
89+
}
90+
91+
async function main(argv) {
2992
const data = await readData();
3093
const groups = Object.keys(data);
3194
const columns = Object.keys(data[groups[0]]);
3295
const rows = Object.keys(data[groups[0]][columns[0]]);
96+
const options = parseArgs(argv);
3397

98+
let out = '';
3499

35-
for (const group of groups) {
36-
console.log(`### ${group}`);
37-
console.log()
38-
const table = [["", ...columns]];
39-
for (const row of rows) {
40-
const column_numbers = columns.map((column) => data[group][column][row].duration_ms);
41-
const minimum = Math.min(...column_numbers);
42-
const column_values = column_numbers.map((number) => {
43-
return `\`${number.toFixed(1)} ms\` (${(number / minimum).toFixed(2)}x)`
44-
});
45-
table.push([row, ...column_values]);
100+
switch(options.format) {
101+
case 'markdown': {
102+
for (const group of groups) {
103+
out += `### ${group}\n`;
104+
const table = [["", ...columns]];
105+
for (const row of rows) {
106+
const column_numbers = columns.map((column) => data[group][column][row].duration_ms);
107+
const minimum = Math.min(...column_numbers);
108+
const column_values = column_numbers.map((number) => {
109+
return `\`${number.toFixed(1)} ms\` (${(number / minimum).toFixed(2)}x)`
110+
});
111+
table.push([row, ...column_values]);
112+
}
113+
out += markdownTable(table) + '\n';
114+
}
115+
break
46116
}
47-
console.log(markdownTable(table));
48-
console.log()
117+
118+
case 'csv': {
119+
const header = ['group', 'bench_name', 'tool', 'measurement', 'duration_ms'];
120+
out += header.join(',') + '\n';
121+
122+
for (const group of groups) {
123+
// swc, oxc
124+
for (const column of columns) {
125+
const benches = data[group][column]
126+
for (const bench in benches) {
127+
const { duration_ms } = benches[bench];
128+
out += `${group},${bench},${column},duration,${duration_ms}\n`;
129+
}
130+
}
131+
}
132+
}
133+
break;
134+
135+
default:
136+
throw new TypeError(`Unexpected output format '${options.format}'`);
137+
}
138+
139+
if (!options.output) {
140+
console.log(out);
141+
} else {
142+
await fs.promises.writeFile(options.output, out, 'utf8');
143+
console.log(`Saved table to ${options.output}`);
49144
}
50145
}
51146

52-
main()
147+
main(process.argv.slice(2)).catch(console.error);

0 commit comments

Comments
 (0)