-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
table.mjs
147 lines (129 loc) · 3.87 KB
/
table.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* @file table.mjs
* @description Generate a table from criterion output.
*
* Usage:
* pnpm table [options]
*
* # Options
* -f,--format <format> Output format. 'md' or 'csv'. Default: 'md'
* -o,--output <path> Output file path. Prints to stdout if not set.
*/
import fs from 'node:fs'
import { markdownTable } from 'markdown-table'
async function readData() {
const data = {};
const dir = "./target/criterion";
const groups = await fs.promises.readdir(dir);
for (const group of groups) {
data[group] ||= {};
const benches = await fs.promises.readdir(`${dir}/${group}`);
for (const bench of benches) {
data[group][bench] ||= {};
const measurements = await fs.promises.readdir(`${dir}/${group}/${bench}`);
for (const measurement of measurements) {
const json = await import(`${dir}/${group}/${bench}/${measurement}/new/estimates.json`, { with: { type: "json" } });
const duration_ms = json.default.mean.point_estimate / 1_000_000;
data[group][bench][measurement] ||= { duration_ms };
}
}
}
return data
}
/**
* @param {string[]} argv
*/
function parseArgs(argv) {
const opts = {
/**
* output format. Markdown or CSV.
* @type {'markdown' | 'csv'}
*/
format: 'markdown',
/**
* Path to output file. `null` prints to stdout.
* @type {string | null}
*/
output: null,
};
for(let arg = argv.shift(); arg; arg = argv.shift()) {
switch(arg) {
case '-f':
case '--format': {
const format = argv.shift()?.trim()?.toLowerCase();
if (!format) throw new TypeError('--format flag requires an argument');
switch(format) {
case 'md':
case 'markdown':
opts.format = 'markdown';
break;
case 'csv':
opts.format = 'csv';
break;
default:
throw new TypeError(`Invalid format '${format}', expected 'md' or 'csv'`);
}
break;
}
case '-o':
case '--output': {
opts.output = argv.shift();
break;
}
// in case someone runs `pnpm table -- --format csv`
case '--':
continue
}
}
return opts;
}
async function main(argv) {
const data = await readData();
const groups = Object.keys(data);
const columns = Object.keys(data[groups[0]]);
const rows = Object.keys(data[groups[0]][columns[0]]);
const options = parseArgs(argv);
let out = '';
switch(options.format) {
case 'markdown': {
for (const group of groups) {
out += `### ${group}\n`;
const table = [["", ...columns]];
for (const row of rows) {
const column_numbers = columns.map((column) => data[group][column][row].duration_ms);
const minimum = Math.min(...column_numbers);
const column_values = column_numbers.map((number) => {
return `\`${number.toFixed(1)} ms\` (${(number / minimum).toFixed(2)}x)`
});
table.push([row, ...column_values]);
}
out += markdownTable(table) + '\n';
}
break
}
case 'csv': {
const header = ['group', 'bench_name', 'tool', 'measurement', 'duration_ms'];
out += header.join(',') + '\n';
for (const group of groups) {
// swc, oxc
for (const column of columns) {
const benches = data[group][column]
for (const bench in benches) {
const { duration_ms } = benches[bench];
out += `${group},${bench},${column},duration,${duration_ms}\n`;
}
}
}
}
break;
default:
throw new TypeError(`Unexpected output format '${options.format}'`);
}
if (!options.output) {
console.log(out);
} else {
await fs.promises.writeFile(options.output, out, 'utf8');
console.log(`Saved table to ${options.output}`);
}
}
main(process.argv.slice(2)).catch(console.error);