Skip to content

Commit 316a4c6

Browse files
committed
Adds mobx benchmark
1 parent f720066 commit 316a4c6

File tree

10 files changed

+490
-5
lines changed

10 files changed

+490
-5
lines changed

benchmark-comparisons/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The benchmarks provide **scientifically accurate** performance comparisons betwe
88
- **Substate** - Our lightweight state management library
99
- **Redux** - Industry standard state management
1010
- **Zustand** - Modern lightweight alternative
11+
- **MobX** - Reactive state with observable/action pattern
1112
- **Native JavaScript Objects** - Baseline performance
1213

1314
## 📊 What We Measure
@@ -107,6 +108,12 @@ All libraries are tested with:
107108
- Tests `getState()` property access
108109
- Estimates memory usage
109110

111+
### MobX (`benchmark-mobx.mjs`)
112+
- Tests `makeAutoObservable()` store creation
113+
- Measures `updateProp()` / `batchUpdate()` action performance
114+
- Tests direct observable property access
115+
- Estimates memory usage
116+
110117
### Native JavaScript (`benchmark-native.mjs`)
111118
- Tests direct object spread operations
112119
- Measures immutable update patterns
@@ -119,12 +126,14 @@ All libraries are tested with:
119126
- **Native JavaScript**: Fastest raw performance, no overhead
120127
- **Substate**: Optimized for reactive state with minimal overhead
121128
- **Zustand**: Good balance of features and performance
129+
- **MobX**: Reactive observable/action pattern with fine-grained updates
122130
- **Redux**: More overhead due to action/reducer pattern
123131

124132
### Use Case Recommendations
125133
- **High-frequency updates**: Consider Native JS or Substate
126134
- **Complex state logic**: Redux provides predictable patterns
127135
- **Simple state management**: Zustand offers good balance
136+
- **Reactive observable pattern**: MobX provides fine-grained reactivity
128137
- **Reactive features needed**: Substate provides built-in Pub/Sub
129138

130139
## 🔧 Customization
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// MobX Performance Benchmark
2+
//
3+
// Hardware Specifications:
4+
// - Processor: 13th Gen Intel(R) Core(TM) i7-13650HX (14 cores)
5+
// - RAM: 16 GB
6+
// - OS: Windows 10 Home (Version 2009)
7+
// - Node.js: v18+
8+
//
9+
import { makeAutoObservable } from 'mobx';
10+
import { createInitialState, measureTime, TEST_CONFIGS, logEnvironmentInfo, runBenchmark, saveBenchmarkResults } from './benchmark-utils.mjs';
11+
12+
console.log('🏃‍♂️ MobX Performance Benchmark');
13+
console.log('==============================\n');
14+
15+
// Log environment information
16+
logEnvironmentInfo();
17+
18+
// MobX store class with observable state and actions
19+
class MobxStore {
20+
constructor(initialState) {
21+
Object.assign(this, initialState);
22+
makeAutoObservable(this);
23+
}
24+
25+
updateProp(propKey, payload) {
26+
this[propKey] = { ...this[propKey], ...payload };
27+
}
28+
29+
batchUpdate(updates) {
30+
for (const [key, value] of Object.entries(updates)) {
31+
this[key] = value;
32+
}
33+
}
34+
}
35+
36+
// Benchmark function for MobX
37+
function benchmarkMobx(stateSize, iterations) {
38+
const initialState = createInitialState(stateSize);
39+
40+
// Store Creation
41+
const createResult = measureTime('Store Creation', () => {
42+
return new MobxStore({ ...initialState });
43+
});
44+
45+
const store = createResult.result;
46+
47+
// Single Update Performance
48+
const singleUpdateResult = measureTime('Single Update', () => {
49+
store.updateProp('prop0', { updated: true });
50+
return store;
51+
});
52+
53+
// Batch Updates Performance
54+
const batchUpdateResult = measureTime('Batch Updates', () => {
55+
const updates = {};
56+
for (let i = 0; i < Math.min(iterations, 1000); i++) {
57+
const propKey = `prop${i % stateSize}`;
58+
const currentProp = store[propKey];
59+
updates[propKey] = {
60+
...currentProp,
61+
counter: (currentProp.counter || 0) + 1
62+
};
63+
}
64+
store.batchUpdate(updates);
65+
return store;
66+
});
67+
68+
// Property Access Performance
69+
const accessResult = measureTime('Property Access', () => {
70+
let sum = 0;
71+
for (let i = 0; i < iterations; i++) {
72+
const propKey = `prop${i % stateSize}`;
73+
const value = store[propKey];
74+
sum += value?.id || 0;
75+
}
76+
return sum;
77+
});
78+
79+
// Estimate memory usage (MobX doesn't have built-in memory tracking)
80+
const stateSizeBytes = JSON.stringify(
81+
Object.fromEntries(
82+
Object.keys(initialState).map((k) => [k, store[k]])
83+
)
84+
).length;
85+
const estimatedMemoryKB = (stateSizeBytes * 50) / 1024; // Rough estimate for 50 states
86+
87+
return {
88+
creation: createResult.duration,
89+
singleUpdate: singleUpdateResult.duration,
90+
batchUpdate: batchUpdateResult.duration,
91+
propertyAccess: accessResult.duration,
92+
memoryKB: estimatedMemoryKB,
93+
batchIterations: Math.min(iterations, 1000),
94+
iterations
95+
};
96+
}
97+
98+
// Run benchmarks
99+
console.log('Starting MobX benchmarks...\n');
100+
101+
const results = [];
102+
103+
// Small state, many operations
104+
results.push(runBenchmark('Small State', TEST_CONFIGS.testSizes.small, TEST_CONFIGS.iterations.large, benchmarkMobx));
105+
106+
// Medium state, medium operations
107+
results.push(runBenchmark('Medium State', TEST_CONFIGS.testSizes.medium, TEST_CONFIGS.iterations.medium, benchmarkMobx));
108+
109+
// Large state, fewer operations
110+
results.push(runBenchmark('Large State', TEST_CONFIGS.testSizes.large, TEST_CONFIGS.iterations.small, benchmarkMobx));
111+
112+
// Save results to JSON file
113+
saveBenchmarkResults('mobx', results);
114+
115+
// Summary
116+
console.log('\n🎯 MOBX PERFORMANCE SUMMARY');
117+
console.log('============================');
118+
119+
results.forEach((result) => {
120+
console.log(`\n✅ ${result.testName}:`);
121+
console.log(` Store Creation: ${result.stats.creation.mean.toFixed(2)}ms`);
122+
console.log(` Single Update: ${result.stats.singleUpdate.mean.toFixed(2)}ms`);
123+
console.log(` Avg Update: ${(result.stats.batchUpdate.mean / result.results[0].batchIterations).toFixed(2)}ms`);
124+
console.log(` Avg Property Access: ${(result.stats.propertyAccess.mean / result.iterations).toFixed(2)}ms`);
125+
if (result.stats.memoryKB.mean > 0) {
126+
console.log(` Memory Usage: ${result.stats.memoryKB.mean.toFixed(0)}KB (estimated)`);
127+
}
128+
});
129+
130+
console.log('\n' + '='.repeat(50));
131+
console.log('🎉 MOBX BENCHMARK COMPLETE!');
132+
console.log('✅ MobX performance data ready for comparison');

