-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Diffing algorithm & vdom stories #10
Comments
fwiw, many of the libs that perform well here [1] use Longest Increasing Subsequence [2] when simple checks fail (common suffix and prefix, etc) [1] https://github.com/krausest/js-framework-benchmark |
@yoshuawuyts @leeoniya WOW these resources are INCREDIBLY helpful. Thanks for diving into so much detail explaining the approaches, pitfalls and wishlist items with Choo's diffing. Being able to learn from other libraries that have been there done that is SO useful. While, like you said, not all optimizations will be tackled right away (a lot of documentation, ergonomics and bug fixing will probably happen before "perfect-performance") knowing the direction to going in whenever someone begins tackling optimizing Percy's vdom is an AMAZING start! I gave some of those links some quick scans, but I'll definitely be diving in to gain a better understand of our options here. In terms of performance approach going forwards, what I have in mind is to have benchmark driven optimizations. So I think that it would be important to have a few benchmarks in place to start (and add more over time of course). I'm thinking that our benchmarks could look like:
I'm almost thinking that getting some basic benchmarks in place (that will probably end up being not-the-best as we learn more but at least it's a starting point to improve on) is step one in our vdom optimization story? Agree? Disagree? Just ideas of course - thanks so much to both of you for all of this detail / insight!! |
That sounds like a reasonable approach! For our diffing algorithm we ended up writing about 100 tests by hand, and running another 200 or so each run using a random seed. I feel there's the possibility to do a much better job in Rust for that using proptest or quickcheck! I don't think you need to test too many different browsers. Most of the DOM modification methods are consistent across browsers.
Hope this is useful! |
WOOHOO again your experience here is so appreciated! Those tests are 👍🏿👍🏿👍🏿 thank you for linking. Yeah I think some of the crates out there can help give Percy a smooth testing story as things start coming together.
Can’t even overstate it! Your comments have already probably saved dozens of people-hours! Ty ty ty ty! |
Added some thoughts on how we can get real browser benchmarking in place #22 |
Hey @chinedufn :D Here's feedback as requested. It's mostly about what we got wrong in Choo's diffing, and what we would do differently if we had the chance to start from scratch. Hope it comes in useful for Percy's development!
Diffing
An optimization that we were planning for nanomorph, but never realized was to move to a well-known diffing algorithm rather than an ad-hoc hacky one.
After a couple of years of trial-and-error engineering, it became pretty clear to us that there's 2 important features that are hard to get right through trial-and-error:
[1, 2, 3, 4]
to[1, 3, 2, 4]
).O(n)
. (we used to do backtracking during diffing, and yep nope never again).If I had to implement a diffing algorithm today, I'd probably start by looking at the Myers diffing algorithm. It's a pretty straight forward algorithm to achieve small diffs. Once that works, the patience-diff algorithm is an optimization on top of Myers that allows for smaller, cleaner diffs.
note: an incomplete patience-diff implementation in JS can be found here. Rust should be a much better fit for a similar approach because it actually has abstractions for binary opcodes. This is also similar-ish to the way ember does it in Glimmer.
Server Rendering
A cool thing we never implemented was async rendering. Being able to wait for requests to complete while rendering turned out to be a must-have to scale out our server rendering pipeline. We never ended up implementing this in Choo, but that was more due to time constraints rather than anything else.
As a note: I wouldn't go as far as to split all rendering using
requestIdleCallback()
(RIC) the way for example React does it. I think RIC has a place, but it's a much better choice to manually use it in performance-critical paths, rather than using it by default through a scheduler. The main drawback of always-on RIC is that it creates compatibility problems with third party programs that have their own scheduling loops (e.g. maps, widgets, glsl frames, etc.)In practice in Rust this would mean every template would return a
Future
. This is probably not ready for prime-time yet; and most likely not quite needed right now. But I figured I'd share what we ended running into, and the insight we had.Standardization Efforts
Something to be aware of is that browser vendors are working on standardizing (a part of) DOM diffing (proposal). The webcomponent folks seem to be pushing this effort.
Personally I think this idea is rather promising as it opens up the potential for better interop between (JS) modules. But at least this spec doesn't seem to be exposing anything high-level, so in any case it would require wrappers to at least be useful.
Conclusion
Hope this comes in useful for the future! I'd love to see Percy realize a better diffing engine than we ever could with Choo! :D
Further Reading
The text was updated successfully, but these errors were encountered: