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
4

2 回答 2

4

魔术函数肯定比 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 倍。

**编辑**

现在,如果您动态创建新的类属性,那么魔术方法只会在第一次被调用,然后任何后续调用都将访问动态创建的公共属性(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

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

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;
}

创建和访问动态属性时?

于 2010-12-21T04:37:40.920 回答
2

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

下一个问题是为什么案例 B 比案例 C 慢。我猜它发生在操作系统级别;我颠倒了测试顺序,以便它测试 CBA 而不是 ABC,突然测试用例 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";
于 2010-12-21T04:27:55.203 回答