Skip to content

Commit

Permalink
Update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
mweststrate committed Jul 25, 2018
1 parent 2513fb8 commit 341cfeb
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 11 deletions.
2 changes: 1 addition & 1 deletion __performance_tests__/add-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {fromJS} from "immutable"
import Seamless from "seamless-immutable"
import deepFreeze from "deep-freeze"

console.log("# add-data - loading large set of data")
console.log("\n# add-data - loading large set of data\n")

const dataSet = require("./data.json")
const baseState = {
Expand Down
2 changes: 2 additions & 0 deletions __performance_tests__/incremental.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import produce, {setAutoFreeze, setUseProxies} from "../dist/immer.umd.js"
import cloneDeep from "lodash.clonedeep"
import * as Immutable from "immutable"

console.log("\n# incremental - lot of small incremental changes\n")

function createTestObject() {
return {
a: 1,
Expand Down
40 changes: 39 additions & 1 deletion __performance_tests__/todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ immutableJsBaseState = List(baseState.map(todo => todoRecord(todo)))
// generate seamless-immutable base state
seamlessBaseState = Seamless.from(baseState)

console.log("# todo - performance")
console.log("\n# todo - performance\n")

measure(
"just mutate",
Expand Down Expand Up @@ -212,6 +212,25 @@ measure(
}
)

measure(
"immer (proxy) - with autofreeze - with patch listener",
() => {
setUseProxies(true)
setAutoFreeze(true)
},
() => {
produce(
baseState,
draft => {
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
draft[i].done = true
}
},
function() {}
)
}
)

measure(
"immer (es5) - without autofreeze",
() => {
Expand Down Expand Up @@ -260,3 +279,22 @@ measure(
)
}
)

measure(
"immer (es5) - with autofreeze - with patch listener",
() => {
setUseProxies(false)
setAutoFreeze(true)
},
() => {
produce(
baseState,
draft => {
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
draft[i].done = true
}
},
function() {}
)
}
)
Binary file modified images/performance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 12 additions & 9 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ const byId = produce(

During the run of a producer, Immer can record all the patches that would replay the changes made by the reducer.
This is a very powerful tool if you want to fork your state temporarily, and replay the changes to the original.

Patches are useful in few scenarios:
* To exchange incremental updates with other parties, for example over websockets
* For debugging / traces, to see precisely how state is changed over time
* As basis for undo/redo or as approach to replay changes on a slightly different state tree

To help with replaying patches, `applyPatches` comes in handy. Here is an example how patches could be used
to record the incremental updates and (inverse) apply them:

Expand Down Expand Up @@ -488,21 +494,18 @@ const updatedTodosArray = produce(todosArray, draft => {

## Performance

Here is a [simple benchmark](__performance_tests__/todo.js) on the performance of Immer. This test takes 100.000 todo items, and updates 10.000 of them. _Freeze_ indicates that the state tree has been frozen after producing it. This is a _development_ best practice, as it prevents developers from accidentally modifying the state tree.
Here is a [simple benchmark](__performance_tests__/todo.js) on the performance of Immer. This test takes 50,000 todo items, and updates 5,000 of them. _Freeze_ indicates that the state tree has been frozen after producing it. This is a _development_ best practice, as it prevents developers from accidentally modifying the state tree.

These tests were executed on Node 8.4.0. Use `yarn test:perf` to reproduce them locally.
These tests were executed on Node 9.3.0. Use `yarn test:perf` to reproduce them locally.

![performance.png](images/performance.png)

Some observations:
Most important observation:

* From `immer` perspective, this benchmark is a _worst case_ scenario, because the root collection it has to proxy is really large relatively to the rest of the data set.
* The _mutate_, and _deepclone, mutate_ benchmarks establish a baseline on how expensive changing the data is, without immutability (or structural sharing in the deep clone case).
* The _reducer_ and _naive reducer_ are implemented in typical Redux style reducers. The "smart" implementation slices the collection first, and then maps and freezes only the relevant todos. The "naive" implementation just maps over and processes the entire collection.
* Immer with proxies is roughly speaking twice as slow as a hand written reducer. This is in practice negligible.
* Immer with proxies is roughly speaking twice to three times slower as a hand written reducer (the above test case is worst case, se `yarn test:perf` for more tests). This is in practice negligible.
* Immer is roughly as fast as ImmutableJS. However, the _immutableJS + toJS_ makes clear the cost that often needs to be paid later; converting the immutableJS objects back to plain objects, to be able to pass them to components, over the network etc... (And there is also the upfront cost of converting data received from e.g. the server to immutable JS)
* The ES5 implementation of Immer is significantly slower. For most reducers this won't matter, but reducers that process large amounts of data might benefit from not (or only partially) using an Immer producer. Luckily, Immer is fully opt-in.
* The peaks in the _frozen_ versions of _just mutate_, _deepclone_ and _naive reducer_ come from the fact that they recursively freeze the full state tree, while the other test cases only freeze the modified parts of the tree.
* Generating patches doesn't significantly slow immer down
* The ES5 fallback implementation is roughly twice as slow as the proxy implementation, in some cases worse.

## FAQ

Expand Down

1 comment on commit 341cfeb

@robberfree
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

500k should be 50k in the image of performance.

Please sign in to comment.