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

Performance and Laziness #75

Closed
ccorcos opened this issue Feb 9, 2017 · 32 comments
Closed

Performance and Laziness #75

ccorcos opened this issue Feb 9, 2017 · 32 comments
Labels
enhancement New feature or request website hyperapp.dev

Comments

@ccorcos
Copy link

ccorcos commented Feb 9, 2017

It appears there's no laziness at all in hyperapp -- no diff of props:

https://github.com/hyperapp/hyperapp/blob/f29647bf9da4bc281078abcacd116031f9fb0c3e/src/app.js#L159-L163

Have you tried this out in a large application with 1000s of nodes?

@jorgebucaran
Copy link
Owner

jorgebucaran commented Feb 9, 2017

There's also no batching for vnodes at the moment, but there used to be. There are more than a couple things we could do to optimize our virtual-dom implementation.

@ccorcos there's no laziness at all

Can you help with that?

See also:

@jorgebucaran jorgebucaran added the enhancement New feature or request label Feb 9, 2017
@leeoniya
Copy link

leeoniya commented Feb 9, 2017

@jbucaran please consider implementing https://github.com/krausest/js-framework-benchmark, it is much more representative of real-world performance.

the only thing dbmonster tests is initial creation and .className and .textContent (or .nodeValue) replacement; it doesnt test complex reconciliation cases, track-by-key bindings or dom mutations that cause reflow/restyle.

@jorgebucaran
Copy link
Owner

@leeoniya Thanks! That's next on my list.

@ccorcos
Copy link
Author

ccorcos commented Feb 10, 2017

Can you help with that?

Sure! Let me see what I can do.

@ccorcos
Copy link
Author

ccorcos commented Feb 10, 2017

oh wait, I just realized there's no composition of components here...

@jorgebucaran
Copy link
Owner

@ccorcos What do you mean?

@tunnckoCore
Copy link

@jbucaran maybe #2, haha. @ccorcos ?

@ccorcos
Copy link
Author

ccorcos commented Feb 12, 2017

yes, every similar. there needs to be some way of combining components together. Hyper isnt so scalable in terms of breaking up components and reusing them by combining them together. For example:

const TwoCounters = app({
    model: {
        one: Counter.model,
        two: Counter.model,
    },
    update: {
        one: ({one, two}) => ({two, one: Counter.update(one)})
        two: ({one, two}) => ({one, two: Counter.update(two)})
    },
    view: ({one, two}, actions) =>
        <div>
            {Counter.view(one, actions.one)}
            {Counter.view(one, actions.two)}
        </div>
})

Then laziness could be introduced where Counter one doesnt need to be rerendered when Counter two changes...

@jorgebucaran
Copy link
Owner

jorgebucaran commented Feb 12, 2017

@ccorcos This is now supported since #77.

<MyComponent propts=...>children</MyComponent>

@tunnckoCore
Copy link

tunnckoCore commented Feb 17, 2017

We are almost like Angular, it's not okey. But yea.

Can you add choo? So we can see how we are compared to it which is similar and uses real dom. Because switching to nanomorph is absolutely easy and i've tried it.

@leeoniya
Copy link

leeoniya commented Feb 17, 2017

from that screenshot, there's not a single recent version lib there; the fastest impl (Mithril) is using an ancient 0.1.21.

take my word for it, just bench this: https://github.com/krausest/js-framework-benchmark, don't waste your time with anything else.

@jorgebucaran
Copy link
Owner

jorgebucaran commented Feb 17, 2017

@tunnckoCore You know, this is time consuming, so I can't promise I'll add it right away, but I'll try 👍

@leeoniya I just forked lhorie's implementation of the benchmark and added mine, I wasn't really paying attention to the version of the other libraries.

Do you have any idea how to get started with the krausest/js-framework-benchmark benchmarks? The README does an incredible good job at not explaining that and from all the other benchmarks I've seen, this one looks more challenging.

@leeoniya
Copy link

leeoniya commented Feb 17, 2017

Do you have any idea how to get started with the krausest/js-framework-benchmark benchmarks?

Yes. The implementation itself is easy, though actually running the bench is less easy - i usually leave the latter to @krausest since he does this for the majority of pull requests and merges.

There are two versions of the bench, and you can implement one or both: -keyed and -non-keyed. Here are examples of my impls with domvm [1][2]. Your package.json should have a build-prod script [3] that bundles and compiles your app as needed to run in the browser.

Other than that, all you have to make sure is that it behaves correctly in the browser, like vanillajs and vanillajs-keyed. You can get an idea of the expected perf from the console timers that are called from your impl, but the actual bench extracts the numbers from Chrome's timeline in a very stable/reproducible way between runs.

I would start by pulling the repo, running build-prod in the vanillajs-keyed dir, and opening its index.html in a browser.

[1] https://github.com/krausest/js-framework-benchmark/tree/master/domvm-v2.0.1-keyed/src
[2] https://github.com/krausest/js-framework-benchmark/tree/master/domvm-v2.0.1-non-keyed/src
[3] https://github.com/krausest/js-framework-benchmark/blob/master/domvm-v2.0.1-keyed/package.json#L7

@jorgebucaran
Copy link
Owner

@leeoniya Thanks, this is at least a start. By the way, can you elaborate on:

don't waste your time with anything else.

You clearly are aware of something I am not.

@jorgebucaran
Copy link
Owner

jorgebucaran commented Feb 17, 2017

