Skip to content
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

Rewrite patch algo / improve diffing performance #499

Closed
jorgebucaran opened this issue Dec 17, 2017 · 19 comments
Closed

Rewrite patch algo / improve diffing performance #499

jorgebucaran opened this issue Dec 17, 2017 · 19 comments
Labels
website hyperapp.dev

Comments

@jorgebucaran
Copy link
Owner

jorgebucaran commented Dec 17, 2017

I want to rewrite patch to incorporate some prefix/suffix trimming techniques as described in the first link posted below.

Hyperapp unkeyed and keyed fares relatively well according to js-framework-benchmarks (arguably the best benchmark out there and by far), but we could do a bit better and still get away with our 1 KB proposition.

@jorgebucaran jorgebucaran added enhancement New feature or request website hyperapp.dev labels Dec 17, 2017
@jorgebucaran jorgebucaran changed the title Rewrite patch Rewrite patch algo / improve diffing Dec 17, 2017
@jorgebucaran jorgebucaran changed the title Rewrite patch algo / improve diffing Rewrite patch algo / improve diffing performance Dec 17, 2017
@leeoniya
Copy link

leeoniya commented Dec 17, 2017

there's a non-zero chance that we'll absorb uibench's 4 "worst case" scenarios [1]. prefix/suffix checks will help some of these, but not all.

all of uibench is actually a great testing/perf regression tool.

[1] https://github.com/localvoid/uibench-base/blob/efacae672bbf4133360a928305876ee1de643e64/lib/uibench.ts#L302-L319

--

the public page is here: https://localvoid.github.io/uibench/

what the linked code does should be fairly obvious from the function names.

Repository owner deleted a comment from leeoniya Dec 17, 2017
@jorgebucaran
Copy link
Owner Author

@leeoniya Thanks. I've merged your comments into the same post. I don't want to derail this issue and if possible keep the convo on point.

@okwolf
Copy link
Contributor

okwolf commented Dec 17, 2017

The core VDOM (Picodom) looks pretty close to vanilla in the latest js-framework-benchmark.

What I find really interesting is where Hyperapp underperforms Picodom:

screen shot 2017-12-16 at 23 11 13

By a factor of 3x in some cases! 😮

Edit: I mistook pico-dom for picodom, as @jorgebucaran points out below 🙃

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Dec 17, 2017

@okwolf That's a different library: https://github.com/hville/pico-dom, the picodom I wrote is not on that list. Not on point though, if you want to contribute to this discussion (I know you want to 😉), check out these links:

@okwolf
Copy link
Contributor

okwolf commented Dec 17, 2017

@jorgebucaran oh. I guess there can never be too many libraries with similar names 😕

Well, I think comparing the performance of the real Picodom library with Hyperapp might still be a worthwhile experiment.

@okwolf
Copy link
Contributor

okwolf commented Jan 15, 2018

@jorgebucaran have you done any more research on this topic yet?

I was browsing through the VDOM for Elm and thinking of you.

@jorgebucaran
Copy link
Owner Author

@okwolf A little bit. I have a simplified (still not quite working) version of this. It adds like 300 bytes, but makes Hyperapp as fast as ivi. 🙄

@joshrtay
Copy link

joshrtay commented Feb 2, 2018

solid diff library: https://github.com/ashaffer/dift

@dima-takoy-zz
Copy link

Any updates?

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Feb 23, 2018

Yes, but nothing concrete yet. I am making progress, just very slowly. I think we'll see a newly improved diff in 2.0 or after, in a few months.

My intention is not to introduce a new faster algorithm at the expense of making the code unreadable and complicated. So, not only I need to understand the problem and the solution perfectly, but I need to document it well so that everyone can understand it too.

@jorgebucaran
Copy link
Owner Author

We need to start saving the DOM instance in the vnodes for easier access later, this could even save bytes. This is also what ivi, nerv, (possibly petit-dom too) do if I am not mistaken.

@localvoid
Copy link

All fastest vdom implementations are storing reference to a DOM node in a vnode object or create one more tree that contains references to a vnode and DOM node. They don't read anything from DOM nodes, DOM api is used only for mutations.

Storing reference to a DOM node in a vnode object has obvious advantage that you won't need to instantiate many additional objects, but there is also a drawback that you won't be able to reuse vnodes, for example, to hoist static trees. It is possible to detect collisions and clone vnodes, but in my opinion it isn't worth it, because there are too many edge cases.

React is using immutable vnodes and has a plugin[1] that detects static subtrees and hoists them to improve performance.

Vue2 is using mutable vnodes[2], so I guess that it doesn't cause any problems in practice and it is worth it. Developers just should be aware of this limitations[3].

  1. https://babeljs.io/docs/plugins/transform-react-constant-elements/
  2. https://github.com/vuejs/vue/blob/6bc75cacb72c0cc7f3d1041b5d9ff447ac2f5f69/src/core/vdom/vnode.js#L8
  3. https://vuejs.org/v2/guide/render-function.html#Constraints

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Mar 9, 2018

@localvoid When you refer to hoisting static trees do you mean something like memoizing a view (that must return the same virtual DOM tree if the state hasn't changed)? In Hyperapp the state is immutable, so it's moderately "easy" to memoize views.

@localvoid
Copy link

By hoisting static trees I mean that you can hoist vnodes that doesn't contain any dynamic content from render functions and then reuse it.

For example, here is a component that consists of two spans: one is dynamic and another one is static.

function Component(props) {
  return (
    <div>
      <span>{props.text}</span>
      <span>Static Text</span>
    </div>
  );
}

In React it is possible to hoist span that doesn't change and reuse it each time component is rendered. And transform-react-constant-elements plugin can automatically hoist such static subtrees.

const staticSpan = <span>Static Text</span>;

function Component(props) {
  return (
    <div>
      <span>{props.text}</span>
      {staticSpan}
    </div>
  );
}

In Vue2 it will break because staticSpan will contain reference to a DOM node, and when you create two instances of this component, second one will overwrite reference from the previous one.

In my opinion, it is easy to optimize static subtrees just by wrapping them into components and it is better to improve overall performance by using mutable vnodes, but there are people who disagree.

@okwolf
Copy link
Contributor

okwolf commented Jul 8, 2018

Based on this tweet I'm guessing we can close this issue once 2.0 ships?

@jorgebucaran
Copy link
Owner Author

@okwolf Not yet, there's still another big optimization left to do.

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Jul 8, 2018

V2's new patch algorithm (based on Superfine) is significantly faster than V1. Here are the benchmark results. To maximize performance, the benchmarked example app makes use of memoization to boost the rendering perf of table rows that are not affected by state changes. This technique will eventually make it into core as described in #721.

Superfine

@poppinlp
Copy link

Nice job!! Can't wait~ :)

@jorgebucaran
Copy link
Owner Author

Closing as I'm tracking this particular performance optimization as part of the VDOM rewrite that will ship with V2 now.

@jorgebucaran jorgebucaran removed the enhancement New feature or request label Sep 1, 2018
Repository owner locked as resolved and limited conversation to collaborators Sep 1, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
website hyperapp.dev
Projects
None yet
Development

No branches or pull requests

7 participants