Closed leeoniya closed 8 years ago
React is quite slow (not sure how much faster v15.0 is). Though you could make the argument that it doesn't matter from its popularity.
Very true. While people like to say "React has amazing rendering performance" that is not really what the authors ever claimed. I think it is fair to say that the authors of React have always claimed that it is "good enough". The authors of React really just wanted to introduce the simplicity of server-side rendering to the browser (i.e., just rerender everything) while maintaining good performance. I think that is a perfectly valid approach.
Well, 7.7k (morphdom) [1] vs 10.9k (domvm) [2] or others [3][4][5] is a negligible difference. Even less so after gzip. Developing countries also have slow, shitty phones :)
I'm not sure where you are getting your numbers from, but with the online closure compiler: 1.5KB gzipped (3.52KB uncompressed)
True, it's unrealistic for 100%, 50% and even 20% mutations, but is realistic for 0%-5% mutations.
I've rarely seen a web application that needs to constantly rerender the screen unless it is game.
To be fair, many (most?) vdom libs also provide this [lifecycle hooks].
Absolutely. My point is that if you leverage those lifecycle hooks then you will see performance improvements. The dbmon benchmark for morphdom does not leverage any lifecycle hooks...the entire table is blindly rerendered and diffed/patched every frame.
From the example [6] at least, it looks like it actually creates a much more expensive detached DOM to do the diff, no?
My point was that the newly rendered DOM is not persisted in memory after the diffing and patching is completed (only the DOM in the real DOM is persisted... by the browser). With React, there is always a persistent real DOM and a persistent virtual DOM (that is kept around for diffing purposes).
This is really the main selling point, which is nice.
I agree :)
I think that using any vdom lib for tick/step based animations is terrible practice.
I agree :)
Disclaimer: I'm the author of domvm [7]...dbmonster [8].
Nice work. I can definitely appreciate the work that goes into maintaining a view library. Personally, I am not a fan of JSONML (or hyperscript for that matter). Disclaimer: I'm the author of marko (an HTML-based templating engine) :) On a related note, Marko and Marko Widgets are designed to be super fast:
From looking at the docs for domvm
it is unclear to me how your view library updates the DOM during a redraw. Can you please clarify?
Thanks for the discussion and thanks for helping to move the web forward.
I've rarely seen a web application that needs to constantly rerender the screen unless it is game.
No, of course not. And yes, dbmonster is a pissing contest. BUT, it's extremely valuable to gauge lib efficiency. Even if you don't redraw at 60 fps, doing so is very telling about the amount of micro-optimization that a view lib requires to get good perf/low latency and also translates directly into the battery life of mobile devices.
I'm not sure where you are getting your numbers from
I'm comparing minified sizes: https://github.com/patrick-steele-idem/morphdom/blob/master/dist/morphdom-umd.min.js
Personally, I am not a fan of JSONML (or hyperscript for that matter).
I'm a fan of these formats because they can be cheaply JSONifed, passed around (including server->client) and mutated by middleware. It really gives a lot of power to optimize if/when needed. domvm neatly supersets JSONML to make it much more pleasant to work with [1], demos [2]. But, different folks, different strokes.
From looking at the docs for domvm it is unclear to me how your view library updates the DOM during a redraw. Can you please clarify?
It maintains a vtree for each ViewModel, diffs it against the newly-reconstructed template (created by the returned render()) and patches the DOM accordingly. Pretty boring/standard vdom stuff.
Disclaimer: I'm the author of marko (an HTML-based templating engine) :)
Marko looks neat, but I have a strong allergy to logic integrated into strings/attrs and lib-specific DSLs. This includes JSX. It's definitely more designer-friendly and for those who may not be proficient in JS, but I prefer having the full power of JS and no extra tooling/parsing/compilation/learning required. The following is cringe-worthy to me: <ul if(notEmpty(data.colors))>
. With a JS-all-the-way-down approach, i don't need to learn yet another lib's conventions and tooling. Again, personal preference, but not unlike your preference to work with raw DOM vs lib-specific vdoms for Morphdom.
Thanks for the discussion and thanks for helping to move the web forward.
No need for thanking, we're all down in the same trenches :)
[1] https://github.com/leeoniya/domvm#template-reference [2] https://leeoniya.github.io/domvm/demos/
No, of course not. And yes, dbmonster is a pissing contest. BUT, it's extremely valuable to gauge lib efficiency. Even if you don't redraw at 60 fps, doing so is very telling about the amount of micro-optimization that a view lib requires to get good perf/low latency and also translates directly into the battery life of mobile devices.
I don't necessarily disagree, but it goes back to my point that performance will really depend on how morphdom
is being used.
I'm comparing minified sizes: https://github.com/patrick-steele-idem/morphdom/blob/master/dist/morphdom-umd.min.js
I fed the unminified version (https://github.com/patrick-steele-idem/morphdom/blob/master/dist/morphdom-umd.js) into Google Closure Compiler to get my drastically different (and better) numbers.
I'm a fan of these formats because they can be cheaply JSONififed, passed around (including server->client) and mutated by middleware. It really gives a lot of power to optimize if/when needed. domvm neatly supersets JSONML to make it much more pleasant to work with: demos [1]. But, different folks, different strokes.
There's tradeoffs in every design. Some design decisions will favor the system and other design decisions will favor the developer. The hard part is trying to find the right balance.
It maintains a vtree for each ViewModel, diffs it against the newly-reconstructed template (created by the returned render()) and patches the DOM accordingly. Pretty boring/standard vdom stuff.
Great. Thanks for clarifying.
Marko looks neat, but I have a strong allergy to logic integrated into strings/attrs and lib-specific DSLs. This includes JSX. It's definitely more designer-friendly and for those who may not be proficient in JS, but I prefer having the full power of JS and no extra tooling/parsing/compilation required. The following is cringe-worthy to me:
<ul if(notEmpty(data.colors))>
. With a JS-all-the-way-down approch, i don't need to learn yet another lib's conventions and tooling. Again, personal preference, but not unlike your preference to work with raw DOM vs lib-specific vdoms for Morphdom.
It depends on what you get used to. I cringe when I look at a JSON representation of HTML and I find it painful to write and more difficult to read, but that's not to say that I couldn't get used to it. My thinking is that you can either try to make JavaScript more like HTML (something like JSX
/domvm
) or you can try to make HTML more like JavaScript (as is the case with marko
). I prefer the marko
approach because it is declarative enough that it can easily be precompiled and optimized. Because a domvm
template is part of code as regular JavaScript you would likely not be able to apply the level optimizations to domvm that we have applied to a compiled marko template (e.g. concatenating static blocks of HTML) and with a marko
template we don't have to create a potentially huge intermediate tree structure just to produce output HTML (important on the server). I'm a huge fan of JavaScript and I write a lot of JavaScript, but I strongly believe I can more quickly produce an HTML view when using Marko. There are a lot of developers who really like the marko syntax so it's not just me. But yeah, "But, different folks, different strokes.".
I'm going to go ahead and close this issue because there's not really an "issue" here, but please feel free to add thoughts if you want to continue the discussion.
I prefer the marko approach because it is declarative enough that it can easily be precompiled and optimized.
Yes, this is a good perk of all pre-compiled templates.
and with a marko template we don't have to create a potentially huge intermediate tree structure just to produce output HTML (important on the server)
Also true. I'm considering moving domvm
to hyperscript to reduce GC pressure for 2.0 in some distant future. While this would remove the compulsory JSONML/array allocation before the vnode objects are built, it would also bloat the lightweight "IR". Whether the trade-off is worth it is quite unclear as the lib is already very fast. In addition, any part of domvm's templates can contain raw html or already-created dom nodes. So static branches can easily be optimized either by this or a false
-returning render()
, which does the same as shouldComponentUpdate => false
.
For morphdom
, the design decisions definitely make sense when viewed through the lens of uniformity with marko
. But morphdom as a standalone view lib seems to rely heavily on the app implementors' careful optimization to attain most of the good perf as experienced in marko.
There are a lot of developers who really like the marko syntax so it's not just me
I'm certainly in the minority here, else few would be praising Angular, JSX, Vue, etc.
My main gripe (and was my initial comment) was that I hoped to be convinced that vdom is not necessary, but in reality the penalty paid for avoiding it is simply too large without having to write a ton of extra hook code to short-circuit the build/diff/patch at every level.
morphdom's README spends a considerable amount of time explaining how the DOM is fast enough and that a vdom is not necessary. However, if we remove very slow vdom implementations (like React's) from the equation, the claims objectively do not hold up at all. The fastest DOM mutating libs (kivi, Inferno) are both based on vdom. Even the inter-op [by avoiding vdom] between different libs is far from guaranteed unless they also use the DOM to store all state associated with their mutations....which is almost never the case. The payload size argument is not very strong WRT other small vdom libs because the anything smaller than 10k gzipped will be near-indistinguishable even on a 2g connection.
From a server/SSR perspective, of course not generating an intermediate representation and simply concatenating variables/funcs/strings from pre-compiled templates will be optimal for serving things like marko
templates at 1k+ req/s.
The premise for using a virtual DOM is that the DOM is "slow". While there is slightly more overhead in creating actual DOM nodes instead of lightweight virtual DOM nodes, we are not seeing any noticeable slowness in our benchmarks.
The dbmonster bench shows that the overhead is not slight but enormous: at least 66% slower at all mutation levels. Admittedly, it may have to do with a non-idiomatic bench implementation.
Even the React impl is much faster at 1% mutations with no specialized code [1]. I would be very interested to see a morphdom
impl that performs well and is not extremely imperative and finely crafted just for this bench without losing most declarative benefits.
In addition, as web browsers get faster the DOM data structure will also likely continue to get faster so there benefits to avoiding the abstraction layer.
Browsers may get faster, but DOM implementations are already very finely tuned. They are simply hamstrung by the specs which they must implement, which will not change. A huge boost in DOM perf is not likely to be around the corner aside from some new/alternate lightweight DOM spec.
My point is that if you leverage those lifecycle hooks then you will see performance improvements. The dbmon benchmark for morphdom does not leverage any lifecycle hooks...the entire table is blindly rerendered and diffed/patched every frame.
Right, which is exactly the same thing that happens in domvm here [2], with no extra optimizations. With a single trivial optimization, the same code goes from 70fps to 145fps at 1% mutation [3].
Anyways, I'm sure you have better things to do with your time, like build awesome apps! Just thought it would make sense to remove a large quantity of unnecessary verbiage about the perils of vdom as a whole rather implicating all vdom libs by virtue of Angular's or React's bloated and slow vdom :)
cheers!
[1] http://mathieuancelin.github.io/js-repaint-perfs/react/app.js [2] https://github.com/mathieuancelin/js-repaint-perfs/blob/gh-pages/domvm/app.js [3] https://github.com/leeoniya/domvm/blob/1.x-dev/test/bench/dbmonster/app.js#L3-L47
My main gripe (and was my initial comment) was that I hoped to be convinced that vdom is not necessary, but in reality the penalty paid for avoiding it is simply too large without having to write a ton of extra hook code to short-circuit the build/diff/patch at every level.
morphdom's README spends a considerable amount of time explaining how the DOM is fast enough and that a vdom is not necessary. However, if we remove very slow vdom implementations (like React's) from the equation, the claims objectively do not hold up at all. The fastest DOM mutating libs (kivi, Inferno) are both based on vdom.
"slow" is relative. To say the DOM is slow is very misleading and I stand by the statement. I never claimed morphdom was the fastest and I do believe that it will have great performance in 99% of applications and the time spent in the render and update code will not be noticeable.
Also, I don't disagree that there is overhead with the DOM and creating real DOM nodes that are temporary will have more overhead than creating lightweight virtual DOM nodes. With that said, there is no reason that morphdom could not be used to patch the real DOM based on a virtual DOM representation as long as the API offered by the virtual DOM nodes matches the actual DOM API. See: https://github.com/patrick-steele-idem/morphdom/issues/7
From a server/SSR perspective, of course not generating an intermediate representation and simply concatenating variables/funcs/strings from pre-compiled templates will be optimal for serving things like marko templates at 1k+ req/s.
morphdom
was created to support our goal of offering best-in-class server-side rendering and great client-side rendering. We needed something that would work with an HTML string and/or raw DOM nodes.
Right, which is exactly the same thing that happens in domvm here [2], with no extra optimizations. With a single trivial optimization, the same code goes from 70fps to 145fps at 1% mutation [3].
morphdom
is best utilized internally by a higher level view library. domvm
is a higher level view library. Marko Widgets is also higher level view library. I can make tweaks to Marko Widgets to significantly boost performance and I'm sure we can make tweaks to morphdom
to significantly boost performance.
Just thought it would make sense to remove a large quantity of unnecessary verbiage about the perils of vdom as a whole rather implicating all vdom libs by virtue of Angular's or React's bloated and slow vdom :)
I never said the vdom was "unnecessary", but just that there are drawbacks to a vdom-based solution (I tried to be very careful in how I stated this too). For our apps, the drawbacks to vdom were huge (e.g. terrible server-side rendering performance). If a vdom solution works for someone else's app that's awesome. No solution is going to be perfect for every use case. In a perfect world, the application developers won't really need to concern themselves with how the DOM is updated. I know with Marko Widgets I could one day update the Marko compiler to produce virtual DOM nodes and switch to using virtual DOM diffing and no one would need to change a single line of code. As view library authors we should be building tools that abstract away the problem of updating the view. Of course, no one will ever agree with how a view should be defined :)
Short follow-up about the PR. That repo distinguishes between 'naive' and 'optimized' approaches (the yellow and orange boxes). My addition is obviously not an optimized approach 😉 .
I'm not sure this [...] does anything to dispel the belief that the DOM is slow
What has tickled me the most since stumbling on Morphdom is that the DOM performs better than expected. The naive implementation offers middle-of-the-road performance. Most libs on that site are in the mid-twenty to low-thirty redraws/sec range, and only a few hit the very-low or very-high range. The "the DOM is slow" mantra is blindly repeated so often and without qualification in our field it is very refreshing to see that the DOM is Fast Enough (TM) for most projects, and for the projects that need more there are always libs like domvm or higher-level additions like Marko. Plus the ~430 SLOC is 😻 .
Hey @patrick-steele-idem ,
Continuing https://github.com/mathieuancelin/js-repaint-perfs/pull/53 so not to clutter the pull req with OT discussion...
React is quite slow (not sure how much faster v15.0 is). Though you could make the argument that it doesn't matter from its popularity.
Well, 7.7k (morphdom) [1] vs 10.9k (domvm) [2] or others [3][4][5] is a negligible difference. Even less so after gzip. Developing countries also have slow, shitty phones :)
True, it's unrealistic for 100%, 50% and even 20% mutations, but is realistic for 0%-5% mutations.
To be fair, many (most?) vdom libs also provide this.
From the example [6] at least, it looks like it actually creates a much more expensive detached DOM to do the diff, no?
This is really the main selling point, which is nice.
True, perhaps one day: https://air.mozilla.org/bay-area-rust-meetup-february-2016/#@25m50s
I think that using any vdom lib for tick/step based animations is terrible practice.
Disclaimer: I'm the author of domvm [7]...dbmonster [8] (optimized a bit for low mutations yields 145fps @ 1%) or dumb re-diff everything [9] (70fps @ 1%)
[1] https://github.com/patrick-steele-idem/morphdom/blob/master/dist/morphdom-umd.min.js [2] https://gist.github.com/leeoniya/baa5b971f3421d2a628729e456766b8e [3] https://github.com/paldepind/snabbdom [4] https://github.com/joelrich/citojs [5] https://github.com/developit/preact [6] https://github.com/patrick-steele-idem/morphdom#usage [7] https://github.com/leeoniya/domvm [8] http://leeoniya.github.io/domvm/test/bench/dbmonster/ [9] https://github.com/mathieuancelin/js-repaint-perfs/blob/gh-pages/domvm/app.js