I was curious about the rendering performance of different approaches to handle style in Elm apps.
I’ve made a small test application that uses requestAnimationFrame to measure the time of each rendering cycle.
Three approaches to style are implemented and compared:
HTML + CSS
HTML and Inline style
Stylish Elephants
elm-css (PR from dmy)
I really like the API of Stylish Elephants but I wanted to find out how much it “costs” in terms of performance.
My conclusion from these tests (and some real world projects) is that SE is quite expensive in terms of performance. This can be mitigated somewhat by using Element.Lazy and that is something you should plan for if performance is important.
I think the real world projects are much more valuable information than the tests!
The biggest danger of microbenchmarks is ignoring the “it doesn’t matter in practice” possibility.
For example, virtual DOM in general adds overhead compared to handwritten DOM mutations. As it turns out, this overhead typically doesn’t matter in practice.
From this test suite we observe that SE has overhead compared to not-SE, but we already knew that! This is not new information.
The critical question is: does the overhead matter in practice?
That’s why I’d put more weight on your subjective experience on a real world project (I assume you converted to SE and noticed slowness?) compared to the objective test suite.
It would be great to see an actual app before and after using SE! I think that would potentially illuminate some interesting bottlenecks, since performance hot spots in practice are so often different from where we expect them to be in theory.
(Unfortunately it’s also a lot of work to get that comparison! It would be awesome to see elm-spa-example with and without SE…)
I agree with you, this is not a very accurate test representing how things would work in a real project. But on the other hand, real world projects have so many variables that it is not very accurate to compare them either. Unless you would have something like elm-spa-example implementing both. I haven’t seen anyone blogging about any real world projects using SE yet so I don’t have anything else to refer to.
I am using SE 6.0.2 for a SPA and we are noticing some performance problems. Mostly on low end mobile devices and on pages with a lot of content (tables with data etc.). Most of those problems can be improved using Element.Lazy. I don’t think all issues are all related to SE either. But I have the feeling some of them are.
I wanted to create a test app to get an idea of how SE behaves compared to other solutions. Something where I could use the performance profiler and get a deeper understanding on what is going on. I also wanted the tests to be repeatable in reliable way.
Hopefully some other people will share their knowledge and experience using SE.
and unfortunately, lazy does not solve everything (and performance varies greatly between different browsers).
From my experience with my real-world app, if you have a page with tenths of “highly styled” elements (for example a list item flexbox container with a few flexbox children, each having margin/padding/font/color/flex set), it will matter in practice if you want a smooth experience with scrolling/hovering/menus. My strategy for now is to use Css.Foreign styles or SCSS for all elements that are rendered a lot (list items, icons, very common custom css styles batches) and use standard Html.Styled elements for everything that is rendered once.
However I believe strongly that the elm-css roadmap will improve the performance a lot and allow me to remove those work-arounds:
Once point 3. has been reached (insertRule), I expect indeed those issues to become “it does not matter in practice” for almost everyone, whereas today, dense styled pages (like gmail for example) are likely to have issues.
I made a pull request to add elm-css so we will see how it compares with the same setup but at first glance it seems quite close to stylish elephants.
I just wanted to add that I may be a minority, but as my app uses a lot of lists of accordions, I find this benchmark useful and close to my real-world use case.
What I find interesting is not that the performance is lower than “native” solutions, this is indeed expected, however the difference of the performance degradation curve shapes might be relevant, as well as the fact that pure elm solutions struggle with 60 FPS from the beginning (with 64 elements).
Thank you very much. What do you call “Small DOM update” and “Large DOM update” in the charts? (I suppose small is opening an element, large is rendering the full list?)
I find interesting that elm solutions struggle with the “view” part for small updates. I think it shows that this is not really the VDOM that is slow, but more the re-computing of the style node(s) (even if only one element style is actually changed). So if I’m not wrong, this benchmark is actually more about elm inline styling (which actually interests me more than VDOM).
You are correct, “Small Update” is opening an accordion. “Large Update” is re-render of the whole view, when switching between implementations.
I would guess the reason for SE and elm-css spending more time in the view function is that they do a lot of work. The two HTML implementations just generates VDOM nodes. SE and elm-css do a lot of List.fold, Dict and Set operations and string concatenation before generating VDOM.
You could also look at it this way:
To perform the same effect (for example render 64 accordions), both HTML/CSS and elm-css completes the test with 60 FPS, but the latter do a lot more work to accomplish this. This might not be an issue in some cases.
HTML+CSS, 64 accordions
Elm CSS, 64 accordions
These are my real world experiences from using Stylish Elephants:
If you are doing animations or reacting to mouse / touch move, you will definitely run into performance problems. You would probably be better of with native css or something like web components. Check this great talk.
If your UI consists of many nodes, for example large tables or trees, you will run into problems on mobile devices. Desktop might get by.
You might also run into problems on low end devices when the phone is doing stuff in the background since the performance headroom is very small.
My conclusions using elm-css are pretty much the same.
Also using Html.Styled.Lazy at the item level doesn’t really solve the issue for lists/tables/trees because it will force to render a style node for each item, and then this is the browser that struggle to parse all of those and recompute the styles (note that firefox is a lot better than Chrome since Quantum for this).