
A Shocking Truth About CSS - _pius
http://blog.twoalex.com/2010/02/26/a-shocking-truth-about-css/
======
Jim72
Please, using more CSS selectors than DOMs isn't going to kill a site. In
fact, it isn't even close to being a bottleneck. We are talking milliseconds.
If you want speed gains, properly compress your images!

~~~
jrockway
Wait, are you saying that a computer capable of executing millions of
instructions per second doesn't take very long to do a few comparisons on a
dozen or so elements? It takes me a long time to do by hand, so I figured the
computer would be really slow too!

~~~
Tuna-Fish
> a computer capable of executing millions of instructions per second

Billions, not millions.

~~~
btilly
Billions of cycles per second. But executing an instruction frequently takes
20 cycles or so. That reduces instructions to hundreds of millions.

~~~
kscaldef
Except that pipelining, multiple cores, and hyperthreading then bring it back
up. So, let's not get overly pedantic here.

------
goodside
Scroll to the botom, and:

11:44:33 AM Alex K: that makes it sound like it may not be worth huge
optimization?

11:44:46 AM Alex M: no, not really

~~~
jbox
Steve's final conclusion:

“For most web sites, the possible performance gains from optimizing CSS
selectors will be small, and are not worth the costs. ”

[http://www.stevesouders.com/blog/2009/03/10/performance-
impa...](http://www.stevesouders.com/blog/2009/03/10/performance-impact-of-
css-selectors/)

~~~
rimantas
Exactly. I have an obsession with "optimized" markup, that means avoiding
unnecessary elements, ids and classes. Let's not forget that all this stuff
sits in markup and gets downloaded on each request. Even if that does not add
up to much, I still want to have my code "clean". There is one particular site
that inspires me: <http://camendesign.com/> — take a look, not a single id or
class.

~~~
jqueryin
He did this, however, at the expense of convoluted selectors, pseudo-elements,
and pattern matching. Personally, I don't find his implementation to be very
clean. I guess it's better to have the mess in your closet than wide out in
the open, though.

------
stanleydrew
Google describes this and a bunch of other great stuff about how to optimize
page rendering: [http://code.google.com/speed/page-
speed/docs/rendering.html#...](http://code.google.com/speed/page-
speed/docs/rendering.html#UseEfficientCSSSelectors)

------
bwill
Can anyone point to where this is required by the css specification? Or is
this an implementation detail? The reality may be that all the browsers do it
this way, but why couldn't someone add some more smarts to choose selectors
based on selectivity, just as a SQL query planner would?

~~~
robin_reala
It’s quicker to walk up a DOM tree than it is to walk down - that’s the root
reason why browsers have all implemented it this way.

~~~
neilk
Yeah, but if we do something like this:

    
    
        #alert > p { background: yellow }
    

Surely the intelligent thing for the CSS engine is to look for the one-and-
only #alert, rather than work backwards from all the <p>'s? (I am assuming
that nodes with ids have their own fast index.)

~~~
gizmo
It's a little trickier when you do it left to right.

Suppose you have a DOM tree like this:

    
    
        div #alert - p, p, p
               - div #alert - p [lots of children]
                            - p - div #alert [lots of children]
    

[right to left]

In order to find all Ps with an #alert parent, you first get a set of all
paragraph elements. Most likely a list of all paragraphs is kept in memory, so
it can be enumerated quickly. Since DOM trees are generally well balanced the
number of parents of any one paragraph is going to be roughly O(
2log(|DOMSIZE|) ) and it can be enumerated in C, so this is basically nothing
(compared to how grossly inefficient javascript is). Total complexity: O( |P
NODES| * 2log( |DOMSIZE| ) ). Memory complexity: 1 linked list of size of
result set.

[left to right]

If we were to look at it from left to right, it gets trickier. You have to
find all #alert nodes (easy & fast), but then you have to take all children of
all #alert nodes. Every sub-tree of the DOM can be a significant fraction of
the entire DOM tree, or perhaps the _entire_ dom tree. Not only that, but you
CANNOT filter for all "p" children, because then you'd need to have lookup
tables for every level in the tree (hugely inefficient). So you're left with
one solution, that is to enumerate all children (and a DOM node can have a few
thousand child nodes easily). But, wait, one #alert contains another #alert.
We're already looking at O( |DOMSIZE| * |DOMSIZE| ), and now we have to
consider uniqueness as well. We cannot alloc a hash map to keep track of
uniqueness, because that would completely fragement your memory space; you'd
end up allocating&deallocating hashmaps for every selector call. So you end up
having to traverse the result set linked list for every node you add, to see
if it isn't already in there. This, needless to say, is another O(N^2) detour.
Total worst case complexity for M selectors:

    
    
        O( M * |DOMSIZE|^2 + M * |RESULTSET|^2 ).
    

Memory complexity: M + 1 linked list.

Difference becomes more profound as complexity increases:

    
    
        #uniq p p span b
    

(R2L) find all "b" nodes, append those with matching parents to linked list.
Return linked list

(L2R) find all #uniq nodes, enumerate all "p" children. Make sure all "p"
children are unique. Take all children of these "p" nodes, filter for "p"
children, make sure result set is unique. Take all children of these "p"
nodes, filter for "span", make sure.... and so it goes.

So that's why.

~~~
qjz
Your examples aren't valid HTML, so that approach is fine for quirks mode. But
what about valid pages that declare a DOCTYPE? Shouldn't those be parsed from
left to right, so the first matching ID that is encountered wins (with any
other matching IDs considered errors and disregarded)?

OT, this was one of the problems XHTML was supposed to solve, since it was
originally assumed by many that pages that weren't well-formed XML would not
be rendered at all, forcing the developers to fix the code. This didn't
happen, and developers are still at the mercy of how individual browsers
implementat quirks mode.

