Hatena::ブログ(Diary)

latest log このページをアンテナに追加 RSSフィード

2009-08-28

理由がない限り、Array.join("") による文字列連結は使わないほうがいいみたい

JavaScript の勉強を開始したときに「文字列の連結には Array.join() を使え」といった記事を見た覚えがあります(ソース失念したけど、 Operaの技術文書だった気がする ⇒ IE の技術文書だったかも)。

一年ほど信じてて、念のためベンチとってみたら「うそやーん」でした。ってお話。

ベンチの条件は、

  • 3種類の文字列連結方法を試す
    • 1. + Operator
    • 2. String.concat()
    • 3. Array.join("")
  • 文字列の要素数を 11個 と 55個で試す
<!doctype html><html><head><title></title>
</head><body>
<script>
window.onload = function() {
  var now = +new Date, lp = 100;
  while (lp--) { job(); }
  document.getElementById("view").innerHTML = ((+new Date) - now);
}
function job() {
  var i = 0, r = "";
  for(; i < 1000; ++i) {
    // ▼▼▼▼▼ ここを変える(これは55個版) ▼▼▼▼
    r = "base" + "1"+"2"+"3"+"4"+"5"+"6"+"7"+"8"+"9" + i
      + "base" + "1"+"2"+"3"+"4"+"5"+"6"+"7"+"8"+"9" + i
      + "base" + "1"+"2"+"3"+"4"+"5"+"6"+"7"+"8"+"9" + i
      + "base" + "1"+"2"+"3"+"4"+"5"+"6"+"7"+"8"+"9" + i
      + "base" + "1"+"2"+"3"+"4"+"5"+"6"+"7"+"8"+"9" + i;
    // ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
  }
}
</script><div id="view"></div></body></html>
    r = str.concat("base", "1","2","3","4","5","6","7","8","9", i,
                   "base", "1","2","3","4","5","6","7","8","9", i,
                   "base", "1","2","3","4","5","6","7","8","9", i,
                   "base", "1","2","3","4","5","6","7","8","9", i,
                   "base", "1","2","3","4","5","6","7","8","9", i);
    r = ["base", "1","2","3","4","5","6","7","8","9", i,
         "base", "1","2","3","4","5","6","7","8","9", i,
         "base", "1","2","3","4","5","6","7","8","9", i,
         "base", "1","2","3","4","5","6","7","8","9", i,
         "base", "1","2","3","4","5","6","7","8","9", i].join("");

結果

素数 11個
Browser + operator String.concat Array.join 速度差
Opera9.52 422 921 1734 4倍
Opera10RC 344 828 1593 4.6倍
Firefox2 1750 1797 5281 3倍
Firefox3 332 339 1296 4倍
Firefox3.5 249 251 532 2倍
IE6 1062 1172 1297 1.2倍
IE7 1078 1188 1296 1.2倍
IE8 609 625 922 1.5倍
Chrome4 124 368 327 2.5倍
Safari4 98 137 176 1.8倍
素数 55個
Browser + operator String.concat Array.join 速度差
Opera9.52 2140 3718 9204 4.3倍
Opera10RC 1516 3297 7610 5倍
Firefox2 4484 4406 タイムアウト -
Firefox3 2085 1756 3411 1.6倍
Firefox3.5 1117 1224 1941 1.7倍
IE6 5594 5500 4297 0.7倍
IE7 5641 5578 4328 0.7倍
IE8 3187 3062 2453 0.7倍
Chrome4 537 1409 1185 2.2倍
Safari4 416 529 442 とんとん?

この結果から、Array.join() を使う必然性のあるケースは以下の場合に限定されるようです、

  • Array.join(",") のように、要素間にセパレータを埋め込む場合
  • IE で沢山(30〜?)の要素数がある場合

これ以外なら、"文字列" + "文字列" + ... が速いようですね。

ブラウザによってはループ中の普遍的な部分を最適化する(ループの外に出してしまう)ため、今回のケースでは最適化されないように、変数 i の値を文字列に加えています。

気になる人は条件変えて(文字列長くするとか)追試するといいよ。

追記

50個もの文字列を一気に扱う場面は通常そんなになくて、「配列に格納されている文字列を、ループで一個ずつ取り出し、文字列に追加するやり方をするんだったら、Array.join("") 使うべき」というケースへの配慮が欠けてました。

つまり、これ(↓)に近いやり方で、文字列をちょっとずつ結合するのは NG

