React Performance
aerotwist.com
aerotwist.com
The DOM manipulation is fast in this case because it's a simple appendChild every time. In other cases like where elements in the middle of a table are updated, you would get into a mess writing vanilla code, either complexity or performance wise, because you'd have to traverse the DOM to get to where you need to do updates and do each update individually. React batches such things together, and does one single update.
Show me a benchmark of an actual real app written in vanilla JS and React. I suspect the DOM manipulation time would be way higher.
The main trap you're falling prey to is the magical thinking which is sadly prevalent about the virtual DOM and batching. Basic application of Amdahl's law tells us that the only way the React approach can be faster is if the overhead of the virtual DOM and framework code is balanced out by being able to do less work. That's true if you're comparing to, say, a primitive JavaScript framework which performs many unnecessary updates (e.g. re-rendering the entire table every time something changes) or if the React abstractions allow you to make game-changing optimizations which would be too hard for you to make in regular code.
Since you mentioned batching, here's a simple example: it's extremely hard to find a case where a single update will be faster because the combined time to execute a JS framework and make an update is always going to be greater than simply making the update directly. If, however, you're making multiple updates it's easy to hit pathologically bad performance due to layout thrashing[2] when the code performing an update reads something from the DOM which was invalidated by an earlier update, requiring the browser to repeatedly recalculate the layout.
That can be avoided in pure JavaScript by carefully structuring the application to avoid that write-read-write cycle or by using a minimalist library like Wilson Page's fastdom[3]. This is quite efficient but can be harder to manage in a large application and that's where React can help by making that kind of structure easier to code. If you are looking for a benchmark where React will perform well, that's the area I'd focus on and do by looking at both the total amount of code and the degree to which performance optimizations interfere with clean separation, testability, etc.
EDIT: just to be clear, I'm not saying that it's wrong to use React but that the reasons you do so are the same as why we're not writing desktop apps entirely in assembly: it takes less time to build richer, more maintainable apps. The majority of web apps are not going to be limited by how quickly any framework can update the DOM.
1. I partially reduced that to a smaller testcase in https://gist.github.com/acdha/092c6d79f9ebb888496c which could use more work. For simple testing that was using JSX inline but the actual real application used a separate JSX file compiled following normal React practice.
2. See e.g. http://wilsonpage.co.uk/preventing-layout-thrashing/
You make a component that puts a reference div into the DOM. Next you override the default shouldComponentUpdate so when you get new data, you create the raw DOM elements using a document fragment and this.refs['elem'].appendChild(newDom) -- 0.14 syntax.
Premature optimization is usually bad. React allows you to write your app and then go as deep as necessary when optimizing later. The fact that you can do this while still keeping within react is a testament to the power of the framework/library.
The fact that a Google employee who pushed web components has a problem with a framework he doesn't know in a case that should usually be avoided without the optimizations that are possible says more about him than the framework he is criticizing.
Or possibly that you haven't paid enough attention to what he wrote. He was very clear to mention that React's productivity wins are significant but wanted to make a point about how important it is to regularly test performance rather than just assuming hype is universally true – or, conversely, that people talking about native browser performance have done the broad, valid benchmarking needed to support sweeping general claims.
As example of the difference, you're reacting defensively trying to downplay real concerns which are easily encountered on any large project and attack the source rather than engage with the actual demonstrated problem. That might feel good but unfortunately problems aren't fixed by pointing out that the reporter works for what you perceive as The Other Team.
I'm glad to see actual React developers are responding differently by trying to improve performance on weak points:
https://twitter.com/sebmarkbage/status/616950267511070721
This is clearly the effect Paul Lewis wanted from his post and it's the one which improves things for everyone who uses React.
With pure DOM, traversing the DOM isn't the issue, you can keep references around if you really need it, or indexes or any other number of abstractions to make it perform well because you probably wouldn't build it any other way and you probably would in most cases end up with a set of primatives that do that same type of batching the updates.
http://elm-lang.org/blog/blazing-fast-html
TodoMVC is way more realistic because items are actually edited, you are not just infinitely adding DOM elements. (Why would you even do that, instead of just showing the number of elements that can fit on the screen, then just reusing those?).
The key benefit of React is an extremely low cognitive load. There are only three simple concepts to grok (props, state and lifecycle) to get productive. The code is very easy to reason about (components are essentially pure functions of props and state) and debugging is much simpler than with vanilla JS or jQuery on a project of any meaningful size.
With respect to performance, React shines when it comes to DOM mutations (e.g. removing a div from DOM, creating a new div, inserting new div into the DOM) which is what you generally encounter with the real-world load. Here is a demo illustrating such load [3]. Benchmark offered by OP is amazingly contrived (actually it feels designed to show React in a bad light and lack of full source code is very telling). I struggle to think of a real-world scenario of append-only page with 1000+ images in the DOM, there is simply no valid reason to do that. React in turn makes it really easy and fast to implement infinite scrolling (similar to UITableView) and there are a couple of good open source components that address that.
[1] See the bottom of https://aerotwist.com/
[2] https://aerotwist.com/blog/polymer-for-the-performance-obses...
First off, the attraction of React, to me, is mostly based on being able to write maintainable, testable, reusable, easy to reason about code when working on large, complex webapps. Yeah, React has relatively high performance—at least when compared to a sluggish framework like Angular when writing those large apps, but that's not even the core selling point (as far as I'm concerned). But okay, part of the attraction of React could be summed up as "better performance than Angular on complex applications", and I guess a benchmark proving (or disproving) that would be nice to have.
That's not what we got. Instead, this guy tested the raw performance of some very simple code compared the sort of vanilla code you'd never ever ever write in the sort of app that React (or Angular) is actually designed to help with. So...yes, React is slower than vanilla JS at stuff that vanilla JS is faster at. Shocking.
I'm struggling to think of a less meaningful way to do the analysis.
I remember seeing people bashing angular 1.2/3 for its speed and then looking at their source and they weren't using some of the most powerful features cough cough react conf (https://youtu.be/z5e7kWSHWTg?t=327) This ended up corrected at some point by some one who looked at the source code, forked it and made it perform on par with the react version.
I don't doubt the vanilla JS is faster (in this case) but really, you need to make your source available before posting to your blog. Benchmarking in a fair way is hard, and you are damaging the public perception of a (from what I hear) a great framework. Maybe this is justified, but at least give the fanboys a chance to call you out - maybe everyone will learn something new.
Also, the standard way to do infinite scrolling when you care about performance, especially on mobile, is to reuse a small number of elements, just enough to cover the screen and then some. You don't actually create 1500 elements.
But this overhead is minimal in most use-cases. The benchmark in this article is not a real use-case.
If you wanna show 1200 images in a web page, or 1200 elements of any kind in one web page, then you should only create DOM elements for those of them that should actually be visible by the user. You read the scroll position and calculate which ones would be visible in the viewport, create DOM elements for those, and disregard the rest.
In most real-world applications, this technique would suffice. Your DOM and vDOM would be small, and you'd only be diffing maybe 5-10 elements at a time.
Although, I can think of use-cases where this technique, and React's style of coding may not be sufficient. One example is iOS's Photos app. Sometimes it animates hundreds of elements at a time (where you're viewing photos by year or location). I guess diffing might not be a fast enough solution for this use-case.
Vanilla JS or React, you can't have 1200 images sitting on a web page all at once without optimizing for what the user is actually looking at and interacting with.
To this day, I still wouldn't be done with the application had I used vanilla JS, so even with the performance tuning it was worth it, but it was not without cost.
if you share that code with me i will find the actual problem for you, just because i'm curious. it isn't react.
One big issue was that I wanted dependent fields to update as-you-type, which also meant validating as-you-type. I added a timer to delay that so that this wasn't all running on every single key-stroke, but wouldn't update until you stopped typing.
Reducing the number of dependent values in the DOM tree at a single time helped a lot too, and there were logical categories to split them into. My validation code was not particularly optimized either, since it was originally designed for batch-processing.
I really don't get huge concerns people have with performance. I find chrome's profiling tools to be very primitive to what I'm used to, but they were more than adequate for me to steadily improve performance.
There's probably still some improvements to be had, but right now there's no issues with responsiveness on mobile, so I stopped looking.
Also, I know approximately nothing about web browsers. It's possible that using something like bootstrap or angular would have given me a big boost here as well, but the article is comparing to vanilla JS, not to other frameworks.
Doing a couple of passes with Chrome's profiler or React's profiling tools would give you a better idea of where your time is going.
I know this is obvious, but I'll say it anyway benchmark is a bit pointless against hypothetical alternative, it should be benchmarked against frameworks trying to solve same problem like WebComponents (Polymer), Angular or other frameworks.
I know that React’s performance has been compared to that of other frameworks, like Angular. What I wanted to do was my own test of it against plain old vanilla JavaScript…The docs claim that JavaScript is fast, and it’s meddling with the DOM that’s slow. So for that to be true we should be largely able to switch out React with something else and see pretty similar performance characteristics.
IOW, it's a common assumption that React has minimal overhead for DOM manipulation, and this benchmark suggests that it rapidly becomes performance constrained on relatively small DOM trees, especially on mobile.
I don't know if this is accurate or not (I suspect there's something a bit off, because the numbers don't look realistic to me) but it's worth looking at anyway!
Comparing it to vanilla JS is the pointless part to me, that's like comparing a appending a list, and diff algorithms together, with complicated way to say it.
Of course it is a lot of work to diff two DOM trees, but the general impression on React was that the DOM is soo unbelievably slow that it will still be much faster to do the diffing (which i never understood, technically, but what the heck, FB engineers are wizards!). At least, that was my impression i got from the React announcements. And i don't even code JS nowadays, so i couldn't care less if you prefer React or Ext or write everything as Silverlight plugin :P
If you have a table and want to edit information this is trivial to do in a React like way without React. Edit button stores reference to the row, edit values, update data store, render out new row element, remove element, insert new element. Yes, React will generally be less code and the argument becomes whether this is more complex, which it is, but I'd say neither the complexity or performance truly suffer. I bet the performance will be faster as you removed diffing entirely and React will have to perform your logic anyway.
Nevertheless, if you ignore the code complexity, i am fairly certain you can write JS code to update something without needing to traverse the whole DOM each time. I don't program JS, but wouldn't you have some variable holding the reference to the place you want to update? Like you have a DOM element that will be updated every second, you would surely not search the whole DOM every second, but get it once and udpate it multiple times? No?
This would cast doubts on some of the theoretic claims on the React docs, at least for mobile.
[1] https://facebook.github.io/react/docs/advanced-performance.h...
> Did you set shouldComponentUpdate to false? Yeah that would definitely improve matters here, unless I need to update something like a “last updated” message on a per-photo basis. It seems to then become a case of planning your components for React, which is fair enough, but then I feel like that wouldn’t be any different if you were planning for Vanilla DOM updates, either. It’s the same thing: you always need to plan for your architecture.
The Vanilla DOM equivalent is just not writing any update code until you need it. I'd argue that adding...
shouldComponentUpdate() { return false }
...is the equivalent React way of saying "this will never be updated", but the default behaviour is the opposite of Vanilla DOM because React automatically handles updates for you, regardless of when you realised you were going to need them.The "last updated" scenario would be a much more interesting comparison with Vanilla DOM, as a React component's lifecycle gives you an obvious place to set up/tear down the necessary timeouts on an individual basis and updates happen only when they need to. I was pleasantly surprised by how trivial it was to make _every_ time/date displayed in my Hacker News clone live-update, even down to the per-second level: https://github.com/insin/react-hn/commit/08893a046b5289b07ef...
Something to worry about!
As someone else pointed out, the render function is another interesting part where time can most likely be wasted. Unfortunately there's no way for us to know more.
In this case, it's not hard to reduce the constants to a point where the complexity doesn't really matter though.
The diffing operation is linear according to React documentation: https://facebook.github.io/react/docs/reconciliation.html
Is Array.prototype.concat linear, too? What about the ImmutableJS equivalent? I don't know a whole lot about algorithms, my intuition is that there would be a way to add an item to the end of the array without traversing the entire thing, but I guess it all depends on how the data structure is implemented.
Each time you add an item to a list of size n, it needs to process the previous n items. This forms the series 1+2+...+(n-1)+n, which is (n^2+n)/2.
Obviously a reference comparison is a lot quicker than a render, so you get big speed wins using immutablejs that are definitely worth it, but it's still O(n^2) components processed.
Even though keys help here, render is still called for every image every time and then diffed.
He'd have to extract the image divs to a component and put PureRenderMixin on it so that the component's render is skipped altogether.
A better way to do this could be with child components with their own unique key, but since we can't see the render method I'm not sure exactly what's going on.
nb: I'm not saying React is definitely "fast" here, just pointing out a potential flaw. I'm also not saying I'm definitely right ;)
I would love to see the same comparison with Ember.js in the mix.
I think though examples like this are good to show you shouldn't always use a framework.
Furthermore with mobile, i've used ES6 classes, handlebars for templates, jquery to drop the html in, and do event bindings. It runs decently well in a cordova app, but i do find somethings lag. I've done a bit with react in cordova as well and found similar problems. Getting a speedy mobile app built with html5 is hard. I really think for a proper experience native is the way to go. I've been liking react native for a side project of mine, but i'm starting to consider using Swift instead.
It doesn't matter what technology you use, it's a bad idea.
So he's basically tested the performance of a badly architected application.
The correct way to design such components would be to only create DOM for elements in view. Which is why this test is flawed.
Consider the chart under "Mobile: Vanilla" heading. It seems to indicate that the total rendering time _drops_ with the number of pictures. That is _almost_ physically impossible. But it is super easy to get that kind of result in benchmarks, e.g. by not properly clearing caches between runs.
Here's a possible explanation (but who knows):
At a certain page height, WebKit/Blink may change rendering behavior. It doesn't need to render elements that are far away from the viewport.
So if the benchmark produces weird results, and is published without source code, so that the results cannot be reproduced, why would anyone trust it?
Even the author suspects the V8 optimized away the vanilla code... and if that is what happened, then apples are being compared to oranges and the whole conclusion is bogus. Which was kind of my point.