Skip to content
This repository was archived by the owner on Oct 16, 2024. It is now read-only.

Commit fd34c4f

Browse files
authored
Merge pull request #168 from agile-ts/performance_optimization
Performance optimization
2 parents bca1ed7 + d053c4e commit fd34c4f

File tree

76 files changed

+1579
-1136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1579
-1136
lines changed

benchmark/README.md

Lines changed: 72 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,75 +10,95 @@ https://stackoverflow.com/questions/28524653/what-do-the-results-from-benchmark-
1010
## Counter Benchmark
1111

1212
```ts
13-
1. Zustand x 30,591 ops/sec ±1.15% (61 runs sampled)
14-
2. Redux x 30,239 ops/sec ±1.64% (63 runs sampled)
15-
3. Mobx x 29,032 ops/sec ±1.24% (64 runs sampled)
16-
4. AgileTs x 28,327 ops/sec ±2.96% (60 runs sampled)
17-
5. Redux-Toolkit x 22,808 ops/sec ±1.79% (65 runs sampled)
18-
6. Jotai x 22,479 ops/sec ±5.79% (63 runs sampled)
19-
7. Valtio x 20,784 ops/sec ±2.75% (63 runs sampled)
20-
8. Recoil x 14,351 ops/sec ±1.55% (65 runs sampled)
13+
1. AgileTs.............43028 ops/se ±2.45 (63 runs sampled)
14+
2. PulseJs.............41086 ops/se ±2.60 (63 runs sampled)
15+
3. Nano Stores.........31933 ops/se ±1.27 (63 runs sampled)
16+
4. Zustand.............29329 ops/se ±1.30 (62 runs sampled)
17+
5. Redux...............28845 ops/se ±2.47 (61 runs sampled)
18+
6. Hookstate...........27555 ops/se ±5.00 (59 runs sampled)
19+
7. Mobx................27427 ops/se ±3.69 (62 runs sampled)
20+
8. Redux-Toolkit.......22191 ops/se ±1.06 (65 runs sampled)
21+
9. Jotai...............22157 ops/se ±4.10 (63 runs sampled)
22+
10. Valtio..............21089 ops/se ±0.77 (63 runs sampled)
23+
11. Recoil..............13926 ops/se ±2.12 (62 runs sampled)
24+
25+
Fastest is AgileTs
2126
```
2227

2328
## 1000 Fields
2429