Also, I'm not sure what it means, but FWIW HyperApp was actually the fastest in the async version of the benchmark (mithril was not even close, I wonder what that is about). 😄

@leeoniya
Copy link

You clearly are aware of something I am not.

I've done impls of a lot of benchmarks. This one is by far the most representative of expected perf and highly stable between runs due to its methodology. Basically, this bench tends to accurately reflect the performance results of all other benches and covers enough surface area not to only test a couple specific cases (like dbmon). There's nothing TodoMVC bench will tell you that you won't find out more thoroughly and with better feedback from js-framework-benchmark.

There are other good ones, like uibench and vdom-benchmark but they don't have the most up-to-date impls of other libs, so it's difficult to compare vs most recent lib versions. They also dont have great output for assessing relative perf in an intuitively-colored scale.

Also, I'm not sure what it means, but FWIW HyperApp was actually the fastest in the async version of the benchmark. 😄

If your implementation or lib is async, rather than relying on the console timers, you should open up chrome's timeline view and record the perf between actions. Then look at "Scripting time", etc in the pie chart.

@krausest
Copy link

I added some hints how to start your implementation in the README of js-framework-benchmark at the bottom. Just let me know if that helps. If you have specific questions don't hesitate to ask.

@ccorcos
Copy link
Author

ccorcos commented Feb 17, 2017

@jbucaran I bet the bigger the app, the worse hyper will perform. try that benchmark with a website that has 1000s of components

@tunnckoCore
Copy link

tunnckoCore commented Feb 17, 2017

@ccorcos of course, absolutely, because each "app" can sucks more than the framework with which it is built. No matter which is the "framework". 2c :D

@ccorcos
Copy link
Author

ccorcos commented Feb 17, 2017

well my point is if you wanted to build a custom visualization or some kind of dashboard, the lack of laziness is going to become a problem. i dont mean to be a put-down -- I like what you guys are building here

@FlorianWendelborn
Copy link

FlorianWendelborn commented Feb 18, 2017

Related question: does hyperapp currently re-use old DOM nodes when rendering? (especially when unchanged)

@jorgebucaran
Copy link
Owner

jorgebucaran commented Feb 26, 2017

tl;dr

  • Optimized engines like Inferno and Mithril are faster, but there is no extraordinary advantage.
  • HyperApp has excellent performance and it's on the same range as Cycle and React; it's faster than Riot, Ractive, Choo, Stem.
  • Choo has the poorest performance of all tested frameworks.

I followed @leeoniya's suggestion and implemented the js-framework-benchmarks.

What benchmarks were used?

Results

screen shot 2017-02-27 at 20 29 32

Hardware

Notes

  • Compare with @krausest's results here, that used similar hardware.

  • What's to do next? Improve HyperApp's vdom engine. Focus on the first detected weakness: deleting items. Done.

@krausest
Copy link

To me the most interesting result is create 1,000 rows where hyperapp is twice as fast as all the others whereas create 10,000 is in the range of others. I think you managed to trick my benchmark runner in this case I'm afraid I wouldn't take that result as granted...

@jorgebucaran
Copy link
Owner

Yep, that was my mistake when adding the frame, it's fixed already. 🙇

lukejacksonn pushed a commit to lukejacksonn/hyperapp that referenced this issue Mar 2, 2017
@jorgebucaran
Copy link
Owner

Some improvements: #124.

@nichoth
Copy link

nichoth commented Mar 2, 2017

Just curious, what led you to implement another view renderer instead of using an existing one? File size?

@jorgebucaran
Copy link
Owner

@nichoth 🤔 I can think of two reasons: one is file size; I am willing to trade some performance for less bytes. If you look at the git history you'll see we went from virtual-dom, to morphodom, then Snabbdom and finally the current custom engine.

After #124 lands we'll be 1869 bytes, <=1500 without the Router. So, 1kb is more of a stretch now, but it's still in the same range.

Also, speed will have improved significantly. See the js-framework-benchmarks above 🔥.

The other reason is: I wanted the entire source of truth in a single place.

Whenever I try to understand how some library X works, I'm often disappointed that I have to go through dozens of modules, also with deps themselves, and sometimes not written by the same person, which usually translates to quite different coding styles.

@selfup
Copy link
Contributor

selfup commented Mar 8, 2017

Should we consider using switch statements for perf improvement? They create lookup tables and are optimized for string evaluation especially.

Only a few flat if trees would benefit from this however

@jorgebucaran
Copy link
Owner

@selfup 🤔 If it makes things any faster, then why not, I guess. Won't switch statements take more space than if else?

@FlorianWendelborn
Copy link

@jbucaran they have low entropy. With gzip it shouldn't matter.

@jorgebucaran
Copy link
Owner

I think we can close here and continue to track future optimization efforts in #183.

Since 0.8.0, and with the introduction of keys, our patch/diff algorithm became more complex (but also more powerful and useful). Performance is now around 1.34~1.37, which is still fine.

@jorgebucaran
Copy link
Owner

jorgebucaran commented Sep 1, 2018

Summary

  • V2 (via minor feature bump posterior to the official release) will introduce a feature equally powerful to React's shouldComponentUpdate comparable to Elm's Html.lazy function (V2 Lazy lists/components #721)
  • V2 will have Superfine's "blazing fast" VDOM built-in (#499)

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
enhancement New feature or request website hyperapp.dev
Projects
None yet
Development

No branches or pull requests

8 participants