uupaa.js 0.7 1001%+ Faster! - CSSセレクタの精度と速度でjQueryを超えてみた。

CSSセレクタのチューン開始から、えーと… 今は、20時間目かな(もうそんなに…)

20時間前に立てた目標は、IE上で動作するjQueryの速度を超えること。

結果発表



uupaa.js 0.6 uupaa.js 0.7(α) jQuery1.2.6 kQuery Prototype.js 1.6.0
3174 317 371 495 2007

uupaa.js 0.7は、jQuery1.2.6と比較して17%ほど高速に。
uupaa.js 0.7は、uupaa.js 0.6と比較して1001%(10倍)ほど高速に

何をどうやって高速化したのか

数字はどうでも良くて、この辺だけに興味がある人がいそうなので、ニーズがあるようでしたらチョコチョコ書いていこうかと思ってます。

とりあえず、以下のことを中心にやりました。

  • コンビネータ(E F, E>F, E+F, E~F)のFを先読みするように変更した。
    • フローがわかりづらくなる恐れがあるためver0.6では先読みしていなかったのですが、ver0.7では先読みするように処理の流れを大きく変更しました。
  • Array.forEach(function() {...}) と Array.filter(function() {...}) を、全て for(;;) にデグレードさせました。
    • 遅いIEにあわせて泣く泣く(;;)です。IEでは、function() の呼び出し時間が無視できないほど大きいのです。
      • 高速化の50%ぐらいはこの作業
  • ary.push(arg); ではなく、ary[ary.length] = arg; に変換しました。これも関数呼び出しが遅いことを回避する理由から。
    • 実際には、length を参照する時間を避けるために、var i = -1, ary = []; としておいて、要素の追加を ary[++i] = arg; とすると、すばやく要素を追加できます。
      • IEなら1000回ループで2〜3ms早くなる。Firefox3でも1.5msほど早くなる。
    • 最終的な配列の長さが最初からわかっている時は、aryを確保する段階で、 var ary = Array(長さ); と予約(reserve)しておくと、途中で配列が伸張されなくなるためより高速に。

forEach, filter で隠蔽されていた変数を自前で用意する必要があったため、気が付いたらローカル変数が倍に増えてたり、ステップ数が2.6倍に増えていたりと、結構厳しいです。

反省会

  • まだまだ高速化の余地があったりするけど、今日はちょっともう無理。全力出してがんばるような機能でも無いし。
  • 6箇所ほどBugFixもした。
  • 相変わらず :not は未実装。
  • CSSセレクタの精度等については過去のエントリを参照のこと。 http://d.hatena.ne.jp/uupaa/20080919/1221834168
    • 最初「jQueryは色々はしょってて、正攻法では勝てんだろうなぁ〜」と、ちょっと投げやりになっていたので、満足度はかなり高いのです。
  • 「1000回ループして1msかよ」とか言う人は「神は細部に宿る」という言葉を知らんのだろうな。
    • 料理も細かな気遣いの積み重ねで最終的に大きく差が付くものです。料理だろうがプログラミングだろうが、良いものを作る方法論は同じなんじゃないかな。