2012年12月24日月曜日

PHPのforループの書き方

jQuery/JavaScriptの高速化テクニックまとめ
を見てたら下記のような記述があった。

for内にlengthを使わない(3倍以上早い)
for( i = 0 ; i < array.length ; i++ ){
}
より
for(var i = 0 ,len = array.length; i < len ; i++ ){
}
の方が3倍以上早い
JavaScriptはいろいろ面倒なんだなぁ・・と思ってたけど、
もしかしてPHPでも同じだったりしてと思い試してみた。

検証コードは下記のとおり。
PEARのBenchmarkで長さ100万の配列を
ループするだけの処理を作って実行時間を計測した。
    require_once 'Benchmark/Timer.php';

    // 長さ100万の配列
    $array = array_fill(0, 1000000, 0);

    echo "[1. 毎回count()]" . PHP_EOL;
    benchmark(function () use($array) {
        for ($i = 0; $i < count($array); $i++) {
            ;
        }
    });
    echo PHP_EOL;

    echo "[2. 最初に一度だけcount()]" . PHP_EOL;
    benchmark(function () use($array) {
        $count = count($array);
        for ($i = 0, $count = count($array); $i < $count; $i++) {
            ;
        }
    });
    echo PHP_EOL;

    echo "[参考. foreach]" . PHP_EOL;
    benchmark(function () use($array) {
        foreach ($array as $key => $value){
            ;
        }
    });
    echo PHP_EOL;

    echo "[なぞ. 2.とおなじ処理でuseを参照渡し]" . PHP_EOL;
    benchmark(function () use(&$array) {
        for ($i = 0, $count = count($array); $i < $count; $i++) {
            ;
        }
    });
    echo PHP_EOL;

    exit();

    function benchmark($func) {
        $timer = new Benchmark_Timer();
        $timer->start();

        $func();

        $timer->stop();
        $timer->display();
    }

結果は、前述のJavaScriptの例と同じように
  • 2. 最初に一度だけcount() → 約5ms
  • 1. 毎回count() → 約28ms
となった(詳細は文末に)。
2.のほうが断然速い(5倍くらい)。
今までずっと1.で書いてたけど、
foreachより遅いみたいでちょっとショック。

あと一点謎なのが、本題と関係ないけど
無名関数にuseで外側の配列みれるようにしているのを
リファレンスにすると、なぜかすごく遅くなること。

実行結果
(環境:Windows8 64bit/Intel Core2 Duo E6600/メモリ4GB)


[1. 毎回count()]
----------------------------------------------------
marker  time index            ex time         perct   
----------------------------------------------------
Start   1356292436.43409400   -                0.00%
----------------------------------------------------
Stop    1356292436.70909500   0.275001       100.00%
----------------------------------------------------
total   -                     0.275001       100.00%
----------------------------------------------------

[2. 最初に一度だけcount()]
----------------------------------------------------
marker  time index            ex time         perct   
----------------------------------------------------
Start   1356292436.70931900   -                0.00%
----------------------------------------------------
Stop    1356292436.76084800   0.051529       100.00%
----------------------------------------------------
total   -                     0.051529       100.00%
----------------------------------------------------

[参考. foreach]
----------------------------------------------------
marker  time index            ex time         perct   
----------------------------------------------------
Start   1356292436.76101700   -                0.00%
----------------------------------------------------
Stop    1356292436.97736200   0.216345       100.00%
----------------------------------------------------
total   -                     0.216345       100.00%
----------------------------------------------------

[なぞ. 2.とおなじ処理でuseを参照渡し]
----------------------------------------------------
marker  time index            ex time         perct   
----------------------------------------------------
Start   1356292436.97756500   -                0.00%
----------------------------------------------------
Stop    1356292437.18008100   0.202516       100.00%
----------------------------------------------------
total   -                     0.202516       100.00%
----------------------------------------------------