~~~
gizmo
You have to write R2L parsing logic anyway, because it's what you want most of
the time (e.g. "div span a.quote"). Yes, in the special case where #uniq
identifiers are used _and_ you know the page to be valid (X)HTML _and_ you
know that you get a substantial speedup by filtering on the #uniq node first
L2R parsing is preferable. But how realistic is that, really?

Isn't it just easier to teach web developers to write their DOM selectors in a
specific way? The R2L approach is (a) easy to understand (b) has predictable
(and stable) performance (c) doesn't malloc (d) is easy to implement. I see
this as a simple case of "good enough".

------
ableal
(from the header:)

 _The Two Alexs

Alexes? Alex's? Alexi? Alexim?_

In hackanonical form, Alexen. As in oxen, vaxen, boxen.

~~~
riffraff
IIRC oxen is a vestigial dual (not plural) form, so quite appropriate.

~~~
bdr
According to <http://www.etymonline.com/index.php?term=ox> \-- "Oxen is the
only true survival in Mod.Eng. of the O.E. weak plural."

From what I can tell, it looks like OE nouns could have either "strong" or
"weak" declensions, and this is different from the dual form. Someone on
another forum claims that "brethren" is another example.

~~~
riffraff
may well be true, my source is an old State of the Onion talk from Larry Wall,
which although a linguist is probably not the most qualified in the world :)

------
CoryMathews
micro optimization... blah.

Why do I really doubt Alex's yoga site needs to worry about this.

------
ihumanable
John Resig (of jQuery fame) talks about why this approach was taken in the
design of the sizzle engine that powers jQuery. Its towards the end of this
talk (which is quite good by the way), I would find the exact time but my work
internet thinks its inappropriate content and won't let me watch the video :(

~~~
todd3834
could you post a link to it?

------
eneveu
Reminds me of how you may optimize your javascript DOM/style manipulations to
avoid re-calculating the styles too often. It was mentioned in this Google I/O
session on GWT performance:
<http://www.youtube.com/watch?v=q9hhENmVTWg#t=32m38s> (slides located here:
[http://code.google.com/events/io/2009/sessions/MeasureMillis...](http://code.google.com/events/io/2009/sessions/MeasureMillisecondsPerformanceTipsWebToolkit.html)
)

This is where Speed Tracer comes in handy (
<http://code.google.com/webtoolkit/speedtracer/> )

------
kellegous
If you want to know how much CSS selector matching (sometimes called Style
Recalculation) is affecting your site you can try Speed Tracer:

[https://chrome.google.com/extensions/detail/ognampngfcbddbfe...](https://chrome.google.com/extensions/detail/ognampngfcbddbfemdapefohjiobgbdl)

It's Chrome-only but you can sometimes get cross-browser insights. It's also a
lot easier to conduct a cursory investigation using this tool than it is to
start changing your selectors.

------
smackfu
I do not like their blog gimmick.

~~~
arsduo
FWIW, the post is a slightly cleaned up version of an actual conversation I
had with my husband. (I'm K, he's M.) It's not a format I'm likely to reuse
(or have used before), but it fit the occasion and captured very well our
feelings of surprise at learning how CSS parses selectors.

Plus, it's always fun to try out new formats, even -- or maybe especially --
if they don't stick.

------
jamesr
The definitive guide to this was written by then-Mozilla dev Dave Hyatt:

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

That's from April 21st, 2000 - almost a decade ago.

------
scotty79
11:44:33 AM Alex K: that makes it sound like it may not be worth huge
optimization? 11:44:46 AM Alex M: no, not really 11:44:56 AM Alex M: I’m a
purist though 11:45:18 AM Alex M: so this makes my world

Last two lines scares the hell out of me.

------
aw3c2
I don't want to read several pages of badly spoken chat english to find the
conclusion. Could someone be so kind and explain what the ___shocking truth_
__is?

~~~
gscott
That tables really are the best, CSS is good for changing fonts all at once
though. I read it all for you so don't worry about double checking.

~~~
aw3c2
Thanks!

------
FluidDjango
The "Two Alexes" are just discovering (Feb 2010) what Steve Souders explained
in detail at [http://www.stevesouders.com/blog/2009/06/18/simplifying-
css-...](http://www.stevesouders.com/blog/2009/06/18/simplifying-css-
selectors/) [which is probably the site you want to visit on "How CSS
strategies affect site performance"] (June 2009).

~~~
tjic
Being "scooped" by a mere 6 months, on a 10 year old technology doesn't really
make their realization dated.

It's new to me, and I bet it's new to most news.yc-ers.

~~~
jrockway
/me mumbles something about Reading The Fine Source. Then you don't have to
guess what your browser is doing.

~~~
smackfu
Trying to figure out HTML from reading the Firefox source is kind of insane.

~~~
jrockway
Yeah, OK. So just be ignorant instead.

~~~
etherealG
do you read the source of the linux kernel to understand how to use bash? how
about the source of a wifi driver before connecting to a network?

your premise is rediculous.

~~~
jrockway
I don't do those things, but that's not because it's a bad method, but because
I don't care. If there was some bug in some system call, or if I needed to
investigate why hardware is misbehaving, of course I'd read the Linux source.
That's one of the great things about open source software; you don't have to
treat it like a black box. You can just look at the internals and see how they
work.

(And I have proof that I do actually do this; I had a misbehaving joystick,
and the linux fork in my github contains the fix.)

Anyway, in conclusion, if you want to know what algorithm Firefox uses to
process CSS selectors, just read the Firefox source code. If you don't give a
damn, then don't do that instead. But if you care and you do nothing, that's
just silly.

------
scotty79
Don't think about how fast it is unless you have solid evidence that it is
slow.

