Skip to content

Commit

Permalink
Merge b0ab864 into 1806812
Browse files Browse the repository at this point in the history
  • Loading branch information
spautz committed Sep 12, 2020
2 parents 1806812 + b0ab864 commit 8c59077
Show file tree
Hide file tree
Showing 14 changed files with 379 additions and 144 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -25,4 +25,6 @@ The original `state` is not modified.
- If you're using [lodash](https://lodash.com) then you don't need this: [see this thread for alternatives](https://github.com/lodash/lodash/issues/1696#issuecomment-328335502).

- The small bundle size of this library comes with a slight speed tradeoff. This library is a little slower than
immutable-assign and immutable.js, but a little faster than Immer and seamless-immutable.
immutable-assign and immutable.js, but a little faster than Immer and seamless-immutable. It's faster if you
use a string path instead of an array of path parts.
[See full benchmark here.](./benchmark)
36 changes: 26 additions & 10 deletions benchmark/README.md
Expand Up @@ -3,7 +3,7 @@
This project exercises several different utilities for deep-setting immutable data.

Note that these numbers ONLY reflect `set` operations. Many of these libraries also offer `get` functionality, so
bundle sizes are not directly comparable. These are for reference only.
the final bundle size will depend on your use cases. These are for reference only.

## Usage

Expand All @@ -16,13 +16,29 @@ yarn run benchmark;

## Results

Last run on (date goes here)
Lower times are better .Last run on September 12, 2020.

| Library | Version | Bundle size | Time (lower is better) |
| :------------------------------------------------------------------------------ | ------------: | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------: |
| [Immer](https://immerjs.github.io/immer/) | `7.0.8` | [![gzip size](https://img.shields.io/bundlephobia/minzip/immer)](https://bundlephobia.com/result?p=immer) | (pending) |
| [Immutable.js](https://immutable-js.github.io/immutable-js/) | `4.0.0-rc.12` | [![gzip size](https://img.shields.io/bundlephobia/minzip/immutable)](https://bundlephobia.com/result?p=immutable) | (pending) |
| [immutable-assign](https://github.com/engineforce/ImmutableAssign) (iassign.js) | `2.1.4` | [![gzip size](https://img.shields.io/bundlephobia/minzip/immutable-assign)](https://bundlephobia.com/result?p=immutable-assign) | (pending) |
| [lodash](https://lodash.com/) (setWith + clone) | `4.17.20` | [![gzip size](https://img.shields.io/bundlephobia/minzip/lodash)](https://bundlephobia.com/result?p=lodash) | (pending) |
| [seamless-immutable](https://github.com/rtfeldman/seamless-immutable) | `7.1.4` | [![gzip size](https://img.shields.io/bundlephobia/minzip/seamless-immutable)](https://bundlephobia.com/result?p=seamless-immutable) | (pending) |
| [tiny-immutable-set](https://github.com/spautz/tiny-immutable-set) | `0.1.0` | [![gzip size](https://img.shields.io/bundlephobia/minzip/tiny-immutable-set)](https://bundlephobia.com/result?p=tiny-immutable-set) | (pending) |
| Library | Version | Bundle size | **Total time** | ["prop1", "prop2", "prop3"] | "prop1.prop2.prop3" | "prop1[prop2][prop3]" |
| :------------------------------------------------------------------------------ | ------------: | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------: | --------------------------: | ------------------: | --------------------: |
| [Immer](https://immerjs.github.io/immer/) | `7.0.8` | [![gzip size](https://img.shields.io/bundlephobia/minzip/immer@7.0.8)](https://bundlephobia.com/result?p=immer@7.0.8) | **27.447886964** | 8.907073234 | 9.249547088 | 9.291266642 |
| [Immutable.js](https://immutable-js.github.io/immutable-js/) | `4.0.0-rc.12` | [![gzip size](https://img.shields.io/bundlephobia/minzip/immutable@4.0.0-rc.12)](https://bundlephobia.com/result?p=immutable@4.0.0-rc.12) | (pending) | (pending) | (pending) | (pending) |
| [immutable-assign](https://github.com/engineforce/ImmutableAssign) (iassign.js) | `2.1.4` | [![gzip size](https://img.shields.io/bundlephobia/minzip/immutable-assign@2.1.4)](https://bundlephobia.com/result?p=immutable-assign@2.1.4) | (pending) | (pending) | (pending) | (pending) |
| [lodash](https://lodash.com/) (setWith + clone) | `4.17.20` | [![gzip size](https://img.shields.io/bundlephobia/minzip/lodash@4.17.20)](https://bundlephobia.com/result?p=lodash@4.17.20) | **4.662552397** | 1.23424402 | 1.784842394 | 1.854285601 |
| [seamless-immutable](https://github.com/rtfeldman/seamless-immutable) | `7.1.4` | [![gzip size](https://img.shields.io/bundlephobia/minzip/seamless-immutable@7.1.4)](https://bundlephobia.com/result?p=seamless-immutable@7.1.4) | (pending) | (pending) | (pending) | (pending) |
| [tiny-immutable-set](https://github.com/spautz/tiny-immutable-set) | `0.1.0` | [![gzip size](https://img.shields.io/bundlephobia/minzip/tiny-immutable-set@0.1.0)](https://bundlephobia.com/result?p=tiny-immutable-set@0.1.0) | **4.702447091** | 2.191983945 | 1.447061770 | 1.63401376 |

## Organization

Each library has a file under `libraries/` which provides a callback for each scenario:

- `setWithString(object, path, value)`, where path looks like `"prop1.prop2.prop3"`
- `setWithArray(object, path, value)`, where path looks like `["prop1", "prop2", "prop3"]`
- `setWithArrayString(object, path, value)`, where path looks like `"prop1[prop2][prop3]"`

Each scenario type has a file under `scenarios/` which provides setup and teardown callbacks:

- `setup(numCasesToGenerate)` returns an array of `(object, path, value)` arguments to use for each test case
- `teardown(cases)` is available if you need it

`run-benchmark.js` runs everything based on the libraries and scenarios exported from each directory's `index.js`,
with some additional settings in `options.js`.
24 changes: 21 additions & 3 deletions benchmark/libraries/immer.js
@@ -1,9 +1,27 @@
const immer = require('immer');
const { produce } = require('immer');

const setWithArray = (obj, path, value) => {
return produce(obj, (draftObj) => {
let index = 0;
const pathToTraverse = path.length - 1;
for (; index < pathToTraverse; index++) {
draftObj = draftObj[path[index]] || {};
}
draftObj[path[index]] = value;
});
};

const immerCase = {
label: 'immer',
setWithString: null,
setWithArray: null,
setWithString: (obj, path, value) => {
const pathParts = path.split('.');
return setWithArray(obj, pathParts, value);
},
setWithArray,
setWithArrayString: (obj, path, value) => {
const pathParts = path.split(/[\.\[\]]/).filter((token) => !!token);
return setWithArray(obj, pathParts, value);
},
};

module.exports = immerCase;
8 changes: 8 additions & 0 deletions benchmark/libraries/index.js
@@ -0,0 +1,8 @@
module.exports = [
require('./immer'),
require('./immutable'),
require('./immutable-assign'),
require('./lodash'),
require('./seamless-immutable'),
require('./tiny-immutable-set'),
];
7 changes: 4 additions & 3 deletions benchmark/libraries/lodash.js
@@ -1,12 +1,13 @@
const setWith = require('lodash/setWith');
const clone = require('lodash/clone');

const lodashImmutableSet = (obj, path, value) => setWith(clone(obj), path, value, clone);
const set = (obj, path, value) => setWith(clone(obj), path, value, clone);

const lodashCase = {
label: 'lodash',
setWithString: lodashImmutableSet,
setWithArray: lodashImmutableSet,
setWithString: set,
setWithArray: set,
setWithArrayString: set,
};

module.exports = lodashCase;
1 change: 1 addition & 0 deletions benchmark/libraries/tiny-immutable-set.js
Expand Up @@ -4,6 +4,7 @@ const tinyImmutableSetCase = {
label: 'tiny-immutable-set',
setWithString: set,
setWithArray: set,
setWithArrayString: set,
};

module.exports = tinyImmutableSetCase;
16 changes: 16 additions & 0 deletions benchmark/options.js
@@ -0,0 +1,16 @@
const options = {
/* How many times each scenario is run for each library */
numIterations: 100000,
/* Whether to console.log while libraries and scenarios are running */
showLogs: true,
/* Whether to console.warn if a library hasn't implemented a scenario */
showWarnings: true,

// To check against order-of-execution issues, we can run libraries-for-each-scenario, scenarios-for-each-library,
// or both. This is mostly to validate the benchmark runner itself. (Running both will double the number of
// iterations, and it won't give you any more information since execution order doesn't matter.)
loopLibrariesThenScenarios: true,
loopScenariosThenLibraries: true,
};

module.exports = options;

0 comments on commit 8c59077

Please sign in to comment.