正規表現のベンチマーク(String.match と RegExp.exec のざっくりとした違い)

2009-01-12追記 タイトル修正しました。
2009-01-14追記 String.indexOf と RegExp.test のスコアが逆に書かれてていたのを修正しました。

String.match ばかりで、RegExp.exec を使ったことがありませんでした。
ちょっと気になったので色々とベンチマークを取ってみました。

TEST TIMES IE6 Fx3.0 Fx3.1
(JIT)
Safari3 Chrome1
(JIT)
A.
match(/\s*/) vs match(/ */)
"aaaaaaaaaaaa".match(/\s*aa\s*/) 1000000 7203 1714 327 2375 2128
"aaaaaaaaaaaa".match(/ *aa */) 1000000 7156 1638 297 2328 2148
B.
RegExp.exec vs RegExp.test
/\s*aa\s*/.exec("aaaaaaaaaaaa") 1000000 6860 4096 3095 2265 1939
/\s*aa\s*/.test("aaaaaaaaaaaa") 1000000 2094 1506 345 875 2050
C.
String.match vs RegExp.exec
"aaaaaaaaaaaa".match(/^aaaaaaa$/) 1000000 2031 1807 438 1453 669
/^aaaaaaa$/.exec("aaaaaaaaaaaa") 1000000 1797 1688 701 1437 543
D.
String.match vs RegExp.exec
(global match)
"aaaaaaaaaaaa".match(/aa/g) 1000000 13422 11773 5264 4048 4969
/aa/g.exec("aaaaaaaaaaaa") 1000000 5969 3761 2480 1953 1338

E.
String.indexOf vs RegExp.test
TIMES IE6 Fx3.0 Fx3.1
(JIT)
Safari3 Chrome1
(JIT)
"E, F, G".indexOf(",") >= 0 1000000 4468 1640 241 922 1418
/,/.test("E, F, G") 1000000 2109 484 648 656 223



TEST E. String.indexOf vs RegExp.test (TIMES 1000000)

IE6 IE7 IE8β2 Firefox2.0 Firefox3.0 Firefox3.1(JIT)
String.indexOf 2109 3604 1076 969 490 608
RegExp.test 4484 5148 3822 2266 1606 203
Safari3 Chrome1(JIT) Opera9.27 Opera9.62 Opera10α
String.indexOf 625 216 2843 1250 1265
RegExp.test 891 1148 3938 1891 2313



TEST E. はこちらでテストできます。


ソースはこうなっています。

<html><head><title></title></head><body><script>
function boot() {
  job(1000000);
}
function job(times) {
  var begin = +new Date;
  while (times--) {
    "E, F, G".indexOf(",") >= 0; // String.indexOf
        または
    /,/.test("E, F, G"); // RegExp.test
  }
  alert(+new Date - begin);
}
window.onload = boot;
</script></body></html>


グローバルマッチだと、 exec は match の3倍速? びっくりですね。
あと、String.indexOf より RegExp.test が2〜4倍速なのは想定外でした。
ごく一部の例外を除いて、String.indexOf は RegExp.test よりも高速です。修正後の表を追加しました。大変お騒がせしました。



for と while ループの比較

普通は for ループを使いますが、特定条件下では while ループのほうが15%ほど高速でした。

ただし、以下の条件をクリアする必要があります。

  • 条件
    • 配列の中身が連続している(欠落[delete]していないこと)
    • 各要素が false として評価されないことを保障できる

CSSセレクタのフィルタリングループなんかに適用すると速くなるかもしれませんね。

// 配列の仕込み
var ary = Array(100000);
var i = 0, iz = ary.length;
for (; i < iz; ++i) {
  ary[i] = i + 1;
}
ary_for(ary, 100);
ary_while(ary, 100);

function ary_for(ary, times) {
  var n = 0, v, i = 0, iz = ary.length;
  var begin = +new Date;

  while (times--) {
    i = 0;
    for (; i < iz; ++i) { // 違いはここと
      v = ary[i];
      n += v;
    }
  }
  alert("for vs while = " + (+new Date - begin));
}
function ary_while(ary, times) {
  var n = 0, v, i = 0, iz = ary.length;
  var begin = +new Date;

  while (times--) {
    i = 0;
    while ( (v = ary[i++]) ) { // ここ
      n += v;
    }
  }
  alert("for vs while = " + (+new Date - begin));
}
TEST TIMES IE6 Fx3.0 Fx3.1(JIT) Safari3 Chrome1(JIT)
F.
for vs while
ary_for(ary, 100) 100 10062 3701 325 4109 328
ary_while(ary, 100) 100 8625 2798 400 3312 307

反省会

  • やれやれ、またお直しですか。