User:Jdm/SlowCSSSelectors

From MozillaWiki
Jump to: navigation, search

https://developer.mozilla.org/en/Writing_Efficient_CSS

http://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSRuleProcessor.cpp

http://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSRuleProcessor.h

http://mxr.mozilla.org/mozilla-central/source/layout/style/StyleRule.h

http://mxr.mozilla.org/mozilla-central/source/layout/style/StyleRule.cpp

http://mxr.mozilla.org/mozilla-central/source/layout/style/nsNthIndexCache.cpp

http://mxr.mozilla.org/mozilla-central/source/layout/style/nsNthIndexCache.h


Functions like SelectorMatches show up in C++ profiles when CSS selector matching is being a problem. However, that is too low level - individual calls of SelectorMatches are fast, but the sheer volume of calls causes the problem. Instead, RuleHash is used to avoid even looking at most selectors.

11:56 <bz> so in Gecko there are at least 3 ways in which a selector can be expensive

11:57 <bz> 1) The selector might need to be matched against a bunch of nodes and be slow to match

11:57 <bz> This is what you can detect from EnumerateAllRules

11:58 <bz> basically, measure the time needed for the ContentEnumFunc call, and accumulate it in the selector

11:58 <bz> this won't add any new code to SelectorMatches, which is good

11:58 <bz> and if you do it only when profiling is active somehow, it shouldn't be a perf hit in general

11:59 <bz> 2) The selector might be very broad and catch a lot of dynamic changes, triggering a lot of style recomputation on dynamic changes

11:59 <bz> 3) The selector might be very broad and catch a lot of sibling stuff, triggering a lot of style recomputation on DOM insertion/removal

12:00 <bz> not sure how best to account for 2 and 3

12:03 <bz> #2 is mostly nsCSSRuleProcessor::HasAttributeDependentStyle and nsCSSRuleProcessor::HasStateDependentStyle

12:04 <bz> #3 is the various places where the rule processor sets the slow selector flags on the node and the places in presshell and frame constructor that then examine those flags....

12:05 <bz> Specifically, NODE_HAS_EDGE_CHILD_SELECTOR, NODE_HAS_SLOW_SELECTOR, NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS, NODE_HAS_EMPTY_SELECTOR

12:05 <bz> The problem is not the call

12:05 <bz> the problem is that if it matches, we restyle either the node or the node and all its descendants or the node and all its following siblings and all descendants of all those

12:05 <bz> depending on the exact selectors that matches


For #1, we can add a timer accumulator to the nsCSSSelector class, and surround all calls to SelectorMatches with a timer that adds its duration to this accumulator. To expose this information, we could potentitally add a method to iniDOMUtils.idl that takes a DOM CSS rule and returns profiling data for all selectors within it? This requires further discussion.

With regards to #2, it would be best served by timing restyles that occur and linking them to triggering CSS selectors, but this is complicated by restyle coalescing in the engine. This can be skipped for this project.

Likewise, #3 would be best served in a similar manner as #2, but we can at least expose a flag on nsCSSSelector to indicate that the selector was involved in a LATER_SIBILINGS restyle. Those are some of the worst offenders, so this knowledge can be useful. This could be part of the information exposed in #1.