mobxjs / mobx-state-tree

Full-featured reactive state management without the boilerplate
https://mobx-state-tree.js.org/
MIT License
6.94k stars 641 forks source link

Orders of magnitude slower than mobx #440

Closed knoopx closed 6 years ago

knoopx commented 7 years ago

Hi I'm migrating an app from mobx to mobx-state-tree and I'm facing some performance issues.

Here is the simplest example case:

https://mattiamanzati.github.io/mobx-state-tree-playground/#src=import%20%7B%20observable%20%7D%20from%20'mobx'%0Aimport%20%7B%20types%20%7D%20from%20%22mobx-state-tree%22%0A%0Aconst%20MSTStore%20%3D%20types.model(%7B%0A%20%20%20%20items%3A%20types.optional(types.array(types.string)%2C%20%5B%5D)%0A%7D).actions(self%20%3D%3E%20(%7B%0A%20%20%20%20setItems(items)%7B%0A%20%20%20%20%20%20%20%20self.items%20%3D%20items%0A%20%20%20%20%7D%0A%7D))%0A%0Aclass%20MobxStore%20%7B%0A%20%20%20%20constructor()%7B%0A%20%20%20%20%20%20%20%20this.items%20%3D%20observable(%5B%5D)%20%0A%20%20%20%20%7D%0A%20%20%20%20setItems(items)%7B%0A%20%20%20%20%20%20%20%20this.items%20%3D%20items%0A%20%20%20%20%7D%0A%7D%20%0A%0Aconst%20items%20%3D%20Array.from(Array(100000).keys()).map(x%20%3D%3E%20%60Item%20%24%7Bx%7D%60)%0A%0Aconst%20mstStore%20%3D%20MSTStore.create()%0A%0Aconsole.time(%22mst%22)%0AmstStore.setItems(items)%0Aconsole.timeEnd(%22mst%22)%0A%0Aconst%20mobxStore%20%3D%20new%20MobxStore()%0A%0Aconsole.time(%22mobx%22)%0AmobxStore.setItems(items)%0Aconsole.timeEnd(%22mobx%22)

Replacing an observable array with 100.000 items takes ~5sec when using MST and just 0.02sec when using mobx. The worst part is that it completely blocks the UI while doing so. Is there something I could do to improve speed?

EDIT: Running with NODE_ENV=production does not improve things too much.

development
mst: 744.248291015625ms
mobx: 0.052001953125ms
production
mst: 645.447021484375ms
mobx: 0.064208984375ms
mweststrate commented 6 years ago

@wbercx your project sounds like a nice real life benchmark given the size & complexity, could you create a sample repo or PR with benchmark test so that we can further investigate?

wbercx commented 6 years ago

@mweststrate Sorry Michel, completely missed this. I probably can, but I don't think I can get around to it for another month or so.

I'm not actually convinced that MST is to blame for the bulk of the loading time. I suspect a lot of it is related to Android.

It's quite hard to profile at the moment as some of the tools just don't work anymore with recent React Native versions. And as soon as you try to do any sort of remote debugging or profiling, everything's lightning fast as all of the JS then runs on the local machine rather than the device.

mweststrate commented 6 years ago

See also: https://github.com/mobxjs/mobx-state-tree/issues/664#issuecomment-366176151 for some tests

aripekkako commented 6 years ago

We're running into performance problems too. We have an ecommerce app, which has quite a big product object. Creating 1000 of the simplest kind product objects in tests takes around 5 secs on my Macbook Pro (Mid 2015). The end result is render blocking in the UI, when loading product lists (60 objects).

Our product models generate 25+ closures (views+actions) / each. What would be the best way to optimize object creation? I've gone through all the issues + docs and the only thing I could find was @KBorders01 suggestion on using a single action function (https://github.com/mobxjs/mobx-state-tree/issues/440#issuecomment-370265554) . Do you think it would be in any way feasible to add support for prototypal inheritance of some sort @mweststrate?

dualscyther commented 6 years ago

I agree, storing actions on the prototype seems like the way to go - maybe this can be configurable/a new feature so that the API doesn't break?

Additionally, lazily binding the action to each object seems like a good way to proceed in the meantime.

mweststrate commented 6 years ago

Thanks to #810 / MST 3 will be significant faster, closing this issue for now. Best open a new one if follow up is needed after releasing MST 3

ejabu commented 6 years ago

@mweststrate how are you getting a 6x bump? I'm seeing 15-25% improvement

[Dev] Initializing MST spreadsheet...: 743.10107421875ms [Prod] Initializing MST spreadsheet...: 645.926025390625ms

I randomly tried the sandbox today

this is the result

Creating sample data...: 0.5999999993946403ms 
10000
Initializing MST spreadsheet...: 0.19999999494757503ms 
Initializing mobx spreadsheet...: 0.29999999969732016ms

What I confused of are the dependencies.

it is stated

mobx 3.5.1
mobx-state-tree 1.2.1

I thought the performance was improved by the update of MST. But it seems that version 1 already performed

markhu commented 5 years ago

I'm not sure how to find out what version the playground is running, but it still seems to impose a 1000x+ speed penalty, #810 notwithstanding.

yang commented 5 years ago

@ejabu You may be looking at the CodeSandbox console, which also reports small numbers for me. Look at the Chrome Devtools console instead.

For posterity: I just forked @mweststrate's sandbox and upgraded to MST 3.10.2 and mobx 5.9.0. Here are updated times—MST is apparently considerably faster than MobX for this construction benchmark. Not sure if these numbers are suspicious (or even how interesting a construction benchmark is).

Initializing MST spreadsheet...: 65.73388671875ms
Initializing mobx spreadsheet...: 130.56787109375ms

Link to sandbox: https://codesandbox.io/s/6w01k206yw

jrmyio commented 5 years ago

Although object creation became very fast. Reading is still very slow (because of the lazy initialization being triggered at that point).

I edited the sandbox: https://codesandbox.io/s/l9n5mo4rjl

As mentioned before, use the Chrome devtools rather than the codesandbox console to test. Here are my results:

Initializing MST spreadsheet...: 36.467041015625ms
Reading MST spreadsheet...: 429.496826171875ms
Initializing mobx spreadsheet...: 71.212890625ms
Reading mobx spreadsheet...: 10.56005859375ms

For some use cases fast initializing is all you need, for other use cases, for example when you want to aggregate some values of your models, you will always need to add the read speed on top of the creation speed.

Combing both times we are still looking at a 5+ multiplier, not to mention this is a very simple MST model. If your model is going to have embedded/child models it will most likely become even more of a difference.

If I recall @mweststrate MST is slower because it doesn't utilize the JS prototypes as it creates copies of each function? Is there any way to change or improve this?

jayarjo commented 5 years ago

@ConneXNL Making copies of each model for every method is at the core of MST architecture, changing that will require substantial refactoring.

dualscyther commented 5 years ago

@jayarjo being not too familiar with this codebase, is there actually a reason for creating a copy per model or was it just something that happened at the time?

aripekkako commented 5 years ago

@dualscyther This is by design and is discussed in this comment https://github.com/mobxjs/mobx-state-tree/issues/440#issuecomment-367623672

jrmyio commented 5 years ago

According to the FAQ: MST provides access to snapshots, patches and interceptable actions. Also, it fixes the this problem.

Here I wonder how much is added by the "fixes the this problem", and what exactly is the this problem? Wouldn't using = () => {} instead of () {} just fix this? Or is there more?