There is a major performance issue when using in-object array’s as a property versus using a global php array variable, why?
To benchmark this problem I created the following benchmark that stores an increasingly larger array with an stdClass as a node, two tests were run one using an array property in a class the other a global array.
The test code
ini_set('memory_limit', '2250M');
class MyTest {
public $storage = [];
public function push(){
$this->storage[] = [new stdClass()];
}
}
echo "Testing Objects".PHP_EOL;
for($size = 1000; $size < 5000000; $size *= 2) {
$start = milliseconds();
for ($a=new MyTest(), $i=0;$i<$size;$i++) {
$a->push();
}
$end = milliseconds();
echo "Array Size $size".PHP_EOL;
echo $end - $start . " milliseconds to perform".PHP_EOL;
}
echo "================".PHP_EOL;
echo "Testing Array".PHP_EOL;
for($size = 1000; $size < 5000000; $size *= 2) {
$start = milliseconds();
for ($a=[], $i=0;$i<$size;$i++) {
$a[] = [new stdClass()];
}
$end = milliseconds();
echo "Array Size $size".PHP_EOL;
echo $end - $start . " milliseconds to perform".PHP_EOL;
}
And the shocking results:
Testing Objects
Array Size 1000
2 milliseconds to perform
Array Size 2000
3 milliseconds to perform
Array Size 4000
6 milliseconds to perform
Array Size 8000
12 milliseconds to perform
Array Size 16000
35 milliseconds to perform
Array Size 32000
97 milliseconds to perform
Array Size 64000
246 milliseconds to perform
Array Size 128000
677 milliseconds to perform
Array Size 256000
2271 milliseconds to perform
Array Size 512000
9244 milliseconds to perform
Array Size 1024000
31186 milliseconds to perform
Array Size 2048000
116123 milliseconds to perform
Array Size 4096000
495588 milliseconds to perform
================
Testing Array
Array Size 1000
1 milliseconds to perform
Array Size 2000
2 milliseconds to perform
Array Size 4000
4 milliseconds to perform
Array Size 8000
8 milliseconds to perform
Array Size 16000
28 milliseconds to perform
Array Size 32000
61 milliseconds to perform
Array Size 64000
114 milliseconds to perform
Array Size 128000
245 milliseconds to perform
Array Size 256000
494 milliseconds to perform
Array Size 512000
970 milliseconds to perform
Array Size 1024000
2003 milliseconds to perform
Array Size 2048000
4241 milliseconds to perform
Array Size 4096000
14260 milliseconds to perform
Now besides the obvious overhead of the object calls itself the object array property scales terribly sometimes taking 3 – 4 times longer when the array becomes larger but this is not the case with the standard global array variable.
Any thoughts or answers regarding this problem and is this a possible bug with the PHP engine?
I tested your code on PHP 5.3.9. To do so I had to translate
[]toarray(), and I also had to correct your line #12: from$a=new MyTest($size), to$mytest=new MyTest($size)(BTW, the constructor argument gets silently ignored, funny). I also added this code:I added the memory usage line to your loops at the same point, added an
unset($mytest);after the object case (to get a more consistent memory log), and also replaced your 5000000’s with 1000000’s because I only have 2GB of RAM. This is what I got:As you can see, appending to the array inside a function call costs almost as much as (and has the same non-linear behavior as) doing it inside your original method call. One thing can be said for sure:
It’s the function calls that eat up CPU time!
Regarding the non-linear behavior, it becomes really evident only above a certain threshold. While all three cases have the same memory behavior (because of incomplete gargabe collection this is only evident among the “plain array” and the “array inside function” case, in this log), it is the “array inside method” and the “array inside function” cases that have the same execution time behavior. This means that it’s the function calls themselves that cause a non-linear increase in time. It seems to me that this can be said:
The amount of data that is around during a function call influences its duration.
To verify this I replaced all
$a[]with$a[0]and all 1000000 with 5000000 (to get similar total execution times) and obtained this output:Note how the times are almost perfectly linear now. Of course, the array size is stuck to 1 now. Note also how the differences of the execution times of the three cases are less pronounced than before. Remember that the innermost operation is the same in all cases.
I’m not going to try to fully explain all this (gargabe collection on function call? memory fragmentation? …?), but I think that I have nonetheless collected some useful information, for everyone here and for myself too.