CSSセレクタの高速化の話を検証

CSSセレクタの高速化の話し - Webtech Walkerの件。元ネタは続・ハイパフォーマンスWebサイト ―ウェブ高速化のベストプラクティスで、元ネタの元ネタはWriting Efficient CSS for use in the Mozilla UI - MDC
先に書いておくと、この元ネタのMozillaの記事には、in the Mozilla UIとある通り、FirefoxなどのUIレベルの話です。Mozillaの場合、ウィンドウとかタブとか、とにかくなんでもCSSでスタイルを指定できる(している)のでCSSのパフォーマンスについて考慮する点が他のブラウザとはズレています。
とはいえ、実際にどうなのか検証したことなかったので、少し試してみました。

今回の検証方法は、dl>dt+ddを5重に入れ子にした300KB強の大きめなHTMLを用意して、CSSを動的に適用したときの時間を計測してみました。
検証ページはCSSセレクタのパフォーマンスです

結果はこんな感じに。(単位はms)

ブラウザ IE6 IE8 Chrome5 Firefox3.6 Opera10.53
.class 3 10 or 20*1 29.2 0 1.5
#ID tag.class 3 20.6 29.7 0 2
body … tag.class 2 20.8 30 0 2
実行時間*2 880 2000 850 1200(610)*3 430

意外にIE6が高速、だけど、よく考えてみるとシンプルなセレクタにしか対応してないからだろうなと察しが付く…。Firefoxは大変優秀で、この程度のHTMLは余裕らしい。しかし、Chromeが遅いのが本当に意外。Safari4も同じようなものなので、-webkitの代償なんでしょうか…(まあ、そのうち修正されるでしょう)。
ブックマークコメントで id:miya2000 さんからFirefoxは非同期処理しているのではという指摘がありましたが、実はその逆です。CSSの適用後、すぐにリセットする処理を入れているので、Firefox以外のブラウザはレンダリングしないのですが、Firefoxだけはその一瞬をレンダリングします。dl{height:0;}のようなCSSを実行してスクロールバーの動きをみるとわかりやすいです。
id:shinichiro_hさんのコメント受けて、実行ボタンを押してから終了するまでの時間を測るとOpera:Chrome:Firefox:IEの順番に。Firefoxは計測した箇所ではほとんど何もせず、遅延評価しているような印象です。もちろん、このテストはブラウザのベンチマークとして作ったものではないので、そういった目的ならPeacekeeperなどを試すのが良いでしょう。

そもそも、この記事は「CSSセレクタの高速化」というが本当に効果のあるものなのかという疑問から、その部分を検証するために書いたのですが、その結果をこのように貼ってみるとついブラウザ間の差に目が言ってしまいますね(と、書いてる私自身も…。反省。)。繰り返しますが、そこはあくまでおまけ程度に見ていただければと。

ついでに、同じセレクタでquerySelectorAllを実行してみた場合はこんな感じに。

ブラウザ IE6(jQuery) IE8 Chrome5 Firefox3.6 Opera10.53
.class 204.4 50 24.7 95.8 49.5
#ID tag.class 108.2 65 41.4 110.8 67.3
body … tag.class 483 66.4 50.5 119.5 80

こちらの場合、元ネタの通りクラス名だけの方が速いという結果に。なるほど、元ネタは間違ってはいないようです。

まとめ

と、その前に、今回の検証はHTMLやセレクタの組み合わせの1つのケースでしかないので、この結果もしかりです。

  • セレクタを複雑にしてもパフォーマンスにはほとんど影響はない
    • クラス名だけのセレクタは保守性を下げるだけだから、IDをつけたりして範囲を明確にしたほうが良い
    • クラス名だけのセレクタにするために要素という要素にクラス名を追加してたら、HTMLが無駄に大きくなって逆に(転送やパースに)コストがかかってお釣りがくるかも…。
  • JavaScriptからquerySelectorAllを使うときはクラス名だけのほうが高速だけど、IE6,7のことを考えるならせめてタグ名は指定したほうが良い

結論は予想通り、ありきたりとも言う。

追記: id:arikuiさんのSelectorの話し - f8gはシンプルなHTMLでのquerySelectorAllの検証。傾向は大体同じ。
あわせて読みたいt32k.com


おまけで、CSSを動的に適用する方法はこんな感じに。

var style, css;
if (document.createStyleSheet){ // IE
	style = document.createStyleSheet();
	css = function(s){
		style.cssText = s;
	}
} else {
	style = document.createElement('style');
	document.getElementsByTagName('head')[0].appendChild(style);
	css = function(s){
		style.textContent = s;
	}
}
/*
    var s = +new Date;
    css('*{color:red;}');
    var t = new Date - s;
*/

*1:最初に実行すると10msだが、なぜか他のセレクタの実行後だと20msになる

*2:実行ボタンを押してから終了するまでの時間[http://ss-o.net/test/cssh.html]

*3:レンダリングに影響の少ないスタイルの場合は約半分の時間に