git.iko.soy/rowtype-yoga/purescript-yoga-om-strom
on e10b6e8 help sign in
199 lines 7.0 kB history raw
1#!/usr/bin/env node
3import fs from 'fs';
4import path from 'path';
5import { fileURLToPath } from 'url';
7const __dirname = path.dirname(fileURLToPath(import.meta.url));
8const resultsDir = path.join(__dirname, 'benchmark-results');
10// Parse benchmark results from a file
11function parseResults(content) {
12 const results = {};
13 const lines = content.split('\n');
15 let currentBench = null;
16 for (const line of lines) {
17 // Look for benchmark name: "๐Ÿƒ benchmark-name (runs: N)"
18 const nameMatch = line.match(/^๐Ÿƒ\s+([a-zA-Z0-9-_]+)\s+\(runs:/);
19 if (nameMatch) {
20 currentBench = nameMatch[1];
21 results[currentBench] = {};
22 continue;
23 }
25 // Look for statistics lines
26 if (currentBench) {
27 const minMatch = line.match(/^\s+min:\s+([\d.]+)\s+ms/);
28 const medianMatch = line.match(/^\s+median:\s+([\d.]+)\s+ms/);
29 const meanMatch = line.match(/^\s+mean:\s+([\d.]+)\s+ms/);
30 const maxMatch = line.match(/^\s+max:\s+([\d.]+)\s+ms/);
32 if (minMatch) results[currentBench].min = parseFloat(minMatch[1]);
33 if (medianMatch) results[currentBench].median = parseFloat(medianMatch[1]);
34 if (meanMatch) results[currentBench].mean = parseFloat(meanMatch[1]);
35 if (maxMatch) results[currentBench].max = parseFloat(maxMatch[1]);
36 }
37 }
39 return results;
42// Get the latest result files
43function getLatestResults() {
44 if (!fs.existsSync(resultsDir)) {
45 console.error('โŒ No benchmark-results directory found');
46 console.error(' Run ./run-minibench-all.sh first');
47 process.exit(1);
48 }
50 const files = fs.readdirSync(resultsDir);
52 const nodeFiles = files.filter(f => f.startsWith('node_')).sort().reverse();
53 const bunFiles = files.filter(f => f.startsWith('bun_')).sort().reverse();
54 const denoFiles = files.filter(f => f.startsWith('deno_')).sort().reverse();
56 if (nodeFiles.length === 0 || bunFiles.length === 0 || denoFiles.length === 0) {
57 console.error('โŒ Missing benchmark results for one or more runtimes');
58 console.error(` Found: Node=${nodeFiles.length}, Bun=${bunFiles.length}, Deno=${denoFiles.length}`);
59 process.exit(1);
60 }
62 return {
63 node: { file: nodeFiles[0], path: path.join(resultsDir, nodeFiles[0]) },
64 bun: { file: bunFiles[0], path: path.join(resultsDir, bunFiles[0]) },
65 deno: { file: denoFiles[0], path: path.join(resultsDir, denoFiles[0]) }
66 };
69// Format number with color based on comparison
70function formatWithComparison(value, isMin, isMid, isMax) {
71 const formatted = value.toFixed(2).padStart(8);
72 if (isMin) return `\x1b[32m${formatted}\x1b[0m`; // Green (fastest)
73 if (isMax) return `\x1b[31m${formatted}\x1b[0m`; // Red (slowest)
74 if (isMid) return `\x1b[33m${formatted}\x1b[0m`; // Yellow (middle)
75 return formatted;
78// Calculate speedup
79function speedup(baseline, comparison) {
80 return (baseline / comparison).toFixed(2);
83// Main comparison
84function compareResults() {
85 console.log('๐Ÿ“Š Strom Runtime Performance Comparison\n');
87 const latest = getLatestResults();
89 console.log('Using results from:');
90 console.log(` ๐ŸŸข Node: ${latest.node.file}`);
91 console.log(` ๐ŸŸ  Bun: ${latest.bun.file}`);
92 console.log(` ๐Ÿ”ต Deno: ${latest.deno.file}`);
93 console.log();
95 const nodeResults = parseResults(fs.readFileSync(latest.node.path, 'utf-8'));
96 const bunResults = parseResults(fs.readFileSync(latest.bun.path, 'utf-8'));
97 const denoResults = parseResults(fs.readFileSync(latest.deno.path, 'utf-8'));
99 // Get all benchmark names
100 const benchmarks = [...new Set([
101 ...Object.keys(nodeResults),
102 ...Object.keys(bunResults),
103 ...Object.keys(denoResults)
104 ])].sort();
106 if (benchmarks.length === 0) {
107 console.error('โŒ No benchmark results found in files');
108 process.exit(1);
109 }
111 // Header
112 console.log('โ”'.repeat(100));
113 console.log('Benchmark Name'.padEnd(35) +
114 'Node (ms)'.padStart(12) +
115 'Bun (ms)'.padStart(12) +
116 'Deno (ms)'.padStart(12) +
117 'Fastest'.padStart(12) +
118 'Speedup'.padStart(17));
119 console.log('โ”'.repeat(100));
121 let nodeFastestCount = 0;
122 let bunFastestCount = 0;
123 let denoFastestCount = 0;
125 // Data rows
126 for (const benchmark of benchmarks) {
127 const node = nodeResults[benchmark]?.median;
128 const bun = bunResults[benchmark]?.median;
129 const deno = denoResults[benchmark]?.median;
131 if (!node || !bun || !deno) {
132 console.log(`${benchmark.padEnd(35)} ${'N/A'.padStart(12).repeat(5)}`);
133 continue;
134 }
136 const values = [
137 { name: 'Node', value: node },
138 { name: 'Bun', value: bun },
139 { name: 'Deno', value: deno }
140 ].sort((a, b) => a.value - b.value);
142 const fastest = values[0].name;
143 const slowest = values[2].value;
144 const fastestValue = values[0].value;
146 if (fastest === 'Node') nodeFastestCount++;
147 if (fastest === 'Bun') bunFastestCount++;
148 if (fastest === 'Deno') denoFastestCount++;
150 const nodeFormatted = formatWithComparison(node, fastest === 'Node', fastest !== 'Node' && slowest !== node, slowest === node);
151 const bunFormatted = formatWithComparison(bun, fastest === 'Bun', fastest !== 'Bun' && slowest !== bun, slowest === bun);
152 const denoFormatted = formatWithComparison(deno, fastest === 'Deno', fastest !== 'Deno' && slowest !== deno, slowest === deno);
154 const speedupRatio = speedup(slowest, fastestValue);
156 console.log(
157 benchmark.padEnd(35) +
158 nodeFormatted +
159 ' ' +
160 bunFormatted +
161 ' ' +
162 denoFormatted +
163 ' ' +
164 fastest.padStart(8) +
165 `${speedupRatio}x`.padStart(9)
166 );
167 }
169 console.log('โ”'.repeat(100));
171 // Summary
172 console.log('\n๐Ÿ“ˆ Summary:');
173 console.log(` ๐ŸŸข Node fastest: ${nodeFastestCount} times`);
174 console.log(` ๐ŸŸ  Bun fastest: ${bunFastestCount} times`);
175 console.log(` ๐Ÿ”ต Deno fastest: ${denoFastestCount} times`);
176 console.log();
178 // Overall winner
179 const winner = [
180 { name: 'Node', count: nodeFastestCount },
181 { name: 'Bun', count: bunFastestCount },
182 { name: 'Deno', count: denoFastestCount }
183 ].sort((a, b) => b.count - a.count)[0];
185 console.log(`๐Ÿ† Overall Winner: ${winner.name} (fastest in ${winner.count}/${benchmarks.length} benchmarks)\n`);
187 // Calculate geometric mean for each runtime
188 const nodeMean = Math.pow(benchmarks.reduce((acc, b) => acc * (nodeResults[b]?.median || 1), 1), 1 / benchmarks.length);
189 const bunMean = Math.pow(benchmarks.reduce((acc, b) => acc * (bunResults[b]?.median || 1), 1), 1 / benchmarks.length);
190 const denoMean = Math.pow(benchmarks.reduce((acc, b) => acc * (denoResults[b]?.median || 1), 1), 1 / benchmarks.length);
192 console.log('โšก Geometric Mean Performance (lower is better):');
193 console.log(` ๐ŸŸข Node: ${nodeMean.toFixed(2)}ms`);
194 console.log(` ๐ŸŸ  Bun: ${bunMean.toFixed(2)}ms`);
195 console.log(` ๐Ÿ”ต Deno: ${denoMean.toFixed(2)}ms`);
196 console.log();
199compareResults();