I read from different sources that mobx outperforms react renderer and are faster then redux. However if I made couple of tests it shows that adding new data to mobx observables are pretty slow. In react-native environment every milliseconds counts and it's tricky to use solution where even looping over 200 elements and filling array takes more then 100ms As I really enjoy mobx I hope someone can take a look a test code and give me some hints - what I'm doing wrong and how to improve performance.
import {observable, transaction,autorun} from 'mobx';
class Runner {
list = observable([]);
run() {
const start = new Date().getTime();
transaction(() => {
for (let i = 0; i < 200; i++) {
this.list.push({
id: observable(i),
one: observable('1'),
two: '2',
three: 3,
x: 'xxxxx',
y: 'yyyyy',
z: 'zzzzz',
z1: 'zzzzz',
z2: 'zzzzz',
z3: 'zzzzz',
z4: 'zzzzz',
});
}
});
console.log('Execution time: ' + (new Date().getTime() - start) + 'ms services ');
}
}
const runner = new Runner();
autorun(() => console.log(runner.list));
runner.run();
On my laptop it's takes about 120ms to complete. Without observable-s it's takes less then 1ms
You are doing nothing fundamentally wrong (except, that, as Robert already indicated, currently all properties are made observable anyway, because observable by default recurses on plain data structures).
The main thing is that you aren't really using MobX yet :) Your tests results are correct, observable data structures are a lot more expensive then plain structures. It's a little bit comparing apples with oranges. Or for a better analogy; It is like benchmarking concatening strings to produce HTML versus using the DOM to generate HTML. The strings will always win.
However, in the bigger picture of a complete app, things are different. Suppose that you need to change the border color of an element. Then the DOM will probably suddenly be a lot more efficient, as it allows you to only mutate a very specific piece of your HTML, and the DOM is smart enough to decide precisely which pixels needs to be repainted on the screen. That would all be a lot harder if you had just a bare string.
MobX is similar, doesn't get it's performance from fast data structures, but from their smartness. If an observable array is modified, MobX pinpoints precisely which actions, which components need to be rendered to be consistent with any computation you write. Because MobX is able to establish far more fine grained 'event listeners' then you would do when writing that kind of stuff by hand, and because MobX can optimize dependency trees, what a human programmer probably won't bother doing, MobX can be very fast. But you have to see it in the bigger picture of the complete lifecycle of your state. If you just want to create some objects and arrays very fast, nothing will beat plain arrays and constructor functions.
I recommend to read this blog by @lavrton https://medium.com/@lavrton/how-to-optimise-rendering-of-a-set-of-elements-in-react-ad01f5b161ae#.enlk3n68g. It nicely demonstrates all the hoops you need to jump trough when optimizing manually, just to get close to the speed at which MobX can update components.
I hope that explains your results!
P.S. there are currently a few known cases where MobX can be slow when sorting or cleaning up large collection. Those will be addressed by the upcoming 2.4.0 release though.
Don't push
but replace
and you will see the performance many times faster.
When evaluating the library and tried thousands or records (to push the limits) the load would freeze the browser. I changed it to replace
and it was a matter of milliseconds.
observable()
makes all values that you push in the array (recursively) observable, which may not be what you need.
For instance, from your example code it may follow that you only want to observe changes to the id
and a
properties, not to the rest. In which case, you can use the asFlat
modifier on your observable array:
const { observable, autorun, transaction, asFlat } = mobx;
....
list = observable(asFlat([]));
This will allow you to observe changes to list
itself (new items added, items removed, etc), or to the id
and a
properties of the list items, but not the rest.
This speeds up your test considerably for me: from 35ms to about 5ms.
I tried your code in a fiddle, the result was 35ms. But usually, for()
loops, are more expensive than other alternatives, here is a more faster code using Array.map()
with 1000 examples, in my machine performs at ~59ms(try many times to get average):
const { observable, autorun, transaction } = mobx;
class Runner {
@observable list = [];
run() {
const start = new Date().getTime();
transaction(() => {
this.list = new Array(1000).fill(0).map((row, i) => {
return {
id: observable(i),
a: observable('1'),
two: '2',
three: 3,
x: 'xxxxx',
y: 'yyyyy',
z: 'zzzzz',
z1: 'zzzzz',
z2: 'zzzzz',
z3: 'zzzzz',
z4: 'zzzzz',
};
});
});
console.log('Execution time: ' + (new Date().getTime() - start) + 'ms services ');
console.log('list observable->', this.list);
}
}
const runner = new Runner();
autorun(() => console.log(runner.list));
runner.run();
jsFiddle
However you should compare a React real case using Redux in order to get a fair conclusion about Mobx observables perfomance when it comes to updating components.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With