Skip to content
This repository was archived by the owner on Oct 16, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benchmark/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MANUAL_BENCHMARK=false
52 changes: 52 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 🚀️ Benchmarks

The `Benchmark Test Suites` are supposed to showcase where AgileTs is roughly located in terms of performance.
I know a counter doesn't really show real world app performance,
but it is better than nothing.

## Counter Benchmark

```ts
1. Zustand x 30,591 ops/sec ±1.15% (61 runs sampled)
2. Redux x 30,239 ops/sec ±1.64% (63 runs sampled)
3. Mobx x 29,032 ops/sec ±1.24% (64 runs sampled)
4. AgileTs x 28,327 ops/sec ±2.96% (60 runs sampled)
5. Redux-Toolkit x 22,808 ops/sec ±1.79% (65 runs sampled)
6. Jotai x 22,479 ops/sec ±5.79% (63 runs sampled)
7. Valtio x 20,784 ops/sec ±2.75% (63 runs sampled)
8. Recoil x 14,351 ops/sec ±1.55% (65 runs sampled)
```

## 🏃 Running Benchmarks

The Benchmark tests run on top of the [`benchmark.js` library](https://github.com/bestiejs/benchmark.js/)
via [Playwright](https://github.com/microsoft/playwright) in the Chrome Browser.

Before starting, make sure you are in the `/benchmark` directory.

### 1️⃣ Install dependencies

To prepare the dependencies, run:
```ts
yarn install
```

### 2️⃣ Run Benchmark Test Suite

Execute the benchmark located in `./benchmarks/react/counter`.
```ts
yarn run test:counter
```

## ⭐️ Contribute

Get a part of AgileTs and start contributing. We welcome any meaningful contribution. 😀
To find out more about contributing, check out the [CONTRIBUTING.md](https://github.com/agile-ts/agile/blob/master/CONTRIBUTING.md).

<a href="https://codeclimate.com/github/agile-ts/agile/coverage.svg">
<img src="https://codeclimate.com/github/agile-ts/agile/badges/gpa.svg" alt="Maintainability"/>
</a>

## 🎉 Credits

The Benchmark CLI is inspired by [`exome`](https://github.com/Marcisbee/exome).
16 changes: 16 additions & 0 deletions benchmark/benchmarks/react/counter/bench/agilets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import ReactDom from 'react-dom';
import { Agile, Logger } from '@agile-ts/core';
import { useAgile } from '@agile-ts/react';

const AgileApp = new Agile({ logConfig: { level: Logger.level.ERROR } });
const COUNT = AgileApp.createState(0);

const App = () => {
const count = useAgile(COUNT);
return <h1 onClick={() => COUNT.set((state) => state + 1)}>{count}</h1>;
};

export default function (target: HTMLElement) {
ReactDom.render(<App key={'agilets'} />, target);
}
21 changes: 21 additions & 0 deletions benchmark/benchmarks/react/counter/bench/jotai.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import ReactDom from 'react-dom';
import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

const App = () => {
const [count, setCount] = useAtom(countAtom);
return (
<h1
onClick={() => {
setCount((v) => v + 1);
}}>
{count}
</h1>
);
};

export default function (target: HTMLElement) {
ReactDom.render(<App key={'jotai'} />, target);
}
19 changes: 19 additions & 0 deletions benchmark/benchmarks/react/counter/bench/mobx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import ReactDom from 'react-dom';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';

const appState = observable({
count: 0,
increment: action(function () {
appState.count += 1;
}),
});

const App = observer(() => {
return <h1 onClick={appState.increment}>{appState.count}</h1>;
});

export default function (target: HTMLElement) {
ReactDom.render(<App key={'mobx'} />, target);
}
22 changes: 22 additions & 0 deletions benchmark/benchmarks/react/counter/bench/recoil.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import ReactDom from 'react-dom';
import { atom, RecoilRoot, useRecoilState } from 'recoil';

const counterState = atom({
key: 'counterState',
default: 0,
});

const App = () => {
const [count, setCount] = useRecoilState(counterState);
return <h1 onClick={() => setCount((v) => v + 1)}>{count}</h1>;
};

export default function (target: HTMLElement) {
ReactDom.render(
<RecoilRoot>
<App key={'recoil'} />
</RecoilRoot>,
target
);
}
40 changes: 40 additions & 0 deletions benchmark/benchmarks/react/counter/bench/redux-toolkit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import ReactDom from 'react-dom';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useDispatch, useSelector } from 'react-redux';

const counterSlice = createSlice({
name: 'counter',
initialState: {
count: 0,
},
reducers: {
increment: (state) => {
state.count += 1;
},
},
});

const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
});

const App = () => {
const count = useSelector((state: any) => state.counter.count);
const dispatch = useDispatch();

return (
<h1 onClick={() => dispatch(counterSlice.actions.increment())}>{count}</h1>
);
};

export default function (target: HTMLElement) {
ReactDom.render(
<Provider store={store}>
<App key={'redux-toolkit'} />
</Provider>,
target
);
}
41 changes: 41 additions & 0 deletions benchmark/benchmarks/react/counter/bench/redux.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import ReactDom from 'react-dom';
import { combineReducers, createStore } from 'redux';
import { Provider, useDispatch, useSelector } from 'react-redux';

const increment = () => {
return {
type: 'INCREMENT',
};
};

const counter = (state = 0, action: any) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
};

