2010/03/01

[PHP, 最適化]「__get, __set, __autoload は避ける。」


[追記]2013/01/25
今更以下の情報はさすがに古いです・・・
昔はこういうことがあったんだよと下の世代へお伝え下さい・・・。


よく言われる、「PHPの最適化 12 PHP Optimization tips」が本当か、今更ながら検証してみます。
環境は

  • EeePC 1002HA
  • Ubuntu 9.10
  • Apache/2.2.12 (Ubuntu)
  • PHP Version 5.2.10-2ubuntu6.4
 第2弾は「__get, __set, __autoload は避ける。」
便利な機能なマジックメソッドですが、最適化になるの??



今回のスクリプトはこちら、__autoload() テスト用にクラス定義を分けました。
A.php (クラス定義ファイル)
<?php
class A
{
    protected $b = '';
    
    public function __get($name)
    {
        return $this->$name;
    } 
    
    public function __set($name, $value)
    {
        $this->$name = $value;
    }
    
    public function getB()
    {
        return $this->_b;
    }
    
    public function setB($value)
    {
        $this->_b = $value;
    }
}
index.php (実行ファイル)
<?php
$limit = 100000;
$timerHolder = array(
    'autoload'    => 0,
    'nonautoload' => 0,
    '__set'       => 0,
    'set'         => 0,
    '__get'       => 0,
    'get'         => 0,
);

// autoload
function __autoload($className)
{
    require_once $className . '.php';   
}

$start = microtime(true);
for ($i = 0; $i < $limit; $i++) {
    $a = new A();
}
$timerHolder['autoload'] = microtime(true) - $start;

$start = microtime(true);
for ($i = 0; $i < $limit; $i++) {
    require_once 'A.php';
    $a = new A();
}
$timerHolder['nonautoload'] = microtime(true) - $start;

// getter setter
require_once 'A.php';
$a = new A();

$start = microtime(true);
for ($i = 0; $i < $limit; $i++) {
    $a->b = 'c';
}
$timerHolder['__set'] = microtime(true) - $start;

$start = microtime(true);
for ($i = 0; $i < $limit; $i++) {
    $a->setB('c');
}
$timerHolder['set'] = microtime(true) - $start;

$start = microtime(true);
for ($i = 0; $i < $limit; $i++) {
    $c = $a->b;
}
$timerHolder['__get'] = microtime(true) - $start;

$start = microtime(true);
for ($i = 0; $i < $limit; $i++) {
    $d = $a->getB();
}
$timerHolder['get'] = microtime(true) - $start;

echo '
';
var_dump($timerHolder);
echo '
';

結果
array(6) {
["autoload"]=>
float(0.352792978287)
["nonautoload"]=>
float(4.32951998711)
["__set"]=>
float(0.595301151276)
["set"]=>
float(0.38552904129)
["__get"]=>
float(0.473675966263)
["get"]=>
float(0.209407091141)
}

getter と sette に関しては、
10万回でこれだけの差しかないので、まったく問題にならないかと。
むしろ、簡単なクラスならガンガン使ってもいいんじゃない?って思う。

__autolod() は正直テストの仕方が悪いんだと思うw
require_once なり reuire なりにもコストがかかるので、
スクリプト上部でどんな時でも読み込む require より、
必要な時に必要なファイルだけ読み込んでくれる __autoload() はなかなか便利ではないか?
ファイルとクラス名との命名規則なんかは、考える必要があるけどね。