开发者

PHP Magic faster than simply setting the class attribute?

Well, not exactly that, but here is an example. Can anyone explain the difference between B and C? How can it be faster to use a magic function to dynamically set a value instead of simply setting the value in the attribute definition?

Here is some code:

[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


Magic functions are definitely slower than anything else in PHP, and should be used carefully. This would actually be a good blog subject (auto-creating attributes with magic functions to speed things up... anyway). As El Yobo stated, I modified your PHP script so the tests are more accurate :

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

Resulting in

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

So, there you have it. You can clearly see that magic functions, when used, take about 4 times more to execute than public methods.

** EDIT **

Now, if you dynamically create new class attribute, the magic method will be called only the first time, then any subsequent call will access the dynamically created public attribute (public for backward compatibility). Change class C to :

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

Will output

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

So this why you say : "Hey! It's faster!" However, look if we reduce the iterations from 1000000 to 10 (for example):

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

Class C is now slower than B because it's initial call to the magic method. My best guess would be that PHP handles dynamically created attributes different than declared ones. But after further research, these results may vary depending on OS, CPU arch, memory, PHP version, etc. Therefore these results cannot be taken for granted, and, generally speaking, magic methods will always take longer to execute than using declared public attributes or calling declared public methods.

** EDIT 2 **

Here's the class D test, skipping the magic method whatsoever with dynamic attribute creation :

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

Yields these results for 1000 iterations :

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

Let's increase our iterations about to 1'000'000 :

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

If magic methods have a great overhead cost, the real question now is : Why, when accessing a same attribute repeatedly many times, is

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

faster than

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

when creating and accessing dynamic attributes?


In the last case (class C), you create a property called "a" the first time the magic method is called; thereafter the magic method won't be called, it will just access the property; this is why it isn't as slow as the first case, class A.

The next question is why is case B slower than case C. I'm guessing that it's happening at the OS level; I reversed the test order so that it tests C B A rather than A B C and suddenly test case C is slower than B, so I'm reasonably certain that the performance win there is outside of PHP; from my reasoning in the first paragraph, it stands to reason that all other things being equal test case C should be fractionally slower than test case B (as there will be one call to the magic method, then all the rest are equal). Note also that (for me anyway) in this example A is always faster than B on 5.2.10 and on 5.3.3.

EDIT

I think that the question title is distracting from interesting issue here; the performance differences (whatever they are) are unrelated to the magic __get method. The following example illustrates this more simply, without bothering about the __get confusing things. Instead we create the dynamic property in the constructor; then the only difference is whether the property is declared first (e.g. public $a) or created dynamically when the value is assigned.

$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";
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