Skip to content

Commit

Permalink
feat: 54% perf boost and added reactively bench
Browse files Browse the repository at this point in the history
  • Loading branch information
mihar-22 committed Dec 6, 2022
1 parent 530ed2e commit 6576447
Show file tree
Hide file tree
Showing 19 changed files with 347 additions and 207 deletions.
File renamed without changes.
39 changes: 32 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -632,13 +632,6 @@ found [here](./bench/layers.js).
Each column represents how deep computations were layered. The average time taken to update the
computation out of a 100 runs is used for each library.

> ❗ Do note that only Maverick and Solid JS are feature complete below which includes nested
> effects, arbritrary node disposal, context, and error handling.
> ❗ Nearly all computations in a real world app are going to be less than 10 layers deep, so
> only the first column really matters. What this benchmark is really showing is how notification
> propagation scales with computation depth.
#### Sync

<img src="./bench/layers.png" alt="Layers sync benchmark table" width="350px" />
Expand All @@ -647,6 +640,38 @@ computation out of a 100 runs is used for each library.

<img src="./bench/layers-batched.png" alt="Layers batched benchmark table" width="350px" />

#### Notes

- Only Maverick and Solid JS are feature complete below which includes nested effects, arbritrary
node disposal, context, and error handling.
- Nearly all computations in a real world app are going to be less than 10 layers deep, so only the
first column really matters. What this benchmark is really showing is how notification propagation
scales with computation depth.

### Reactively

This benchmark was taken from [`reactively`](https://github.com/modderme123/reactively). It sets
up various computation graphs with a set number of sources (e.g., `1000x5` is 1000 computations with
a tree depth of 5). The benchmark measures how long it takes for changes to be applied after static
or dynamic updates are made to the graph (i.e., pick a node and update its value).

<img src="./bench/reactively.png" alt="Reactively benchmark charts" />

#### Notes

- This is not an apples to apples comparison. Reactively and Preact Signals are not feature
complete as they currently don't support scope tracking, nested effects, context, error handling,
and arbritray subtree disposal. This means their overall tracking logic is simplified. You can
safely apply a ~10-15% penalty to their scores (do note we haven't applied it here).
- This assumes Solid JS is in batch-only mode which is not realistic as a real world app won't
have batch applied everywhere.
- Preact Signals is reporting unusually slow numbers for the Wide Dense and Large Web App charts
which may be the result of a bug or something to do with how they've modelled the computation
graph. Issue is being tracked [here](https://github.com/preactjs/signals/issues/274).
- Only Maverick uses a `Set` to track observers/dependencies. This means multiple observer calls in
other libraries will result in an edge being created every time a signal is called. This is one of
the reasons why Maverick does consistenly well across small and large data sets.

## Inspiration

`@maverick-js/observables` was made possible based on my learnings from:
Expand Down
Binary file modified bench/layers-batched.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 6 additions & 10 deletions bench/layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,12 @@ const SOLUTIONS = {
const isSolution = (layers, answer) => answer.every((_, i) => SOLUTIONS[layers][i] === _);

async function main() {
const report = {
'preact/signals': { fn: runPreact, runs: [] },
S: { fn: runS, runs: [] },
cellx: { fn: runCellx, runs: [] },
solid: { fn: runSolid, runs: [] },
};

if (BATCHED) {
report.maverick = { fn: runMaverick, runs: [], avg: [] };
}
const report = {};
report['preact/signals'] = { fn: runPreact, runs: [] };
if (BATCHED) report.maverick = { fn: runMaverick, runs: [], avg: [] };
report.S = { fn: runS, runs: [] };
report.cellx = { fn: runCellx, runs: [] };
report.solid = { fn: runSolid, runs: [] };

for (const lib of Object.keys(report)) {
const current = report[lib];
Expand Down
Binary file added bench/reactively.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 5 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
"index.d.ts"
],
"scripts": {
"bundle": "rimraf dist && node .scripts/bundle.js",
"build": "rimraf dist && node .scripts/build.js",
"types": "tsc -p tsconfig-build.json && api-extractor run -c types.json && rimraf dist/*.d.ts",
"test": "vitest --run",
"format": "prettier src --write --loglevel warn",
"test:watch": "vitest --watch",
"validate": "pnpm test && pnpm bundle && pnpm types",
"test": "node --expose-gc ./vitest.js",
"test:watch": "node --expose-gc ./vitest.js --watch",
"validate": "pnpm test && pnpm build && pnpm types",
"release": "pnpm validate && standard-version && git push --follow-tags origin main && npm publish",
"size": "pnpm bundle && export-size ."
"size": "pnpm build && export-size ."
},
"contributors": [
"Rahim Alwer <rahim.alwer@gmail.com>"
Expand All @@ -38,9 +38,6 @@
"bugs": {
"url": "https://github.com/maverick-js/observables/issues"
},
"dependencies": {
"@maverick-js/scheduler": "^2.1.0"
},
"devDependencies": {
"@microsoft/api-extractor": "^7.25.3",
"@preact/signals-core": "^1.2.0",
Expand Down
8 changes: 0 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6576447

Please sign in to comment.