Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

applySnapshot is slow #2128

Open
lavrton opened this issue Dec 6, 2023 · 1 comment
Open

applySnapshot is slow #2128

lavrton opened this issue Dec 6, 2023 · 1 comment
Labels
help/PR welcome Help/Pull request from contributors to fix the issue is welcome level: intermediate

Comments

@lavrton
Copy link

lavrton commented Dec 6, 2023

Sandbox link or minimal reproduction code

Code:

import { types, getSnapshot, applySnapshot, applyPatch } from 'mobx-state-tree';
import * as jsonpatch from 'fast-json-patch';

// create simple model
const Shape = types.model('Shape', {
  x: 0,
  y: 0,
  width: 100,
  height: 100,
  rotation: 90,
  fill: 'red',
  stroke: 'black',
  name: ''
}).actions((self) => ({
  set(attrs) {
    Object.assign(self, attrs)
  }
}));

const Group = types.model('Group', {
  shapes: types.array(Shape)
});

// generate large number of data
const group = Group.create({
  shapes: new Array(10000).fill({})
});

// create initial snapshot
const firstSnapshot = getSnapshot(group);
// change shapes
group.shapes[500].set({ x: 20, y: 30 });

// revert change via mobx-state-tree
console.time('revert');
applySnapshot(group, firstSnapshot);
console.timeEnd('revert');


// apply change again
group.shapes[500].set({ x: 20, y: 30 });

// revert changes via manual patch calculations
console.time('revert2');
const secondShapshot = getSnapshot(group);
const patch = jsonpatch.compare(secondShapshot, firstSnapshot);
applyPatch(group, patch);
console.timeEnd('revert2');

Demo: https://codesandbox.io/p/sandbox/mst-perf-test-v93yqt?file=%2Fsrc%2Findex.ts%3A14%2C15

Describe the observed behavior

I have a large store with plenty of models. Every model has many attributes. Occasionally, I save a full snapshot of store into a memory to reload it later. I noticed that applying old snapshot works very slow, even if not much data is changed.

Describe the expected behavior

I would expect that applying a previous snapshot, that has almost the same data, should load very fast. In the demo I am trying to restore old data using two ways:

  1. Applying applySnapshot from mobx-state-tree
  2. Manually calculating patch with an external library and applying it.

On my machine I have 782ms for (1) and 14ms for (2). That is 55x faster.
I was thinking to use my second approach in the code, but looks like the library's patches don't exactly match patches of mst

I don't know mobx-state-tree internals, but I guess it can do something like this internally:

function applySnapshot(target, snapshot) {
   const currentSnap = getSnapshot(target);
   if (currentSnap === snaphot) {
     // do nothing, all good
     return;
   }
   return getStateTreeNode(target).applySnapshot(snapshot)
}

UPD: as I understand mst is doing that, only once on the top level, but not in children.

@coolsoftwaretyler
Copy link
Collaborator

Hey @lavrton - thanks for filing this issue!

We are currently investigating performance issues and trying to make improvements. You can see some of the conversation and ongoing work here: #2095

I will add your sample code to my own personal benchmarking suite and dig in.

I can't give you a good estimate of when we might improve this, but your report here is going to really help us figure out where we can improve, and because you did a great job writing this up and including some ideas, I expect you'll see improvements in the coming months.

I wish I had a better answer for you, but thanks again for the details, and if you'd like to pitch in, I'm happy to help you get started with the internals. No worries if not, this has been great already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help/PR welcome Help/Pull request from contributors to fix the issue is welcome level: intermediate
Projects
None yet
Development

No branches or pull requests

2 participants