Replace

ある文字列を10パターンで置換したいとする。
たとえば、Aを1に、Bを2に、・・・Jを10に変えたい。"ABCDEFGHIJKLMN"を"12345678910KLMN"に変えたいわけですよ。
そこで以下の二つのコードを書いてみた。

//コード1
var x = "ABCDEFGHIJKLMN";
x = x.replace(/A/g,"1").replace(/B/g,"2").replace(/C/g,"3")
     .replace(/D/g,"4").replace(/E/g,"5").replace(/F/g,"6")
     .replace(/G/g,"7").replace(/H/g,"8").replace(/I/g,"9")
     .replace(/J/g,"10");
//コード2
var x = "ABCDEFGHIJKLMN";
var rp = {A: "1", B: "2", C: "3",
          D: "4", E: "5", F: "6",
          G: "7", H: "8", I: "9",
          J: "10" };
x = x.replace(/([A-J])/g, function(){return rp[RegExp.$1]});

コード1のほうは、replaceにreplaceを重ねて実行する。
コード2のほうは、検索は一回でその都度変換後の値を調べる。

これの最後の1文をFirefox3.0.12でそれぞれ百回ずつ実行すると・・・コード1が4.332ms、コード2が8.353msという結果になった。ちなみに、3.6Pre1(20090729判)でやってみたら、コード1が2.066ms、コード2が6.555msだった。
コード2が思いのほか遅かったぜ・・・。置換する文字数やパターン数、パターンの長さなんかでも結果は変わってきそうだけどそこまで検証する気力はなく。replace連打がなんかダサいけど早いっぽい、ということで。
なんというか、根本的に関数を呼ぶのが遅いんですよね。コード2の中の無名関数1000回コールのコストが高すぎる感じ。無名関数じゃなくても結果は変わらない。
検索自体の回数が少ないのは確かに効果的で、たとえばコード2を次のように変更すると、2.3ms(3.6pre1だと0.8ms)で帰ってくる。

x = x.replace(/([A-J])/g, "!");

そんでもってオブジェクト参照(rp[RegExp.$1])は関数呼び出しに比べると遅くはない。次のコードだと、7.5ms(3.6pre1だと6.0)になる。

x = x.replace(/([A-J])/g, function(){return "!";});

関数呼び出しに6ms、辞書の参照に0.8msかかっているわけですね。

・・・ということは、tracemonkyをもってしても、多回数ループ内の小さい関数は自力でインライン展開すべきなのかもしれませんネ。