2530
```ts
2631
// 1 Field
27-
Agile Collection x 13,729 ops/sec ±3.42% (60 runs sampled) [updatedFieldsCount: 76468, renderFieldsCount: 73]
28-
Agile State x 19,008 ops/sec ±1.87% (66 runs sampled) [updatedFieldsCount: 103559, renderFieldsCount: 72]
29-
Agile nested State x 21,119 ops/sec ±1.45% (64 runs sampled) [updatedFieldsCount: 116226, renderFieldsCount: 72]
30-
Hookstate x 20,026 ops/sec ±0.68% (64 runs sampled) [updatedFieldsCount: 112513, renderFieldsCount: 112513]
31-
Jotai x 16,372 ops/sec ±3.34% (63 runs sampled) [updatedFieldsCount: 90275, renderFieldsCount: 90275]
32-
Mobx x 15,892 ops/sec ±3.42% (60 runs sampled) [updatedFieldsCount: 82400, renderFieldsCount: 82400]
33-
Nano Stores x 21,455 ops/sec ±1.00% (66 runs sampled) [updatedFieldsCount: 114136, renderFieldsCount: 114136]
34-
Recoil x 11,504 ops/sec ±3.44% (63 runs sampled) [updatedFieldsCount: 61553, renderFieldsCount: 61554]
35-
Redux x 13,070 ops/sec ±2.73% (62 runs sampled) [updatedFieldsCount: 69239, renderFieldsCount: 69240]
36-
Valtio x 9,962 ops/sec ±2.60% (60 runs sampled) [updatedFieldsCount: 54290, renderFieldsCount: 108579]
32+
1. Agile nested State..27992 ops/se ±1.73 (64 runs sampled)
33+
2. Pulse Collection....25547 ops/se ±1.04 (64 runs sampled)
34+
3. Agile State.........23962 ops/se ±2.16 (64 runs sampled)
35+
4. Nano Stores.........20662 ops/se ±1.76 (65 runs sampled)
36+
5. Hookstate...........19430 ops/se ±1.81 (61 runs sampled)
37+
6. Agile Collection....18491 ops/se ±2.13 (65 runs sampled)
38+
7. Jotai...............16029 ops/se ±3.39 (62 runs sampled)
39+
8. Mobx................15631 ops/se ±3.42 (61 runs sampled)
40+
9. Redux...............12698 ops/se ±2.86 (61 runs sampled)
41+
10. Recoil..............11183 ops/se ±3.73 (61 runs sampled)
42+
11. Valtio..............9728 ops/se ±2.81 (62 runs sampled)
43+
44+
Fastest is Agile nested State
3745

3846
// 10 Fields
39-
Agile Collection x 10,651 ops/sec ±4.14% (58 runs sampled) [updatedFieldsCount: 56668, renderFieldsCount: 582]
40-
Agile State x 16,175 ops/sec ±1.55% (65 runs sampled) [updatedFieldsCount: 87481, renderFieldsCount: 80]
41-
Agile nested State x 20,703 ops/sec ±1.27% (65 runs sampled) [updatedFieldsCount: 113946, renderFieldsCount: 712]
42-
Hookstate x 18,733 ops/sec ±3.14% (59 runs sampled) [updatedFieldsCount: 105792, renderFieldsCount: 105801]
43-
Jotai x 15,602 ops/sec ±3.65% (61 runs sampled) [updatedFieldsCount: 85977, renderFieldsCount: 85986]
44-
Mobx x 9,283 ops/sec ±3.16% (52 runs sampled) [updatedFieldsCount: 50806, renderFieldsCount: 508060]
45-
Nano Stores x 20,125 ops/sec ±1.62% (62 runs sampled) [updatedFieldsCount: 108704, renderFieldsCount: 108713]
46-
Recoil x 11,103 ops/sec ±4.50% (61 runs sampled) [updatedFieldsCount: 62920, renderFieldsCount: 62939]
47-
Redux x 8,728 ops/sec ±1.61% (64 runs sampled) [updatedFieldsCount: 50794, renderFieldsCount: 507950]
48-
Valtio x 3,557 ops/sec ±2.96% (23 runs sampled) [updatedFieldsCount: 22473, renderFieldsCount: 449450]
47+
1. Agile nested State..27658 ops/se ±1.99 (64 runs sampled)
48+
2. Pulse Collection....24839 ops/se ±1.31 (65 runs sampled)
49+
3. Agile State.........19853 ops/se ±2.15 (64 runs sampled)
50+
4. Nano Stores.........19479 ops/se ±2.12 (60 runs sampled)
51+
5. Hookstate...........18104 ops/se ±3.37 (60 runs sampled)
52+
6. Jotai...............15472 ops/se ±2.45 (62 runs sampled)
53+
7. Agile Collection....13352 ops/se ±3.67 (61 runs sampled)
54+
8. Recoil..............10522 ops/se ±3.79 (58 runs sampled)
55+
9. Mobx................9477 ops/se ±1.94 (62 runs sampled)
56+
10. Redux...............8434 ops/se ±2.67 (47 runs sampled)
57+
11. Valtio..............3532 ops/se ±2.27 (23 runs sampled)
58+
59+
Fastest is Agile nested State
4960

5061
// 100 Fields
51-
Agile Collection x 3,897 ops/sec ±3.01% (25 runs sampled) [updatedFieldsCount: 24427, renderFieldsCount: 2502]
52-
Agile State x 8,355 ops/sec ±0.85% (67 runs sampled) [updatedFieldsCount: 46249, renderFieldsCount: 173]
53-
Agile nested State x 18,641 ops/sec ±1.17% (63 runs sampled) [updatedFieldsCount: 98669, renderFieldsCount: 6802]
54-
Hookstate x 14,865 ops/sec ±2.51% (61 runs sampled) [updatedFieldsCount: 81616, renderFieldsCount: 81715]
55-
Jotai x 12,676 ops/sec ±3.09% (61 runs sampled) [updatedFieldsCount: 65930, renderFieldsCount: 66029]
56-
Mobx x 1,812 ops/sec ±1.49% (63 runs sampled) [updatedFieldsCount: 9639, renderFieldsCount: 963900]
57-
Nano Stores x 16,283 ops/sec ±1.39% (62 runs sampled) [updatedFieldsCount: 84772, renderFieldsCount: 84871]
58-
Recoil x 9,418 ops/sec ±2.94% (62 runs sampled) [updatedFieldsCount: 52425, renderFieldsCount: 52624]
59-
Redux x 1,896 ops/sec ±1.74% (62 runs sampled) [updatedFieldsCount: 10133, renderFieldsCount: 1013400]
60-
Valtio x 472 ops/sec ±2.97% (61 runs sampled) [updatedFieldsCount: 2494, renderFieldsCount: 498700]
62+
1. Agile nested State..24124 ops/se ±1.05 (65 runs sampled)
63+
2. Pulse Collection....21912 ops/se ±1.35 (66 runs sampled)
64+
3. Nano Stores.........15638 ops/se ±1.63 (62 runs sampled)
65+
4. Hookstate...........13986 ops/se ±2.28 (59 runs sampled)
66+
5. Jotai...............12167 ops/se ±2.78 (63 runs sampled)
67+
6. Agile State.........9175 ops/se ±1.56 (51 runs sampled)
68+
7. Recoil..............8717 ops/se ±3.51 (49 runs sampled)
69+
8. Agile Collection....4177 ops/se ±1.64 (61 runs sampled)
70+
9. Redux...............1763 ops/se ±1.06 (63 runs sampled)
71+
10. Mobx................1699 ops/se ±1.82 (62 runs sampled)
72+
11. Valtio..............432 ops/se ±2.18 (60 runs sampled)
73+
74+
Fastest is Agile nested State
6175

6276
// 1000 Fields
63-
Agile Collection x 503 ops/sec ±2.23% (62 runs sampled) [updatedFieldsCount: 2616, renderFieldsCount: 3520]
64-
Agile State x 1,437 ops/sec ±1.48% (59 runs sampled) [updatedFieldsCount: 7569, renderFieldsCount: 1061]
65-
Agile nested State x 9,411 ops/sec ±1.54% (56 runs sampled) [updatedFieldsCount: 46693, renderFieldsCount: 33243]
66-
Hookstate x 4,539 ops/sec ±3.61% (27 runs sampled) [updatedFieldsCount: 26381, renderFieldsCount: 27380]
67-
Jotai x 4,014 ops/sec ±5.35% (53 runs sampled) [updatedFieldsCount: 20390, renderFieldsCount: 21389]
68-
Mobx x 151 ops/sec ±0.75% (59 runs sampled) [updatedFieldsCount: 786, renderFieldsCount: 786000]
69-
Nano Stores x 5,511 ops/sec ±6.27% (32 runs sampled) [updatedFieldsCount: 31266, renderFieldsCount: 32265]
70-
Recoil x 3,562 ops/sec ±3.16% (58 runs sampled) [updatedFieldsCount: 18503, renderFieldsCount: 20502]
71-
Redux x 165 ops/sec ±1.40% (57 runs sampled) [updatedFieldsCount: 858, renderFieldsCount: 859000]
72-
Valtio x 38.76 ops/sec ±5.50% (42 runs sampled) [updatedFieldsCount: 215, renderFieldsCount: 429000]
77+
1. Agile nested State..10756 ops/se ±1.43 (58 runs sampled)
78+
2. Pulse Collection....9774 ops/se ±2.39 (58 runs sampled)
79+
3. Hookstate...........4737 ops/se ±4.33 (58 runs sampled)
80+
4. Nano Stores.........4638 ops/se ±6.40 (28 runs sampled)
81+
5. Jotai...............3352 ops/se ±4.17 (53 runs sampled)
82+
6. Recoil..............3139 ops/se ±4.69 (54 runs sampled)
83+
7. Agile State.........1389 ops/se ±1.52 (57 runs sampled)
84+
8. Agile Collection....500 ops/se ±1.89 (61 runs sampled)
85+
9. Redux...............154 ops/se ±1.48 (57 runs sampled)
86+
10. Mobx................144 ops/se ±1.06 (55 runs sampled)
87+
11. Valtio..............37 ops/se ±4.26 (40 runs sampled)
88+
89+
Fastest is Agile nested State
7390
```
7491

