-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbenchmark.js
More file actions
184 lines (157 loc) · 5.27 KB
/
benchmark.js
File metadata and controls
184 lines (157 loc) · 5.27 KB
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/**
* RailJS Benchmark Suite
* Performance tests to demonstrate characteristics and overhead
*/
import { Rail } from './rail.js';
// Benchmark utilities
const formatNumber = (num) => num.toLocaleString();
const formatOpsPerSec = (ops) => `${formatNumber(Math.round(ops))} ops/sec`;
function benchmark(name, fn, iterations = 100000) {
// Warmup
for (let i = 0; i < 1000; i++) fn();
// Actual benchmark
const start = performance.now();
for (let i = 0; i < iterations; i++) {
fn();
}
const end = performance.now();
const duration = end - start;
const opsPerSec = (iterations / duration) * 1000;
return { name, duration, iterations, opsPerSec };
}
async function benchmarkAsync(name, fn, iterations = 10000) {
// Warmup
for (let i = 0; i < 100; i++) await fn();
// Actual benchmark
const start = performance.now();
for (let i = 0; i < iterations; i++) {
await fn();
}
const end = performance.now();
const duration = end - start;
const opsPerSec = (iterations / duration) * 1000;
return { name, duration, iterations, opsPerSec };
}
function printResult(result) {
console.log(`\n${result.name}`);
console.log(` ${formatNumber(result.iterations)} iterations in ${result.duration.toFixed(2)}ms`);
console.log(` ${formatOpsPerSec(result.opsPerSec)}`);
}
// Benchmarks
console.log('🚀 RailJS Performance Benchmarks\n');
console.log('='.repeat(50));
// 1. Event emission with cloning (default)
let result = benchmark('Event emission (with cloning)', () => {
const rail = new Rail({ clone: true });
rail.on('test', () => {}, 'bench');
rail.emit('test', { data: 'test', nested: { value: 123 } });
});
printResult(result);
// 2. Event emission without cloning
result = benchmark('Event emission (without cloning)', () => {
const rail = new Rail({ clone: false });
rail.on('test', () => {}, 'bench');
rail.emit('test', { data: 'test', nested: { value: 123 } });
});
printResult(result);
// 3. Multiple listeners
result = benchmark('Event with 10 listeners (with cloning)', () => {
const rail = new Rail({ clone: true });
for (let i = 0; i < 10; i++) {
rail.on('test', () => {}, `bench-${i}`);
}
rail.emit('test', { data: 'test' });
});
printResult(result);
// 4. Multiple listeners without cloning
result = benchmark('Event with 10 listeners (without cloning)', () => {
const rail = new Rail({ clone: false });
for (let i = 0; i < 10; i++) {
rail.on('test', () => {}, `bench-${i}`);
}
rail.emit('test', { data: 'test' });
});
printResult(result);
// 5. Module attach/detach
result = benchmark('Module attach/detach', () => {
const rail = new Rail();
const module = {
name: 'bench-module',
connect(rail) {
rail.on('test', () => {}, 'bench-module');
},
};
rail.attach(module);
rail.detach('bench-module');
}, 10000);
printResult(result);
// 6. Deep cloning overhead
result = benchmark('Deep clone overhead', () => {
const rail = new Rail({ clone: true });
const data = {
user: { id: 1, name: 'Test' },
items: [{ id: 1 }, { id: 2 }, { id: 3 }],
metadata: { timestamp: Date.now(), nested: { deep: { value: true } } },
};
rail._deepClone(data);
}, 50000);
printResult(result);
// 7. Event history recording
result = benchmark('Event with history recording', () => {
const rail = new Rail();
rail.on('test', () => {}, 'bench');
rail.emit('test', { data: 'test' });
rail.getHistory(1);
});
printResult(result);
// 8. Async event emission
console.log('\n' + '='.repeat(50));
console.log('\nAsync Benchmarks (fewer iterations):\n');
result = await benchmarkAsync('Async event emission', async () => {
const rail = new Rail();
rail.on('test', async () => {
return 'result';
}, 'bench');
await rail.emitAsync('test', { data: 'test' });
}, 5000);
printResult(result);
// 9. Async with multiple handlers
result = await benchmarkAsync('Async event with 5 handlers', async () => {
const rail = new Rail();
for (let i = 0; i < 5; i++) {
rail.on('test', async () => `result-${i}`, `bench-${i}`);
}
await rail.emitAsync('test', { data: 'test' });
}, 2000);
printResult(result);
// 10. Memory efficiency test
console.log('\n' + '='.repeat(50));
console.log('\nMemory Efficiency:\n');
const rail = new Rail();
const memBefore = process.memoryUsage().heapUsed;
// Create 1000 modules with listeners
for (let i = 0; i < 1000; i++) {
rail.on('test-event', () => {}, `module-${i}`);
}
// Emit 1000 events
for (let i = 0; i < 1000; i++) {
rail.emit('test-event', { iteration: i, data: 'test' });
}
const memAfter = process.memoryUsage().heapUsed;
const memUsed = (memAfter - memBefore) / 1024 / 1024;
console.log(`1000 modules with 1000 events emitted`);
console.log(`Memory used: ${memUsed.toFixed(2)} MB`);
console.log(`Event history size: ${rail.getHistory().length} events`);
console.log(`Stats:`, rail.getStats());
// Comparison table
console.log('\n' + '='.repeat(50));
console.log('\n📊 Performance Summary:\n');
console.log('Cloning Impact:');
console.log(' - With cloning is ~2-3x slower than without');
console.log(' - Trade-off: Safety vs Speed');
console.log(' - Use clone: false for high-throughput scenarios\n');
console.log('Recommendations:');
console.log(' - For APIs handling <1000 req/sec: Keep cloning enabled');
console.log(' - For real-time systems (>10k events/sec): Disable cloning');
console.log(' - For typical web apps: Default settings are fine');
console.log('\n' + '='.repeat(50));