PHP 对象数组不是线性缩放而全局数组呢? [英] PHP object array's not linearly scale while global arrays do?

查看:59
本文介绍了PHP 对象数组不是线性缩放而全局数组呢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用对象内数组作为属性而不是使用全局 php 数组变量时存在一个主要的性能问题,为什么?

There is a major performance issue when using in-object array's as a property versus using a global php array variable, why?

为了对这个问题进行基准测试,我创建了以下基准测试,它存储一个越来越大的数组,以 stdClass 作为节点,运行了两个测试,一个使用类中的数组属性,另一个使用全局数组.

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.

测试代码

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

令人震惊的结果:

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

现在,除了对象调用自身的明显开销外,对象数组属性的缩放比例非常大,有时当数组变大时会花费 3-4 倍的时间,但标准全局数组变量并非如此.

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.

关于这个问题的任何想法或答案,这是否是 PHP 引擎的一个可能的错误?

Any thoughts or answers regarding this problem and is this a possible bug with the PHP engine?

推荐答案

我在 PHP 5.3.9 上测试了您的代码.为此,我必须将 [] 转换为 array(),并且还必须更正您的第 12 行: from $a=new MyTest($size),到 $mytest=new MyTest($size)(顺便说一句,构造函数参数被默默忽略,有趣).我还添加了此代码:

I tested your code on PHP 5.3.9. To do so I had to translate [] to array(), 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:

echo "================".PHP_EOL;
echo "Testing Function".PHP_EOL;
for($size = 1000; $size < 1000000; $size *= 2) {
    $start = milliseconds();
    for ($a=array(), $i=0;$i<$size;$i++) {
        my_push($a);
    }
    $end = milliseconds();
    echo "Array Size $size".PHP_EOL;
    echo $end - $start . " milliseconds to perform".PHP_EOL;
    echo "memory usage: ".memory_get_usage()." , real: ".memory_get_usage(true).PHP_EOL;
}

function my_push(&$a)
{
   $a[] = array(new stdClass());
}

我在同一点将内存使用行添加到您的循环中,在对象案例之后添加了一个 unset($mytest);(以获得更一致的内存日志),并且还替换了您的5000000 和 1000000,因为我只有 2GB 的 RAM.这是我得到的:

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:

Testing Objects
Array Size 1000
2 milliseconds to perform
memory usage: 1666376 , real: 1835008
Array Size 2000
5 milliseconds to perform
memory usage: 2063280 , real: 2097152
Array Size 4000
10 milliseconds to perform
memory usage: 2857008 , real: 2883584
Array Size 8000
19 milliseconds to perform
memory usage: 4444456 , real: 4718592
Array Size 16000
44 milliseconds to perform
memory usage: 7619392 , real: 8126464
Array Size 32000
103 milliseconds to perform
memory usage: 13969256 , real: 14417920
Array Size 64000
239 milliseconds to perform
memory usage: 26668936 , real: 27262976
Array Size 128000
588 milliseconds to perform
memory usage: 52068368 , real: 52690944
Array Size 256000
1714 milliseconds to perform
memory usage: 102867104 , real: 103546880
Array Size 512000
5452 milliseconds to perform
memory usage: 204464624 , real: 205258752
================
Testing Array
Array Size 1000
1 milliseconds to perform
memory usage: 18410640 , real: 20709376
Array Size 2000
4 milliseconds to perform
memory usage: 18774760 , real: 20709376
Array Size 4000
7 milliseconds to perform
memory usage: 19502976 , real: 20709376
Array Size 8000
13 milliseconds to perform
memory usage: 20959360 , real: 21233664
Array Size 16000
29 milliseconds to perform
memory usage: 23872176 , real: 24379392
Array Size 32000
61 milliseconds to perform
memory usage: 29697720 , real: 30146560
Array Size 64000
124 milliseconds to perform
memory usage: 41348856 , real: 41943040
Array Size 128000
280 milliseconds to perform
memory usage: 64651088 , real: 65273856
Array Size 256000
534 milliseconds to perform
memory usage: 111255536 , real: 111935488
Array Size 512000
1085 milliseconds to perform
memory usage: 204464464 , real: 205258752
================
Testing Function
Array Size 1000
357 milliseconds to perform
memory usage: 18410696 , real: 22544384
Array Size 2000
4 milliseconds to perform
memory usage: 18774768 , real: 22544384
Array Size 4000
9 milliseconds to perform
memory usage: 19503008 , real: 22544384
Array Size 8000
17 milliseconds to perform
memory usage: 20959392 , real: 22544384
Array Size 16000
36 milliseconds to perform
memory usage: 23872208 , real: 24379392
Array Size 32000
89 milliseconds to perform
memory usage: 29697720 , real: 30146560
Array Size 64000
224 milliseconds to perform
memory usage: 41348888 , real: 41943040
Array Size 128000
529 milliseconds to perform
memory usage: 64651088 , real: 65273856
Array Size 256000
1587 milliseconds to perform
memory usage: 111255616 , real: 111935488
Array Size 512000
5244 milliseconds to perform
memory usage: 204464512 , real: 205258752

如您所见,在函数调用中附加到数组的成本几乎与在原始方法调用中执行此操作的成本一样高(并且具有相同的非线性行为).可以肯定地说一件事:

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:

消耗CPU时间的是函数调用!

关于非线性行为,只有在某个阈值以上才会变得非常明显.虽然所有三种情况都具有相同的内存行为(由于不完整的 gargabe 收集,这仅在此日志中的普通数组"和函数内部数组"情况中很明显),但它是数组内部方法"和函数内部的数组"具有相同执行时间行为的情况.这意味着函数调用本身导致了时间的非线性增加.在我看来可以这样说:

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:

函数调用期间的数据量会影响其持续时间.

为了验证这一点,我将所有 $a[] 替换为 $a[0] 并将所有 1000000 替换为 5000000(以获得类似的总执行时间)并获得此输出:

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:

Testing Objects
Array Size 1000
2 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 2000
4 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 4000
8 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 8000
15 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 16000
31 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 32000
62 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 64000
123 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 128000
246 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 256000
493 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 512000
985 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 1024000
1978 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 2048000
3965 milliseconds to perform
memory usage: 1302672 , real: 1572864
Array Size 4096000
7905 milliseconds to perform
memory usage: 1302672 , real: 1572864
================
Testing Array
Array Size 1000
1 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 2000
3 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 4000
5 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 8000
10 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 16000
20 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 32000
40 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 64000
80 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 128000
161 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 256000
322 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 512000
646 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 1024000
1285 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 2048000
2574 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 4096000
5142 milliseconds to perform
memory usage: 1302464 , real: 1572864
================
Testing Function
Array Size 1000
1 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 2000
4 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 4000
6 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 8000
14 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 16000
26 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 32000
53 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 64000
105 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 128000
212 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 256000
422 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 512000
844 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 1024000
1688 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 2048000
3377 milliseconds to perform
memory usage: 1302464 , real: 1572864
Array Size 4096000
6814 milliseconds to perform
memory usage: 1302464 , real: 1572864

注意现在时间几乎是完美的线性.当然,数组大小现在固定为 1.另请注意,这三种情况的执行时间差异如何不如以前那么明显.请记住,最里面的操作在所有情况下都是相同的.

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.

这篇关于PHP 对象数组不是线性缩放而全局数组呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