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

StyleSheet: perf characteristics #110

Closed
1 of 3 tasks
necolas opened this issue Mar 24, 2016 · 3 comments
Closed
1 of 3 tasks

StyleSheet: perf characteristics #110

necolas opened this issue Mar 24, 2016 · 3 comments

Comments

@necolas
Copy link
Owner

necolas commented Mar 24, 2016

After discussing this with @vjeux I thought of a few more things to include in a performance evaluation of inline styles vs class names.

  • inline styles vs class names
  • perf celing of StyleSheet creating a large string in memory
  • perf ceiling of StyleSheet updating a single <style> element's content (and does the browser recalc everything when you do so?)
@necolas
Copy link
Owner Author

necolas commented Jul 11, 2016

@vjeux shared some finding with me about how Chrome performs:

inserting a dom tree (newsfeed story) and matching it against a ton of css is relatively cheap (3-5ms). doing the same with inline styles is about 2x faster (1-3ms).
the biggest performance hog for css is inserting new stylesheets. it takes anywhere from 30ms to 100ms. the more elements are in the dom and stylesheets you add, the more expensive adding another stylesheet becomes.
there's one caveat. all rules are not equal performance wise. if you add only rules that are just a single class .a {}, then you can add 2k at a time and it is super cheap (1-5ms). but if you have rules that have a nested selector like .a .b {} then that's where it starts taking a ton of time.

This means injecting single classes like react-native-web has been doing should perform fine in Chrome. However, inline styles appear to be very cheap in Chrome too. And they avoid potentially creating very large strings when producing the style sheet.

@necolas necolas closed this as completed Jul 11, 2016
necolas added a commit that referenced this issue Jul 11, 2016
This fixes several issues with 'StyleSheet' and simplifies the
implementation.

1. The generated style sheet could render after an apps existing style
sheets, potentially overwriting certain 'html' and 'body' styles. To fix
this, the style sheet is now rendered first in the document head.

2. 'StyleSheet' didn't make it easy to render app shells on the server.
The prerendered style sheet would contain classnames that didn't apply
to the client-generated style sheet (in part because the class names
were not generated as a hash of the declaration). When the client
initialized, server-rendered parts of the page could become unstyled. To
fix this 'StyleSheet' uses inline styles by default and a few predefined
CSS rules where inline styles are not possible.

3. Even with the strategy of mapping declarations to unique CSS rules,
very large apps can produce very large style sheets. For example,
twitter.com would produce a gzipped style sheet ~30 KB. Issues related
to this are also alleviated by using inline styles.

4. 'StyleSheet' didn't really work unless you rendered an app using
'AppRegistry'. To fix this, 'StyleSheet' now handles injection of the
DOM style sheet.

Using inline styles doesn't appear to have any serious performance
problems compared to using single classes (ref #110).

Fix #90
Fix #106
@vjeux
Copy link

vjeux commented Jul 11, 2016

Actually, I'm sorry but this was a completely busted analysis. I later found out what was really happening.

So, when you inject stylesheets, there's a fast path that checks every rule against the current DOM and if it matches, then flags the dom node as dirty and dirties the entire subtree. This check is very crude, if you have a rule .someClass .someOtherClassThatDoesntExist and there's an element with someClass class, it'll flag it.

Then, the expensive part is to re-compute the styles of all the elements that are dirty. This scales linearly with the number of dom nodes. If you end up dirtying the root element (we have a ton of top level classes on facebook.com like hasLeftCol home composerExpanded fbx chrome webkit mac x2 Locale_en_US), then it'll take a long time.

Also, note that you only need one rule that dirties a big tree to pay the cost for the entire stylesheet you inject.

@necolas
Copy link
Owner Author

necolas commented Jul 11, 2016

Ouch, that sounds much worse. Inline styles it is!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants