配列を列挙するとき、逆に回すと早い
JavaScriptで配列を列挙するとき、
for ( var i = 0; i < array.length; i++ ) {}
と書くより、
var length = array.length; for ( var i = 0; i < length; i++ ) {}
とした方が早いらしい。理由は「配列の長さを参照するのが遅い」からで、上の方だとループ回数分長さの参照が行なわれるので低速になるとのこと。で、下のコードみたいに配列の長さを一旦変数で受けておくと参照回数が減るので早くなるんだけど、一行増えるのが嫌だ。というか、この理由なら配列を逆に回せば早いんじゃね?と思って試してみた。
/** * メソッドの実行時間を計る */ function perf( f ) { var count = 1; // 試行回数 var total = 0; var max = null; var min = null; for ( var i = 0; i < count; i++ ) { var start = new Date(); try { f(); } finally { var time = new Date().getTime() - start.getTime(); total += time; if ( max === null || max < time ) { max = time; } if ( min === null || min > time ) { min = time; } } } return { avg: Math.floor((total / count)), max: max, min: min } ; } // メイン関数 function main() { var array = new Array(1000000); var fs = { "普通にループ" : function() { for ( var i = 0; i < array.length; i++ ) {} }, "サイズを一旦変数で受ける" : function() { var length = array.length; for ( var i = 0; i < length; i++ ) {} }, "逆にループ" : function() { for ( var i = array.length-1; i >= 0; i-- ) {} } } var stdout = document.getElementById( "out" ); for ( var key in fs ) { stdout.innerHTML += key + " : " + perf(fs[key]).avg + "<br/>"; } }
手近にあったブラウザでの計測結果は以下の通り。
FireFox3 | IE6 | Safari3(Windows版) | Opera 9 | Google Chrome 0.3 | |
---|---|---|---|---|---|
普通にループ | 124ms | 500ms | 203ms | 312ms | 8ms |
サイズを一旦変数で受ける | 81ms | 109ms | 94ms | 219ms | 6ms |
逆にループ | 62ms | 109ms | 93ms | 187ms | 3ms |
うむ、確かに多少は早い。なお、使えるのは逆に回しても問題ない場合だけなので注意な!(←当たり前)
ところで、こういう話、昔のJavaにもあったなー。for文を回すときにiをインクリメントするよりもデクリメントする方が早いとかなんとか。(確か数値の比較が、0を比較する場合とそれ以外で違う、とかだったような気がする)で、
for ( int i = array.length-1; i >= 0; i-- ) { ... }
↑みたいなforループがたくさん書かれていたのだけど、JavaVMのバージョンアップで差がなくなって、後には謎のfor文だけが残ったとか。いや、それほど謎ではないけどさ。
まぁ、こんな話もあり、個人的にはボトルネックでもない限り、シンプルな書き方を選択した方がいいんじゃないかと思うわけです。よほど巨大な配列を扱う場合でなければ、性能もそんなに変わらないし変にこだわる必要はないかと思います。