benchmark-comparisons/benchmark-start.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ console.log(`📁 Output directory: ${runDir}\n`);
2727
// List of benchmarks to run
2828
const benchmarks = [
2929
'benchmark-substate.mjs',
30-
'benchmark-redux.mjs',
30+
'benchmark-redux.mjs',
3131
'benchmark-zustand.mjs',
32+
'benchmark-mobx.mjs',
3233
'benchmark-native.mjs'
3334
];
3435

benchmark-comparisons/generate-report.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,8 @@ function generateMarkdownTables(results) {
393393
markdown += `- **Native JavaScript**: Baseline performance for direct object operations\n`;
394394
markdown += `- **Substate**: Optimized for reactive state management with built-in features\n`;
395395
markdown += `- **Redux**: Mature ecosystem with predictable state updates\n`;
396-
markdown += `- **Zustand**: Lightweight alternative with minimal boilerplate\n\n`;
396+
markdown += `- **Zustand**: Lightweight alternative with minimal boilerplate\n`;
397+
markdown += `- **MobX**: Reactive state with observable/action pattern\n\n`;
397398

398399
markdown += `> **💡 Note**: Performance varies by use case. Choose based on your specific requirements, not just raw speed.\n`;
399400
markdown += `> **📊 Data**: Results are averaged over ${NUM_RUNS} runs with statistical analysis.\n`;
@@ -658,6 +659,7 @@ function generateComparisonTable(results) {
658659
console.log('- **Substate**: Optimized for reactive state management with built-in features');
659660
console.log('- **Redux**: Mature ecosystem with predictable state updates');
660661
console.log('- **Zustand**: Lightweight alternative with minimal boilerplate');
662+
console.log('- **MobX**: Reactive state with observable/action pattern');
661663
console.log();
662664

663665
console.log('> **💡 Note**: Performance varies by use case. Choose based on your specific requirements, not just raw speed.');

benchmark-comparisons/package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchmark-comparisons/package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@
44
"description": "Performance comparison benchmarks for Substate vs other state management libraries",
55
"type": "module",
66
"scripts": {
7-
"report": "node benchmark-start.mjs"
7+
"report": "node benchmark-start.mjs",
8+
"benchmark:substate": "node benchmark-substate.mjs",
9+
"benchmark:redux": "node benchmark-redux.mjs",
10+
"benchmark:zustand": "node benchmark-zustand.mjs",
11+
"benchmark:mobx": "node benchmark-mobx.mjs",
12+
"benchmark:native": "node benchmark-native.mjs",
13+
"benchmark:all": "node benchmark-start.mjs"
814
},
915
"dependencies": {
1016
"clone-deep": "^4.0.1",
17+
"mobx": "^6.13.0",
1118
"redux": "^4.2.1",
1219
"zustand": "^4.4.1"
1320
},

0 commit comments

Comments
 (0)