mobx-state-tree: Orders of magnitude slower than mobx
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 { observable } from ‘mobx’ import { types } from “mobx-state-tree” const MSTStore %3D types.model({ items%3A types.optional(types.array(types.string)%2C [])%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
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 11
- Comments: 64 (35 by maintainers)
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
@mweststrate when using just frozen you will loose MST built-in functionality like references, views, and deserialization (in relation to references).
Also I am not sure if I understand what you mean with data points. Is a spreadsheet with cells something one would do with non-frozen types in MST?
Let’s consider a simple 100x100 spreadsheet in MST and Mobx and compare the performance with arguably the exact same functionality:
Prepare some test data:
MST:
Mobx:
The above MST takes a whopping 2 seconds to initialize. Things can only get worse from there as you add more cells / make the Cell model more complex. While having almost the same functionality in place we have to take a 80-90% performance loss when moving from mobx to MST.
Performance PR’s are welcome!
Note there are quite some
// optimizationcomments in the code which indicate clear possibilities for optimizations. Without really testing I think the following things might help big time:Nodeis quite a complicated and generic object, but for immutable values (primitives) we should probably create a cheaper and much version of it (ImmutableNode) that doesn’t support attaching onPatch / onSnapshot handlers etc etc. This one might not be trivial btw@computed), likeroot, environment etc.nullinstead of[], that makes object creation cheaper but requires a bit more pre-condition checking in the code. Shouldn’t be too complicated eitherHere’s my latest implementation, now running in production. Everything seems to be working so far.
@mweststrate @mattiamanzati
I’ve been toying around with lazy init-ing MST nodes as my app started to take over 10s to initialize my MST store of nested hell and I came up with a hacky solution to my problem – don’t instantiate the node until something actually cares about its value.
This is still all synchronous and brought me back to the 100s of ms load time. It’s making me wonder whether this would be a better default for MST.
An array with 50000 items with a single string property:
Code Sandbox
Would love to hear from you as to how to achieve this properly! Thanks!!
@mweststrate is there any plan to support storing actions on the prototype for MST in the future, or would this not be possible due to the way that MST works? Alternatively, would you recommend just using a single action function that takes a sub-action name as an argument and call other statically-defined functions to reduce the number of closures per object instance?
I randomly tried the sandbox today
this is the result
What I confused of are the dependencies.
it is stated
I thought the performance was improved by the update of MST. But it seems that version 1 already performed
@skellock Even in production mode. Even when I don’t render a FlatList 😉
@mweststrate while wrapping all actions is helpful, some might easily do without it (if it affects performance dramatically). Maybe make this behaviour optional/configurable instead?
I am little shocked to see the difference in performance between mobx and mobx-state-tree. I just transformed the models of a mobx application to mobx-state-tree only to find out I can’t actually use it as it has to load thousands of objects in memory and I’m looking at > 10 sec boot time.
Mobx and mobx-state-tree can’t be that much of difference until you actually use mobx-state-tree’s functionality (when it should kick-in).
Would it make sense to opt-out some features of mobx-state-tree to make it more like basic mobx usage? For example, if a model is not likely to be changed I don’t need patches etc. .
One could argue that you wouldn’t need mobx-state-tree for these models but I would like to define my models in a simular way + I really like the way mobx-state-tree handles deserialization of json input.
@dnakov made a good example of lazy loading. However, if one were to loop trough the models for the initial view (for example to do some filtering), then the lazyloading wouldn’t make much of a difference.
If the performance penalty during initialization is because of support for the mutation functionality of the objects then would there be a way to enhance the objects as soon as a first mutation is being performed?
@mshibl it creates a new type that is a lazy version of the given type.
Another possible improvement would be having a computed value based on parent signaling if any of the parent have any listener for onPatch/onSnapshot or onAction, and don’t bubble up that event at all if any of that is not present! 😃
Yeah, that was already a TODO in the code, that would be a starting point for performance improvements for sure, node.ts and mst-operations.ts contains a lot of those perf todos in comments 😃
https://github.com/mobxjs/mobx-state-tree/blob/master/src/core/node.ts#L269
Yeah, I’d opt to not freeze at all in prod mode 😃
^ I’m with my fellow React Native brethern, Sanket here. I’d love to help out in anyway.
One very corner-case thing I’ve noticed when using
frozenis the act of freezing objects seems to incur a lot of overhead. Especially on Android devices.I know of other libs that opt out on prod builds.
I’d love to see some kind of
types.yolo()that would befrozenminus thefreeze. Or preferably, some kind of switch to turn offfreeze/isFrozenjust in prod?