const rootReducer = combineReducers({
counter,
});

const store = createStore(rootReducer);

const App = () => {
const count = useSelector((state: any) => state.counter);
const dispatch = useDispatch();

return <h1 onClick={() => dispatch(increment())}>{count}</h1>;
};

export default function (target: HTMLElement) {
ReactDom.render(
<Provider store={store}>
<App key={'redux'} />
</Provider>,
target
);
}
14 changes: 14 additions & 0 deletions benchmark/benchmarks/react/counter/bench/valtio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import ReactDom from 'react-dom';
import { proxy, useSnapshot } from 'valtio';

const state = proxy({ count: 0 });

function App() {
const snapshot = useSnapshot(state, { sync: true });
return <h1 onClick={() => state.count++}>{snapshot.count}</h1>;
}

export default function (target: HTMLElement) {
ReactDom.render(<App key={'valito'} />, target);
}
19 changes: 19 additions & 0 deletions benchmark/benchmarks/react/counter/bench/zustand.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import ReactDom from 'react-dom';
import create from 'zustand';

const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));

const App = () => {
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);

return <h1 onClick={increment}>{count}</h1>;
};

export default function (target: HTMLElement) {
ReactDom.render(<App key={'zustand'} />, target);
}
79 changes: 79 additions & 0 deletions benchmark/benchmarks/react/counter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import Benchmark, { Suite, Options } from 'benchmark';
import ReactDOM from 'react-dom';

// Files to run the Benchmark on
import agilets from './bench/agilets';
import jotai from './bench/jotai';
import mobx from './bench/mobx';
import recoil from './bench/recoil';
import redux from './bench/redux';
import reduxToolkit from './bench/redux-toolkit';
import valtio from './bench/valtio';
import zustand from './bench/zustand';

// @ts-ignore
// Benchmark.js requires an instance of itself globally
window.Benchmark = Benchmark;

// Create new Benchmark Test Suite
const suite = new Suite('Count');

// Retrieve the Element to render the Benchmark Test Suite in
const target = document.getElementById('bench')!;

// Increment Element
let increment: HTMLHeadingElement;

// Configures a single Benchmark Test
function configTest(renderElement: (target: HTMLElement) => void): Options {
return {
fn() {
// Execute increment action
increment.click();
},
onStart() {
// Render React Component in the target Element
renderElement(target);

// Retrieve Element to execute the increment action on
increment = target.querySelector('h1')!;
},
onComplete() {
// Set 'output' in the Benchmark itself to print it later
(this as any).output = parseInt(target.innerText, 10);

// Unmount React Component
ReactDOM.unmountComponentAtNode(target);
target.innerHTML = '';
},
};
}

// Add Tests to the Benchmark Test Suite
suite
.add('AgileTs', configTest(agilets))
.add('Jotai', configTest(jotai))
.add('Mobx', configTest(mobx))
.add('Recoil', configTest(recoil))
.add('Redux', configTest(redux))
.add('Redux-Toolkit', configTest(reduxToolkit))
.add('Valtio', configTest(valtio))
.add('Zustand', configTest(zustand))

// Add Listener
.on('start', function (this: any) {
console.log(`Starting ${this.name}`);
})
.on('cycle', (event: any) => {
console.log(String(event.target));
})
.on('complete', function (this: any) {
console.log(`Fastest is ${this.filter('fastest').map('name')}`);

// @ts-ignore
// Notify server that the Benchmark Test Suite has ended
window.TEST.ended = true;
})

// Run Benchmark Test Suite
.run({ async: true });
4 changes: 4 additions & 0 deletions benchmark/lodash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import _ from 'lodash';

// Benchmark.js requires lodash globally
window._ = _;
52 changes: 52 additions & 0 deletions benchmark/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "benchmark",
"version": "0.1.0",
"private": true,
"author": "BennoDev",
"license": "MIT",
"homepage": "https://agile-ts.org/",
"description": "Benchmark Tests",
"scripts": {
"test": "node -r esbuild-register run.ts",
"test:counter": "yarn test ./benchmarks/react/counter",
"test:fields": "yarn test ./benchmarks/react/fields",
"install:local:agile": "yalc add @agile-ts/core @agile-ts/react",
"install:public:agile": "yarn add @agile-ts/core @agile-ts/react"
},
"repository": {
"type": "git",
"url": "git+https://github.com/agile-ts/agile.git"
},
"dependencies": {
"@agile-ts/core": "^0.1.0",
"@agile-ts/react": "^0.1.0",
"@reduxjs/toolkit": "^1.6.0",
"benchmark": "^2.1.4",
"colorette": "^1.2.2",
"dotenv": "^10.0.0",
"esbuild": "^0.12.14",
"esbuild-register": "^2.6.0",
"jotai": "^1.1.2",
"lodash": "^4.17.21",
"mobx": "^6.3.2",
"mobx-react": "^7.2.0",
"playwright": "^1.12.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"recoil": "^0.3.1",
"redux": "^4.1.0",
"typescript": "^4.3.5",
"valtio": "^1.0.6",
"zustand": "^3.5.5"
},
"devDependencies": {
"@types/benchmark": "^2.1.0",
"@types/node": "^16.0.0",
"@types/react": "^17.0.13",
"@types/react-dom": "^17.0.8"
},
"bugs": {
"url": "https://github.com/agile-ts/agile/issues"
}
}
Loading