読者です 読者をやめる 読者になる 読者になる

nazolabo

なぞさんのブログ

PHPベンチマーク実験室

GCAの作者の人の、ループ最適化の記事を久々に見てて、PHPでもやっぱ有効なのかな?ということでテスト。
環境はPHP5.2.1。時間計測には

function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

を使用(PHPマニュアルのmicrotimeの項にあったやつ)

テスト1(ループ変数にメンバ変数を使用した場合)

  • コード
class Test01 {
    var $mCount;
    
    function run() {
        
        // メンバ変数使用
        $time_start = microtime_float();
        $a = 1;
        
        for($this->mCount=0;$this->mCount<1000000;$this->mCount++) {
            $a = $a + 2;
        }
        
        $time_end = microtime_float();
        $time = $time_end - $time_start;
        
        echo "time1 : $time\n";
    
        // メンバ変数不使用
        $time_start = microtime_float();
        $a = 1;
        
        for($i=0;$i<1000000;$i++) {
            $a = $a + 2;
        }
        
        $time_end = microtime_float();
        $time = $time_end - $time_start;
        
        echo "time2 : $time\n";
    }
}
  • 結果

time1 : 0.32159495353699
time2 : 0.1459629535675

まあこれは言うまでもないかなー

テスト2(メンバに持っている配列をあれこれ)

  • コード
class Test02 {
    var $list = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
    
    function run() {
        
        // ベタベタ
        $time_start = microtime_float();
        $a = 1;
        
        for($i=0;$i<100000;$i++) {
            for($j=0;$j<count($this->list);$j++) {
                $a = $this->list[$j];
            }
        }
        
        $time_end = microtime_float();
        $time = $time_end - $time_start;
        
        echo "time1 : $time\n";
    
        // 配列の要素数だけ事前に取得
        $time_start = microtime_float();
        $a = 1;
        $count = count($this->list);
        
        for($i=0;$i<100000;$i++) {
            for($j=0;$j<$count;$j++) {
                $a = $this->list[$j];
            }
        }
        
        $time_end = microtime_float();
        $time = $time_end - $time_start;
        
        echo "time2 : $time\n";
    
        // 配列の参照を一時変数にコピー
        $time_start = microtime_float();
        $a = 1;
        $p = $this->list;
        for($i=0;$i<100000;$i++) {
            for($j=0;$j<count($p);$j++) {
                $a = $p[$j];
            }
        }
        
        $time_end = microtime_float();
        $time = $time_end - $time_start;
        
        echo "time3 : $time\n";
    
        // 配列の参照を一時変数にコピー・事前カウント
        $time_start = microtime_float();
        $a = 1;
        $count = count($this->list);
        $p = $this->list;
        for($i=0;$i<100000;$i++) {
            for($j=0;$j<$count;$j++) {
                $a = $p[$j];
            }
        }
        
        $time_end = microtime_float();
        $time = $time_end - $time_start;
        
        echo "time4 : $time\n";
    }
}

※PHP4の場合は参照渡しを明示する必要があります

  • 結果

time1 : 1.1919388771057
time2 : 0.53308200836182
time3 : 0.71943187713623
time4 : 0.32778811454773

ウノウラボの記事の訂正を真に受けた人は、「なんだよ毎回カウントしてないから遅くならないんじゃないのかよ!」と思うかもしれませんが、countを保持しているnNumOfElementsが外部に存在する以上、それのアクセスより一時変数にコピーしてアクセスしたほうが速いのは当然です。

結論

でもそんな負荷かかるサービス作ってないし、いざとなればeAcceleratorとかAPCとかあるし、さらには横に並べれはスケールしそうだし、そこまで気にしなくてもねー
PHPで100万回ループするような処理を書く人は気にしてみてはいかがでしょうか。