アクセッサの速度を計測する in PHP

PHPで、「$obj->var」と「$obj->get_var()」にどのくらい速度差があるのかを調べてみた。
またjQueryのようにgetterとsetterを同じメソッドで行うようにしたいとき、引数があるかどうかを調べるのにfunc_num_args()を使った場合と使わない場合との速度差も測ってみた。

class Foo {
    var $var;
    /// ふつうのaccessor
    function var0() {
        return $this->var;       /// getter
    }
    /// jQueryライクなaccessor: func_num_args() を使わない方法
    function var1($arg=null) {
        if ($arg === null) {
            return $this->var;   /// getter
        }
        else {
            $this->var = $arg;   /// setter
            return $this;
        }
    }
    /// jQueryライクなaccessor: func_num_args() を使うバージョン
    function var2($arg=null) {
        if (func_num_args() === 0) {
            return $this->var;   /// getter
        }
        else {
            $this->var = $arg;   /// setter
            return $this;
        }
    }
}

$x = new Foo();
$x->var = 'foo';

$times = 1000000;    /// 100万回
echo "-- ", $times, " times\n";
$N = $times / 10;

echo '* $x->var:    ';
$t1 = microtime(true);
for ($i = 0; $i < $N; $i++) {
    $x->var;   $x->var;   $x->var;   $x->var;   $x->var;
    $x->var;   $x->var;   $x->var;   $x->var;   $x->var;
}
$t2 = microtime(true);
printf("%f sec\n", $t2 - $t1);

echo '* $x->var0(): ';
$t1 = microtime(true);
for ($i = 0; $i < $N; $i++) {
    $x->var0(); $x->var0(); $x->var0(); $x->var0(); $x->var0();
    $x->var0(); $x->var0(); $x->var0(); $x->var0(); $x->var0();
}
$t2 = microtime(true);
printf("%f sec\n", $t2 - $t1);

echo '* $x->var1(): ';
$t1 = microtime(true);
for ($i = 0; $i < $N; $i++) {
    $x->var1(); $x->var1(); $x->var1(); $x->var1(); $x->var1();
    $x->var1(); $x->var1(); $x->var1(); $x->var1(); $x->var1();
}
$t2 = microtime(true);
printf("%f sec\n", $t2 - $t1);

echo '* $x->var2(): ';
$t1 = microtime(true);
for ($i = 0; $i < $N; $i++) {
    $x->var2(); $x->var2(); $x->var2(); $x->var2(); $x->var2();
    $x->var2(); $x->var2(); $x->var2(); $x->var2(); $x->var2();
}
$t2 = microtime(true);
printf("%f sec\n", $t2 - $t1);


実行結果: (Mac OS X 10.6, Intel Core2 Duo 2GHz, PHP 5.2.11)

$ php hoge.php
-- 1000000 times
* $x->var:    0.106071 sec    # 直アクセス
* $x->var0(): 0.432793 sec    # 通常のgetter
* $x->var1(): 0.669569 sec    # func_num_args() なし
* $x->var2(): 0.853244 sec    # func_num_args() あり


考察:

  • getterを使うと、使わない場合と比べて4倍以上遅くなる。ただしその差は100万回で0.3msec程度。1回のリクエストで1000回呼び出されたとしても差は0.3nsec。これなら許容範囲内か。
  • func_num_args() を使って jQuery ライクなインターフェースにすると、直アクセスに比べて 8 倍遅くなる。それでも100万回で0.75msec程度だから、やっぱり許容範囲内だろう。
  • func_num_args() を使わない場合と使った場合の差は、思ったよりずっと小さかった。func_num_args() ってもっと遅いと思ってたけど、そうでもないのね。