A whole lot.
These benchmarks are not designed to prove that Om is the fastest UI component system in the world. These benchmarks are designed to demonstrate that it's important to avoid implementation decisions that defy global optimization or leave so little guidance that users will inevitably make these same problematic design decisions themselves.
Of course you can correct these issues in your client side application on a tedious case by case basis, but the whole point of Om is to deliver competitive levels of component abstraction while making an entire family of common and tedious hand optimization techniques obsolete.
Open the Om TodoMVC in a tab and run the first benchmark. It creates 200 todos and on my 11 inch Macbook Air it takes Safari 7 around 120ms to render.
Now open up the Backbone.js TodoMVC in a tab and run the same benchmark. On my machine this takes around 500ms to render.
Under Chrome and Firefox, Om on my machine is consistently 2-4X faster. If you try toggling all of the todos you'll notice Om feels natural, while Backbone.js will feel a bit janky. This is probably because Om always re-renders on requestAnimationFrame. A pretty nice optimization to have enabled in your applications.
Taking a look at the Chrome Dev Tools JS profile flame graphs for this benchmark is suprisingly informative as far as how React/Om works out of the box versus how unoptimized Backbone.js works:
This is React/Om:
This is Backbone.js:
The React/Om flame graph seems to suggest, at least to my eyes, a design far more amenable to global optimization.
Ok, excellent work! But, uh, while 2-4X faster across 3 major browsers should be enough to get anyone interested, especially considering the fact that we're achieving this level of performance because of immutable data, that's nowhere near the 30X-40X claims you might have seen me make on Twitter.
Try the second Om benchmark - it creates 200 todos, toggles them all 5 times, and then deletes them. Safari 7 on my 11 inch Macbook Air takes around 5ms to render this.
Make sure to delete all of the todos from the Backbone.js benchmark first, then try the second Backbone.js benchmark. On my machine, running Safari, this takes around 4200ms to complete.
How is this possible?
Om never does any work it doesn't have to. Data, views and control
logic are not tied together. If data changes, we never immediately
trigger a re-render - we simply schedule a render of the data via
requestAnimationFrame. Om conceptually considers the browser as
something more akin to a GPU.
I suspect many JS MVC applications follow the Backbone.js TodoMVC lead
and link together changes in the model, the view, and truly orthogonal
concerns like serializing app state into
localStorage simply out of
convenience, as few frameworks provide the required support to ensure that users
keep these concerns architecturally separate. But really this should come as
no surprise, because the predominant culture leans on string based templates,
CSS selectors, and direct DOM manipulation - all markers of place-oriented
programming, and all potential bottlenecks which Om leaves behind.
Of course you can use Backbone.js or your favorite JS MVC with React, and that's a great combo that delivers a lot of value. However, I'll go out on a limb and say I simply don't believe in event-oriented MVC systems - the flame graph above says it all. Decoupling the models from the views is only the first important step.
Technical description follows.
Modifying and querying the DOM is a huge performance bottleneck, and React embraces an approach that eschews this without sacrificing expressivity. It presents a well-designed object-oriented interface, but everything underneath the hood has been crafted with the eye of a pragmatic functional programmer. It works by generating a virtual version of the DOM and, as your application state evolves, it diffs changes between the virtual DOM trees over time. It uses these diffs to make the minimal set of changes required on the real DOM so you don't have to.
When React does a diff on the virtual DOM specified by your components,
there is a very critical function -
shouldComponentUpdate. If this
returns false, React will never compute the children of the
component. That is, React builds the virtual DOM tree lazily for
diffing based on what path in the tree actually changed.
As it turns out, the default
shouldComponentUpdate implementation is
objects and arrays! So in order to determine if some properties of a component
to figure this out.
structures which we know will not be changed. Because of this, we can
provide a component that implements
shouldComponentUpdate by doing
the fastest check possible - a reference equality check. This means we
can always determine if the paths changed, starting from the root, in
Thus we don't need React operations like
exists to support both efficient subtree updating as well as good
object-oriented style. Subtree updating for Om
starting from root is always lightning fast because we're just doing
reference equality checks all the way down.
Also, because we always re-render from the root, batched updates are trivial to implement. We don't bother with the batched update support in React, as it's designed to handle cases we don't care about, so we can just use our own 6-line rocket fuel enhancement.
Finally, because we always have the entire state of the UI in a single piece of data, we can trivially serialize all of the important app state - we don't need to bother with serialization protocols, or making sure that everyone implements them correctly. Om UI states are always serializable, always snapshottable.
This also means that Om UIs get undo for free. You can simply snapshot any state in memory and reinstate it whenever you like. It's memory-efficient, as ClojureScript data structures work by sharing structure.
Expect more posts in the future elaborating ideas I've only hinted at or haven't mentioned: VCR playback of UI state, trivial UI instrumentation, client/server template sharing, relational user interfaces, and much more.
I would not have written this post or written Om if wasn't for the following people.
Brandon Bloom has been bugging me for many months to give React a closer look. Sadly I didn't give it a proper chance until I saw Peter Hunt's JSConf EU 2013 presentation, which explained the architecture.
Thanks to Kovas Boguta for humoring and encouraging me through several Om design discussions.
Thanks to Jordan Walke for inadvertently giving me the original inspiration to try something like Om via a Twitter conversation and to Peter Hunt and Ben Alpert and the other super friendly people on the React IRC channel for answering my numerous questions. Without the great technology that is React none of the above would have been possible.