WebKit browsers have a built-in optimization technique for style rendering "that results in not even having to match style for about 60% of the elements on your page."
However, that optimization is switched off completely for the entire page if "any sibling selector is encountered anywhere in the stylesheet... This includes the +
selector and selectors like :first-child
and :last-child
."
Does anyone know the complete list of selector types that disable this optimization?
--
More Info
The optimization is discussed in Tali Garsiel's study on browser internals: How Browsers Work.
Here's the full quote on sibling selectors from Dave Hyatt, who apparently authored the browser code: "There must be no sibling selectors in use at all. WebCore simply throws a global switch when any sibling selector is encountered and disables style sharing for the entire document when they are present. This includes the + selector and selectors like :first-child and :last-child."
That quote seems to come from an article Hyatt wrote in 2005. Below he discusses it in more detail (same source as previous):
"WebCore (in upcoming Safari releases) has a really cool optimization that I came up with to avoid even having to compute the set of declarations that apply to an element. This optimization in practice results in not even having to match style for about 60% of the elements on your page. The idea behind the optimization is to recognize when two elements in a page are going to have the same style through DOM (and other state) inspection and to simply share the front end style information between those two elements whenever possible."
This article by Nate Koechley discusses the algorithm in more detail. He sums it up with:
"In web development there are often 6 different similar ways to do the same thing. What makes a good web developer is continually choosing the best of nearly-indistinguishable paths. These insider tips from Hyatt give us a more complete understanding of the guts of the browsers, and will help us choose the best methods."
Hyatt also discussed the optimization in this W3C mailing list archive
It also came up briefly in a Stack chat from Ryan Kinal: "Wow. Just wow. I will never use another sibling selector again."
I'm particularly interested in knowing:
whether child selectors also turn off the optimization
whether Trident/IE uses any similar optimization
whether any tests exist that show how big of a difference it makes for rendering performance
popupbutton is the fastest.
To optimize the CSSOM construction, remove unnecessary styles, minify, compress and cache it, and split CSS not required at page load into additional files to reduce CSS render blocking.
The adjacent sibling selector is used to select an element that is directly after another specific element. Sibling elements must have the same parent element, and "adjacent" means "immediately following".
The table outlines that attribute selectors are approximately 3x slower compared to standard classes. Relying on attribute selectors also requires more characters to target an element. It's better to define short and concise class names to keep your stylesheet small.
I don't have a full list, but this text from mozilla and Servo can be helpful, I think.
WebKit's handling of style updates
Attribute changes
If the element does not already have a style recalc flagged, and if either the attribute is the id attribute or there are selectors that involve the attribute, the element is flagged for a style recalc. There is no attempt to double-check whether those selectors have anything to do with the element and no attempt to handle cases involving '~
' and '+
' at this stage. There is also a separate hook called when class attributes change that amongst other things unconditionally flags the element as needing a style recalc. Again, no attempt is made to handle '~
' and '+
'. In none of these cases is there an attempt to optimize away selector matching on descendants.
State changes
There is no unified setup for state changes in WebKit. For each pseudo-class that is handled via boolean states in Gecko, selector matching has a dedicated function the element that it can call to test whether that pseudo-class matches. Changes to that state inside an element are responsible for directly marking that element as needing style recalc. Again, no attempt to optimize away selector matching on descendants or to handle '+' or '~
' is made. There are some optimizations here similar to the one Gecko makes for :hover
that cover :hover
, :active,
and something about dragging.
Handling of insertions and deletions
The RenderStyle has flags that indicate whether its kids are affected by various structural pseudo-classes and '+
' or '~
' combinators. On DOM mutations, the first affected element after the change (in child list order) is marked as needing a style recalc, or the single first child of the parent if it might need a recalc. If more things before the change might need a recalc, then the parent is marked as needing a style recalc, which will recalc all its kids.
In all of these cases, when actually recomputing style on an element, a check is made to see whether its kids are affected by '+
' or '~
'. If so, then if any child is flagged as needing style recalc either the child after it or all children after it (depending on whether '+
' or '~
' was involved) are also flagged as needing style recalc. There are some bugs here around chains of multiple '+
', I think.
The upshot is that in some cases WebKit ends up recomputing style on a lot more elements than Gecko does, as far as I can tell, but in others it ends up recomputing style on many fewer elements. For example, given a selector like ".foo ~ span
" and a div that changes class from "foo" to "bar", Gecko will restyle all later siblings of the div, while WebKit will not do any work at all if there are no "span" kids, since it would never have marked the parent as being affected by the '+' in that case. I'm not sure to what extent this affects insertion behavior, where it seems like the two should be more similar. Somehow WebKit seems to do better than Gecko on the HTML5 single-page spec scripts, and I can't figure out why at this point. Perhaps this is simply because its style recomputation seems to be much cheaper than Gecko's to actually run.
The other upshot is that the work involved in individual attribute and state changes is much less than in Gecko, at the cost of more style recomputation. The work involved in DOM insertion/deletion is about the same.
source
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With