PHP对象数组在全局数组中不是线性扩展的吗?

时间:2012-05-23 14:04:35

标签: php scalability

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

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

测试代码

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倍,但标准全局数组变量的情况并非如此。

有关此问题的任何想法或答案,这可能是PHP引擎的错误吗?

2 个答案:

答案 0 :(得分:6)

我在PHP 5.3.9上测试了你的代码。为此,我必须将[]翻译为array(),我还必须纠正第12行:从$a=new MyTest($size)$mytest=new MyTest($size)(BTW,构造函数参数获取)默默地忽略,好笑)。我还添加了这段代码:

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替换了你的5000000,因为我只有2GB RAM。这就是我得到的:

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

正如您所看到的,在函数调用中附加到数组的成本几乎与原始方法调用中的数据一样多(并且具有相同的非线性行为)。有一点可以肯定:

这是占用CPU时间的函数调用!

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

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

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

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。还要注意三种情况的执行时间差异如何不如以前那么明显。请记住,在所有情况下,最里面的操作都是相同的。

我不会试图完全解释所有这些(函数调用中的gargabe集合?内存碎片?......?),但我认为我已经为这里和我自己收集了一些有用的信息。

答案 1 :(得分:2)

我不能在评论中发布这一切,所以这更像是一个观察而非答案。看起来SplObjectStorage相当慢。此外,array_push比$ array [] ='item';

快得多

免责声明:为草率代码道歉:)

<?php

$time = microtime();
$time = explode(' ', $time);
$time = $time[1] + $time[0];
$start = $time;

$iteration = 10000;

switch ($_REQUEST['test'])
{
    case 1:
        $s = new SplObjectStorage();

        for ($i = 0; $i < $iteration; $i++) {
            $obj = new stdClass;
            $s[$obj] = 'test';
        }
        break;
    case 2:

        $s = array();
        for ($i = 0; $i < $iteration; $i++) {
            $obj = new stdClass;
            $s[$i] = $obj;
        }
        break;

    case 3:
        class Test {
            public $data = array();
        }
        $s = new Test;
        for ($i = 0; $i < $iteration; $i++) {
            $obj = new stdClass;
            $s->data[] = $obj;
        }
        break;

    case 4:
        class Test {
            public static $data = array();
        }
        $s = new Test;
        for ($i = 0; $i < $iteration; $i++) {
            $obj = new stdClass;
            $s->data[] = $obj;
        }
        break;  
    case 5:
        class Test {
            public $data = array();
        }
        $s = new Test;
        for ($i = 0; $i < $iteration; $i++) {
            $obj = new stdClass;
            array_push($s->data, $obj);
        }
        break;  
    default:
        echo 'Type in ?test=#';
}

$time = microtime();
$time = explode(' ', $time);
$time = $time[1] + $time[0];
$finish = $time;
$total_time = round(($finish - $start), 6);
echo 'Page generated in '.$total_time.' seconds.';