7592
## Computed
7693

7794
```ts
78-
Agile Hard Coded x 23,201 ops/sec ±1.39% (64 runs sampled)
79-
Agile Auto Tracking x 22,661 ops/sec ±3.31% (60 runs sampled)
80-
Jotai x 18,489 ops/sec ±5.43% (62 runs sampled)
81-
Recoil x 10,312 ops/sec ±2.57% (64 runs sampled)
95+
1. Agile Hard Coded....32079 ops/se ±1.51 (62 runs sampled)
96+
2. Agile Auto Tracking.30974 ops/se ±2.21 (64 runs sampled)
97+
3. Nano Stores.........28821 ops/se ±1.49 (64 runs sampled)
98+
4. Jotai...............18922 ops/se ±2.12 (61 runs sampled)
99+
5. Recoil..............10103 ops/se ±2.47 (64 runs sampled)
100+
101+
Fastest is Agile Hard Coded
82102
```
83103

84104
## 🏃 Running Benchmarks

benchmark/benchmarkManager.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
export interface CycleResultInterface {
2+
name: string;
3+
opsInSec: number;
4+
failRate: number;
5+
ranSampleCount: number;
6+
ui: any;
7+
}
8+
9+
export function getCycleResult(event: any): CycleResultInterface {
10+
return {
11+
name: event.target.name,
12+
opsInSec: Math.round(event.target.hz),
13+
failRate: event.target.stats.rme.toFixed(2),
14+
ranSampleCount: event.target.stats.sample.length,
15+
ui: {
16+
count: event.target.output,
17+
},
18+
};
19+
}
20+
21+
export function startBenchmarkLog(testSuiteName: string): void {
22+
console.log(`{white Starting Benchmark "{magenta.bold ${testSuiteName}}"..}`);
23+
}
24+
25+
export function cycleLog(
26+
cycleResult: CycleResultInterface,
27+
...addition: any[]
28+
): void {
29+
console.log(
30+
`{gray ..Proceeded {green.bold ${cycleResult.name}} - {yellow ${cycleResult.opsInSec} ops/sec}}`,
31+
...addition
32+
);
33+
}
34+
35+
export function endBenchmarkLog(
36+
testSuiteName: string,
37+
results: CycleResultInterface[],
38+
fastest: string[]
39+
): void {
40+
console.log(`{white ..End Benchmark "{magenta.bold ${testSuiteName}}"}\n\n`);
41+
42+
results.sort((a, b) => {
43+
if (a.opsInSec < b.opsInSec) return 1;
44+
return -1;
45+
});
46+
47+
let resultString = '';
48+
for (let i = 0; i < results.length; i++) {
49+
const cycleResult = results[i];
50+
51+
// Build Cycle Result Log
52+
const cycleString = `{bold.bgGreen ${
53+
i + 1
54+
}.} {bold.blue ${cycleResult.name
55+
.padEnd(20, '.')
56+
.replace(/(\.+)$/, '{red $1}')}}{yellow ${
57+
cycleResult.opsInSec
58+
} ops/se} {gray ±${cycleResult.failRate}%} (${
59+
cycleResult.ranSampleCount
60+
} runs sampled)`;
61+
62+
resultString += `${cycleString}${i < results.length - 1 ? '\n' : ''}`;
63+
}
64+
65+
// Build Leaderboard Header
66+
console.log('{bgYellow.white.bold Leaderboard:}\n');
67+
68+
// Print Leaderboard
69+
console.log(resultString);
70+
71+
// Print fastest
72+
console.log(`\n{bold Fastest is {bold.green ${fastest}}}\n`);
73+
}

