https://localvoid.github.io/vdom-benchmark/
Cheers,
Ryan
Try the Chrome Dev Editor: https://chrome.google.com/webstore/detail/chrome-dev-editor-develop/pnoffddplpippgcfjdhbmhkofpnaalpg
I couldn't find any obvious issues with the benchmark; I'm also curious because in my own testings, mithril always came up on top of react even in synchronous mode.
Specifically, I looked at React and mithril. If the number of spans is lower, (for instance, 100 instead of 5000) mithril is a bit faster, however, for a big number of items, yes, React is faster. It's quite hard to compare the two very different approaches but here's my interpretation:
- mithril is much lighter and the infrastructure code generally runs faster than React's; So for a small number of node changes, this will make a bigger difference.
- For a moderate number of operations, appendChild (used by mithril) is fine; However, for a big, wholesale change (like reversing a 5000 items array), innerHTML (used by react) is more efficient somehow. This used to a no brainer a few years back but I thought modern browsers were very efficient using both APIs.
- The benchmark is quite kind to React. It uses divs and spans which are quite lightweight in React's world but if we were to use options for instance it might become much slower (See the special virtual implementation here: https://github.com/facebook/react/blob/master/src/browser/ui/dom/components/ReactDOMInput.js). What's more, idiomatic React tends to use a lot of sub-components and these are slower than regular virtual dom nodes (automatic 'this' binding, bookeeping code, etc)
- For a moderate number of operations, appendChild (used by mithril) is fine; However, for a big, wholesale change (like reversing a 5000 items array), innerHTML (used by react) is more efficient somehow. This used to a no brainer a few years back but I thought modern browsers were very efficient using both APIs.
- The benchmark is quite kind to React. It uses divs and spans which are quite lightweight in React's world but if we were to use options for instance it might become much slower (See the special virtual implementation here: https://github.com/facebook/react/blob/master/src/browser/ui/dom/components/ReactDOMInput.js).
What's more, idiomatic React tends to use a lot of sub-components and these are slower than regular virtual dom nodes (automatic 'this' binding, bookeeping code, etc)
I see what you mean about innerHTML; but they add all these data-reactid for tracking purpose.
The React code is quite hard to follow but it appears they always use innerHTML for a newly rendered component (so half the cases in that benchmark!), after that, it seems to be close to what mithril does with setAttribute, insertChild, etc) This hybrid approach helps making their codebase complicated.
Perhaps the use of innerHTML is a direct consequence of them wanting to support React.renderComponentToString.
By the way, does mithril support rendering to a DocumentFragment root that is later attached? This would probably speed things up quite a bit for the initial rendering, where these numerous appendChild are no longer done directly in the live document. It would also be great with hybrid architectures, where the server renders some base page and mithril complement it (the client fetches a page via ajax, create a document fragment out of it, then decorate it with mithril before updating the document).
http://jsperf.com/innerhtml-vs-insertbefore-2
This explains why, in the benchmark, mithril does well for updates (except in a few cases where react has an optimized code path), but not creations from a blank state.
Ah well, the mithril build function is too big to see where the majority of time is spent with Chrome profiler :(
I run each test 10 times instead of 3 for better averages.
http://i171.photobucket.com/albums/u320/boubiyeah/ScreenShot2014-11-05at200140_zps04a243d7.png
@acl FYI, I still want to do a few more tweaks.What you mentioned before about React using innerHTML for creation was intriguing. I could look into that idea (though that will be a bigger effort than just fixing the sub-optimal lines of code.)
--
You received this message because you are subscribed to the Google Groups "mithril" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mithriljs+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Nowadays, VMs can call many functions with barely any penalty (There are tons of function calls in react after all). if mithril's build function were to be split in a few smaller functions, it would help in finding where most of the time is spent.
I added another test to the JsPerf: http://jsperf.com/rendering-dom-in-mithril/6 to see the difference between rooted and unrooted elements; while it makes a decent difference in Firefox, Chrome does not care at all, so there must be something else.
Nowadays, VMs can call many functions with barely any penalty (There are tons of function calls in react after all). if mithril's build function were to be split in a few smaller functions, it would help in finding where most of the time is spent.
If you're interested in helping doing perf analysis on the `build` function, what I've been doing is wrapping sections in `new function foo() { ... }` blocks and running a profiler.
@Barney the `.call` … when comparing v0.1.22 and origin/next […] I think comparing against native appendChild isn't very useful because the order of magnitude in difference between a naked appendChild vs the diff algorithm overhead shadows the deltas between the incremental perf tweaks.
Another thing I still need to double check is to see if I'm inadvertedly touching the DOM when I shouldn't be.
- In Firefox, those concats are indeed the killers; Just replacing that second loop with an inner loop that pushes to the existing Array put the perfs on par with React or even better (got to go with the imperative approach sometimes). That's about a 3x boost easily. The impact on Chrome isn't nearly as much! :(
These numerous concats caused a lot of GC stress, so we now have steadier results.
- Deconstructing 'build' into many functions doesn't hurt performances, perhaps on the contrary. One possible interpretation is that Chrome is having troubles optimizing (JIT) mithril while it optimizes react just fine. I think we want smaller functions, and less dynamic forms (e.g reassigning unrelated objects, etc). Using new function() {}, is intrusive and skew the results (many short lived objects are created).
- It's definitely Chrome not being able to optimize some of Mithril's hot functions. In general, chrome is not slower than FF, so given the huge difference between the two here, it must be chrome doing something funky. http://i171.photobucket.com/albums/u320/boubiyeah/ScreenShot2014-11-15at143301_zps7a48051f.png
- I split the build function into buildObj, buildArray, buildTextNode, then split buildObj some more into buildNewObj and updateObj, etc. When smaller functions are introduced, chrome becomes a bit faster, as it can optimize a bigger percentage of the code base. I also moved the content of the setAttributes try-catch block into its own function, which again, is supposed to help chrome a bit.
- Some functions are very hard for Chrome to optimize, like the new buildNewObj. Normally, chrome can not optimize a function permanently when the function is too polymorphic; So I rewrote some part of the codebase so that for instance, a cache is never just an object with a 'nodes' property, but always a fully-fledged cache object ({tag, children, attrs, nodes}). I also made sure 'children' was never a plain string, always an Array.
Still, Chrome refuse to optimize the hottest function! At this point, I believe chrome is at least as responsible for the perfs issues than the mithril coding style itself (which is fine by me), given how FF was fast no matter what I did. The V8 optimizer seems very fragile.
What do think?
--