Skip to content

Commit

Permalink
Merge pull request #38 from mweststrate/feature/performance
Browse files Browse the repository at this point in the history
improve performance
  • Loading branch information
mweststrate authored Jan 8, 2018
2 parents cc61e1e + e49d0a2 commit 670fcff
Show file tree
Hide file tree
Showing 8 changed files with 403 additions and 306 deletions.
110 changes: 88 additions & 22 deletions __performance_tests__/todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import immerProxy, {setAutoFreeze as setAutoFreezeProxy} from ".."
import immerEs5, {setAutoFreeze as setAutoFreezeEs5} from "../es5"
import cloneDeep from "lodash.clonedeep"
import {List, Record} from "immutable"
import deepFreeze from "deep-freeze"

function freeze(x) {
Object.freeze(x)
return x
}

describe("performance", () => {
const MAX = 100000
Expand All @@ -21,14 +27,7 @@ describe("performance", () => {
}

// Produce the frozen bazeState
frozenBazeState = baseState.map(todo => {
const newTodo = {...todo}
newTodo.someThingCompletelyIrrelevant = todo.someThingCompletelyIrrelevant.slice()
Object.freeze(newTodo.someThingCompletelyIrrelevant)
Object.freeze(newTodo)
return newTodo
})
Object.freeze(frozenBazeState)
frozenBazeState = deepFreeze(cloneDeep(baseState))

// generate immutalbeJS base state
const todoRecord = Record({
Expand All @@ -43,20 +42,41 @@ describe("performance", () => {
test(name, fn)
}

measure("just mutate", () => {
{
const draft = cloneDeep(baseState)
measure("just mutate", () => {
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
draft[i].done = true
}
})
}

{
const draft = cloneDeep(baseState)
measure("just mutate, freeze", () => {
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
draft[i].done = true
}
deepFreeze(draft)
})
}

measure("deepclone, then mutate", () => {
const draft = cloneDeep(baseState)
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
baseState[i].done = true
draft[i].done = true
}
})

measure("deepclone, then mutate", () => {
measure("deepclone, then mutate, then freeze", () => {
const draft = cloneDeep(baseState)
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
draft[i].done = true
}
deepFreeze(draft)
})

measure("handcrafted reducer", () => {
measure("handcrafted reducer (no freeze)", () => {
const nextState = [
...baseState.slice(0, MAX * MODIFY_FACTOR).map(todo => ({
...todo,
Expand All @@ -66,6 +86,42 @@ describe("performance", () => {
]
})

measure("handcrafted reducer (with freeze)", () => {
const nextState = freeze([
...baseState.slice(0, MAX * MODIFY_FACTOR).map(todo =>
freeze({
...todo,
done: true
})
),
...baseState.slice(MAX * MODIFY_FACTOR)
])
})

measure("naive handcrafted reducer (without freeze)", () => {
const nextState = baseState.map((todo, index) => {
if (index < MAX * MODIFY_FACTOR)
return {
...todo,
done: true
}
else return todo
})
})

measure("naive handcrafted reducer (with freeze)", () => {
const nextState = deepFreeze(
baseState.map((todo, index) => {
if (index < MAX * MODIFY_FACTOR)
return {
...todo,
done: true
}
else return todo
})
)
})

measure("immutableJS", () => {
let state = immutableJsBaseState
state.withMutations(state => {
Expand All @@ -75,13 +131,14 @@ describe("performance", () => {
})
})

measure("immer (proxy) - with autofreeze", () => {
setAutoFreezeProxy(true)
immerProxy(frozenBazeState, draft => {
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
draft[i].done = true
}
})
measure("immutableJS + toJS", () => {
let state = immutableJsBaseState
.withMutations(state => {
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
state.setIn([i, "done"], true)
}
})
.toJS()
})

measure("immer (proxy) - without autofreeze", () => {
Expand All @@ -94,9 +151,9 @@ describe("performance", () => {
setAutoFreezeProxy(true)
})

measure("immer (es5) - with autofreeze", () => {
setAutoFreezeEs5(true)
immerEs5(frozenBazeState, draft => {
measure("immer (proxy) - with autofreeze", () => {
setAutoFreezeProxy(true)
immerProxy(frozenBazeState, draft => {
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
draft[i].done = true
}
Expand All @@ -112,4 +169,13 @@ describe("performance", () => {
})
setAutoFreezeEs5(true)
})

measure("immer (es5) - with autofreeze", () => {
setAutoFreezeEs5(true)
immerEs5(frozenBazeState, draft => {
for (let i = 0; i < MAX * MODIFY_FACTOR; i++) {
draft[i].done = true
}
})
})
})
49 changes: 40 additions & 9 deletions __tests__/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,31 @@ function runBaseTest(name, lib, freeze) {
expect(nextState.nested).toBe(baseState.nested)
})

it("deep change bubbles up", () => {
const nextState = immer(baseState, s => {
s.anObject.nested.yummie = false
if (
("should preserve type",
() => {
const nextState = immer(baseState, s => {
expect(Array.isArray(s)).toBe(true)
expect(s.protoType).toBe(Object)
s.anArray.push(3)
s.aProp = "hello world"
expect(Array.isArray(s)).toBe(true)
expect(s.protoType).toBe(Object)
})
expect(Array.isArray(nextState)).toBe(true)
expect(nextState.protoType).toBe(Object)
})
)
it("deep change bubbles up", () => {
const nextState = immer(baseState, s => {
s.anObject.nested.yummie = false
})
expect(nextState).not.toBe(baseState)
expect(nextState.anObject).not.toBe(baseState.anObject)
expect(baseState.anObject.nested.yummie).toBe(true)
expect(nextState.anObject.nested.yummie).toBe(false)
expect(nextState.anArray).toBe(baseState.anArray)
})
expect(nextState).not.toBe(baseState)
expect(nextState.anObject).not.toBe(baseState.anObject)
expect(baseState.anObject.nested.yummie).toBe(true)
expect(nextState.anObject.nested.yummie).toBe(false)
expect(nextState.anArray).toBe(baseState.anArray)
})

it("can add props", () => {
const nextState = immer(baseState, s => {
Expand Down Expand Up @@ -133,6 +148,16 @@ function runBaseTest(name, lib, freeze) {
])
})

it("can delete array items", () => {
const nextState = immer(baseState, s => {
s.anArray.length = 3
})
expect(nextState).not.toBe(baseState)
expect(nextState.anObject).toBe(baseState.anObject)
expect(nextState.anArray).not.toBe(baseState.anArray)
expect(nextState.anArray).toEqual([3, 2, {c: 3}])
})

it("should support sorting arrays", () => {
const nextState = immer(baseState, s => {
s.anArray[2].c = 4
Expand Down Expand Up @@ -223,6 +248,7 @@ function runBaseTest(name, lib, freeze) {
delete s.anObject
obj.coffee = true
s.messy = obj
debugger
})
expect(nextState).not.toBe(baseState)
expect(nextState.anArray).toBe(baseState.anArray)
Expand Down Expand Up @@ -330,3 +356,8 @@ function enumerableOnly(x) {
// this can be done better...
return JSON.parse(JSON.stringify(x))
}

// TODO: test problem scenarios
// nesting immmer
// non-trees
// complex objects / functions
Loading

0 comments on commit 670fcff

Please sign in to comment.