benchmark/benchmarks/react/1000fields/bench/agilets/collection.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import React from 'react';
22
import ReactDom from 'react-dom';
3-
import { createCollection } from '@agile-ts/core';
3+
import { createCollection, LogCodeManager } from '@agile-ts/core';
44
import { useAgile, useValue } from '@agile-ts/react';
55

6+
LogCodeManager.getLogger().isActive = false;
7+
68
export default function (target: HTMLElement, fieldsCount: number) {
79
const FIELDS = createCollection({
810
initialData: Array.from(Array(fieldsCount).keys()).map((i) => ({

benchmark/benchmarks/react/1000fields/bench/agilets/nestedState.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import * as React from 'react';
22
import * as ReactDom from 'react-dom';
3-
import { createState, State } from '@agile-ts/core';
3+
import { createState, LogCodeManager, State } from '@agile-ts/core';
44
import { useAgile } from '@agile-ts/react';
55

6+
LogCodeManager.getLogger().isActive = false;
7+
68
export default function (target: HTMLElement, fieldsCount: number) {
79
const FIELDS = createState(
810
Array.from(Array(fieldsCount).keys()).map((i) =>

benchmark/benchmarks/react/1000fields/bench/agilets/state.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import React from 'react';
22
import ReactDom from 'react-dom';
3-
import { createState } from '@agile-ts/core';
3+
import { createState, LogCodeManager } from '@agile-ts/core';
44
import { useAgile } from '@agile-ts/react';
55

6+
LogCodeManager.getLogger().isActive = false;
7+
68
export default function (target: HTMLElement, fieldsCount: number) {
79
const FIELDS = createState(
810
Array.from(Array(fieldsCount).keys()).map((i) => `Field #${i + 1}`)

benchmark/benchmarks/react/1000fields/index.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
import Benchmark, { Suite, Options } from 'benchmark';
21
import ReactDOM from 'react-dom';
2+
import Benchmark, { Suite, Options } from 'benchmark';
3+
import {
4+
cycleLog,
5+
CycleResultInterface,
6+
endBenchmarkLog,
7+
getCycleResult,
8+
startBenchmarkLog,
9+
} from '../../../benchmarkManager';
310

411
// Files to run the Benchmark on
512
import agileCollection from './bench/agilets/collection';
@@ -66,14 +73,16 @@ function configTest(
6673
};
6774
}
6875

76+
const results: CycleResultInterface[] = [];
77+
6978
// Add Tests to the Benchmark Test Suite
7079
suite
7180
.add('Agile Collection', configTest(agileCollection))
7281
.add('Agile State', configTest(agileState))
7382
.add('Agile nested State', configTest(agileNestedState))
7483
.add('Pulse Collection', configTest(pulseCollection))
75-
.add('Pulse State', configTest(pulseState))
76-
.add('Pulse nested State', configTest(pulseNestedState))
84+
// .add('Pulse State', configTest(pulseState))
85+
// .add('Pulse nested State', configTest(pulseNestedState))
7786
.add('Hookstate', configTest(hookstate))
7887
.add('Jotai', configTest(jotai))
7988
.add('Mobx', configTest(mobx))
@@ -84,16 +93,18 @@ suite
8493

8594
// Add Listener
8695
.on('start', function (this: any) {
87-
console.log(`Starting ${this.name}`);
96+
startBenchmarkLog(this.name);
8897
})
8998
.on('cycle', (event: any) => {
90-
console.log(
91-
String(event.target),
99+
const cycleResult = getCycleResult(event);
100+
cycleLog(
101+
cycleResult,
92102
`[updatedFieldsCount: ${event.target.updatedFieldsCount}, renderFieldsCount: ${event.target.renderFieldsCount}]`
93103
);
104+
results.push(cycleResult);
94105
})
95106
.on('complete', function (this: any) {
96-
console.log(`Fastest is ${this.filter('fastest').map('name')}`);
107+
endBenchmarkLog(this.name, results, this.filter('fastest').map('name'));
97108

98109
// @ts-ignore
99110
// Notify server that the Benchmark Test Suite has ended

benchmark/benchmarks/react/computed/bench/agilets/autoTracking.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import React from 'react';
22
import ReactDom from 'react-dom';
3-
import { createComputed, createState } from '@agile-ts/core';
3+
import { createComputed, createState, LogCodeManager } from '@agile-ts/core';
44
import { useAgile } from '@agile-ts/react';
55

6+
LogCodeManager.getLogger().isActive = false;
67
const COUNT = createState(0);
78
const COMPUTED_COUNT = createComputed(() => {
89
return COUNT.value * 5;

benchmark/benchmarks/react/computed/bench/agilets/hardCoded.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import React from 'react';
22
import ReactDom from 'react-dom';
3-
import { createComputed, createState } from '@agile-ts/core';
3+
import { createComputed, createState, LogCodeManager } from '@agile-ts/core';
44
import { useAgile } from '@agile-ts/react';
55

6+
LogCodeManager.getLogger().isActive = false;
67
const COUNT = createState(0);
78
const COMPUTED_COUNT = createComputed(
89
() => {

benchmark/benchmarks/react/computed/index.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import ReactDOM from 'react-dom';
22
import Benchmark, { Suite, Options } from 'benchmark';
3+
import {
4+
cycleLog,
5+
CycleResultInterface,
6+
endBenchmarkLog,
7+
getCycleResult,
8+
startBenchmarkLog,
9+
} from '../../../benchmarkManager';
310

411
// Files to run the Benchmark on
512
import agileAutoTracking from './bench/agilets/autoTracking';
@@ -13,7 +20,7 @@ import recoil from './bench/recoil';
1320
window.Benchmark = Benchmark;
1421

1522
// Create new Benchmark Test Suite
16-
const suite = new Suite('Count');
23+
const suite = new Suite('Computed');
1724

1825
// Retrieve the Element to render the Benchmark Test Suite in
1926
const target = document.getElementById('bench')!;
@@ -53,6 +60,8 @@ function configTest(renderElement: (target: HTMLElement) => void): Options {
5360
};
5461
}
5562

63+
const results: CycleResultInterface[] = [];
64+
5665
// Add Tests to the Benchmark Test Suite
5766
suite
5867
.add('Agile Auto Tracking', configTest(agileAutoTracking))
@@ -63,16 +72,18 @@ suite
6372

6473
// Add Listener
6574
.on('start', function (this: any) {
66-
console.log(`Starting ${this.name}`);
75+
startBenchmarkLog(this.name);
6776
})
6877
.on('cycle', (event: any) => {
69-
console.log(
70-
String(event.target),
78+
const cycleResult = getCycleResult(event);
79+
cycleLog(
80+
cycleResult,
7181
`[Count: ${event.target.output}, ComputedCount: ${event.target.computedOutput}]`
7282
);
83+
results.push(cycleResult);
7384
})
7485
.on('complete', function (this: any) {
75-
console.log(`Fastest is ${this.filter('fastest').map('name')}`);
86+
endBenchmarkLog(this.name, results, this.filter('fastest').map('name'));
7687

7788
// @ts-ignore
7889
// Notify server that the Benchmark Test Suite has ended

0 commit comments

Comments
 (0)