PHP Magic比简单设置class属性更快?

时间:2010-12-21 04:18:27

标签: php php-5.3

嗯,不完全是这样,但这是一个例子。谁能解释B和C之间的区别?如何使用魔术函数动态设置值而不是简单地在属性定义中设置值更快?

以下是一些代码:

[root@vm-202-167-238-17 ~]# cat test.php; for d in A B C; do echo "------"; ./test.php $d; done;
#!/usr/bin/php
<?php

$className = $argv[1];

class A
{
    public function __get($a)
    {
        return 5;
    }
}

class B
{
    public $a = 5;
}

class C
{
    public function __get($a)
    {
        $this->a = 5;

        return 5;
    }
}

$a = new $className;

$start = microtime(true);

for ($i=0; $i < 1000000; $i++)
    $b = $a->a;

$end = microtime(true);

echo (($end - $start) * 1000) ." msec\n";

------
598.90794754028 msec
------
205.48391342163 msec
------
189.7759437561 msec

2 个答案:

答案 0 :(得分:4)

Magic函数肯定比PHP中的任何其他函数慢,应该谨慎使用。这实际上是一个很好的博客主题(自动创建具有魔术功能的属性,以加快速度......无论如何)。正如El Yobo所述,我修改了您的PHP脚本,以便测试更准确:

<?php

class A {
    public function __get($a) {
        return 5;
    }
}

class B {
    public $a = 5;
}

class C {
    private $a = 5;
    public function __get($a) {
        return $this->a;
    }
}

$classes = array('A','B','C');

header('Content-type: text/plain; charset=utf-8');
foreach ($classes as $className) {
   $a = new $className;

   $start = microtime(true);

   for ($i=0; $i < 1000000; $i++) {
      $b = $a->a;
   }

   $end = microtime(true);

   echo 'Class ' . get_class($a) . ' = ' . (($end - $start) * 1000) ." msec\n";
}

导致

Class A = 378.85212898254 msec
Class B = 109.26413536072 msec
Class C = 423.51794242859 msec

所以,你有它。你可以清楚地看到魔术函数在使用时比公共方法执行大约多4倍。

** 编辑 **

现在,如果您动态创建新的class属性,魔术方法将仅在第一次调用,然后任何后续调用将访问动态创建的 public 属性(public以实现向后兼容)。将C类改为:

class C {
    public function __get($a) {
        $this->a = 5;
        return 5;
    }
}

将输出

Class A = 392.09413528442 msec
Class B = 110.16988754272 msec
Class C = 96.771955490112 msec

所以这就是为什么你说:“嘿!它更快!”但是,看看我们是否将迭代次数从1000000减少到10(例如):

Class A = 0.033140182495117 msec
Class B = 0.0078678131103516 msec
Class C = 0.01215934753418 msec

C类现在比B慢,因为它是对魔术方法的初始调用。我最好的猜测是PHP处理动态创建的属性与声明的属性不同。但经过进一步的研究,这些结果可能会因操作系统,CPU拱形,内存,PHP版本等而有所不同。因此,这些结果不能被视为理所当然,而且一般来说,魔术方法总是需要更长的时间执行而不是使用声明的公共属性或调用声明的公共方法。

** 编辑2 **

这是D类测试,使用动态属性创建跳过魔术方法:

class D {
   public function __construct() {
      $this->a = 5;
   }
}

产生1000次迭代的结果:

Class A = 1.3999938964844 msec
Class B = 0.42200088500977 msec
Class C = 0.3960132598877 msec
Class D = 0.37002563476562 msec       <-- faster

让我们将迭代次数增加到1'000'000:

Class A = 380.80310821533 msec
Class B = 109.7559928894 msec
Class C = 91.224908828735 msec        <-- faster ???
Class D = 96.340894699097 msec

如果魔术方法的开销很大,那么现在真正的问题是:为什么在重复多次次时访问同一属性时

public function __get($a) {
   $this->a = 5;
   return 5;
}

public function __construct() {
   $this->a = 5;
}

创建和访问动态属性时?

答案 1 :(得分:2)

在最后一种情况(C类)中,在第一次调用magic方法时创建一个名为“a”的属性;此后魔术方法将不会被调用,它只会访问该属性;这就是为什么它不像第一种情况那样慢,A级。

接下来的问题是为什么案例B比案例C慢。我猜它是在OS级别发生的;我颠倒了测试顺序,以便它测试C B A而不是A B C并且突然测试用例C比B慢,所以我有理由确定那里的性能胜利是在PHP之外;从我在第一段中的推理中可以看出,所有其他条件相同的测试用例C应该比测试用例B慢一些(因为一个调用魔法方法,然后全部其余的都是平等的)。另请注意(在我看来,无论如何)A在5.2.10和5.3.3上总是比B快。

修改

我认为问题标题在这里分散了有趣的问题;性能差异(无论它们是什么)与魔术__get方法无关。以下示例更简单地说明了这一点,而不必担心__get令人困惑的事情。相反,我们在构造函数中创建动态属性;那么唯一的区别是属性是先声明(例如public $a)还是在分配值时动态创建。

$className = $argv[1];

class A
{
    public function __construct() {
        $this->a = 5;
    }
}

class B extends A
{
    public $a;
}

$a = new $className;

$start = microtime(true);

for ($i=0; $i < 1000000; $i++)
    $b = $a->a;

$end = microtime(true);

echo (($end - $start) * 1000) ." msec\n";