function join(ary) {
  var rv = "";

  for (var i = 0, iz = ary.length; i < iz; ++i) {
    rv += ary[i];
  }
  return rv;
}

それよりも、配列に溜めておいて、一気に Array.join("") を使うべきだよ。という話

function join(ary) {
  return ary.join("");
}

当たり前すぎて書かないほうが良かったかもしれませんが。追記してみました。

ここからが本題

関数内にちょこっとした文字列があって、それをわざわざ Array.join("") しているようなケースなら、+ で直に連結したほうが地球に優しいコードになりそうです。

つまり、今まではわざわざこう(↓)していたものを…

function hoge() {
  var a = "a", b = "b", c = "c", d = "d", e = "e";
  
  return [a, b, c, d, e].join("");
}

明日からは、こうすると良いかもね

function hoge() {
  var a = "a", b = "b", c = "c", d = "d", e = "e";

  return a + b + c + d + e;
}

taku_eoftaku_eof 2009/08/28 13:02 > Operaの技術文書だった気がする
"Efficient JavaScript" のことでしょうか。
原文:http://dev.opera.com/articles/view/efficient-javascript/
邦訳:http://www.hyuki.com/yukiwiki/wiki.cgi?EfficientJavaScript
ただ、これらは Array.prototype.join() ではなく + 演算子の累積スタイル利用についてで、かつツッコミが入れられています。

比較的最近の記事だと、
http://www.sitepen.com/blog/2008/05/09/string-performance-an-analysis/
が該当するかもしれません。

> IE の技術文書だったかも
は、もしかしたら IEBlog の
http://blogs.msdn.com/ie/archive/2006/11/16/ie-javascript-performance-recommendations-part-2-javascript-code-inefficiencies.aspx
かもしれませんね。

uupaauupaa 2009/08/28 13:53 コメントありがとうございます。
確かに、これらのエントリーを一読した覚えがあります。
大変ありがたいです。

+= のコストについては今回触れてませんが、Operaの技術文書は、たしかに += についての言及でしたね。

V8のコードを読んで「Array.join("") で文字列連結するのは遅そう」と疑問に思ったので、ベンチとってみました。
と…すると「Array.join("") 最高です」は、どこで仕入れた知識なんだろう?

思い出せない。

サルサル 2009/08/28 17:08 記事とは関係ないコメントで恐縮ですが、
当方、IE8でWebをみているのですが、こちらのページの
レイアウトが崩れており、ほとんど記事を読むことができません。
IE8は対応していないのでしょうか?
ちょっと見づらい、程度ではなく、全く読めないほどになっております。

サルサル 2009/08/28 17:11 パーマリンクのページのみがレイアウト崩れているようです。
トップページなどは崩れていませんでした。
補足しておきます。

mallowlabsmallowlabs 2009/08/28 20:09 もしかしたらこの記事かもです。
http://code.google.com/intl/ja/speed/articles/optimizing-javascript.html

uupaauupaa 2009/08/29 02:52 こんにちは、サル さん。mallowlabs さん。

> パーマリンクのページのみがレイアウト崩れているようです。

このページは、はてなのデザインテンプレートを使ってるのですが、テンプレ自体がIE8対応できていないので放置してました。IE8の互換表示モードにしても、コメントがコンテンツにかぶって読めなくなるので、なんかもう色々と…
同じテンプレ使ってる他の方のページもぐちゃってなってて、あーー みたいな感じです。

> もしかしたらこの記事かもです。

初めて見ました。なるほど Google エンジニアの方が書かれた記事ですね。
他の記事でも書かれていましたが、IEのガベージの実装がひどいので、+= や + でメモリを沢山使うような結合を行うと、問題が発生するようですね。
+ による結合でも、量があまりなければ問題ないようですね。

yukobayukoba 2010/01/18 10:39 IE5の時代、特に、MacOS9のIE5.1のGCは物凄くひどく、
キロバイト級の文字配列を + で連結していくと、
10分待ちとかいう時代があり、そういう時代は、join("")が最速でした。

uupaauupaa 2010/01/18 16:59 Ajaxという言葉が生まれる前(2000年頃)は、たしかにそんな感じだった気がします。文字列操作やテーブルの多数のセル操作は鬼門でしたね。当時のノウハウがリプレースされてないだけなのかもしれませんね。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト

コメントを書くには、なぞなぞ認証に回答する必要があります。