PHP性能:复制与参考

时间:2010-10-28 13:37:32

标签: php reference benchmarking php-internals

嘿那里。今天我编写了一个小的基准脚本来比较复制变量的性能与创建对它们的引用。我期待,例如,创建对大型数组的引用将比复制整个数组慢得多。这是我的基准代码:

<?php
    $array = array();

    for($i=0; $i<100000; $i++) {
        $array[] = mt_rand();
    }

    function recursiveCopy($array, $count) {
        if($count === 1000)
            return;

        $foo = $array;
        recursiveCopy($array, $count+1);
    }

    function recursiveReference($array, $count) {
        if($count === 1000)
            return;

        $foo = &$array;
        recursiveReference($array, $count+1);
    }

    $time = microtime(1);
    recursiveCopy($array, 0);
    $copyTime = (microtime(1) - $time);
    echo "Took " . $copyTime . "s \n";


    $time = microtime(1);
    recursiveReference($array, 0);
    $referenceTime = (microtime(1) - $time);
    echo "Took " . $referenceTime . "s \n";

    echo "Reference / Copy: " . ($referenceTime / $copyTime);

我得到的实际结果是,recursiveReference只需20次(!),只要recursiveCopy。

有人可以解释一下这种PHP行为吗?

6 个答案:

答案 0 :(得分:17)

PHP很可能会为其数组实现copy-on-write,这意味着当您“复制”数组时,PHP不会完成物理复制内存的所有工作,直到您修改其中一个副本并且您的变量可以不再引用相同的内部表示。

因此,您的基准测试存在根本缺陷,因为您的recursiveCopy函数实际上并未复制对象;如果确实如此,你会很快耗尽内存。

试试这个:通过分配数组元素,你可以强制PHP 实际制作副本。你会发现你的内存耗尽很快,因为没有任何副本超出范围(并且不会被垃圾回收),直到递归函数达到其最大深度。

function recursiveCopy($array, $count) {
    if($count === 1000)
        return;

    $foo = $array;
    $foo[9492] = 3; // Force PHP to copy the array
    recursiveCopy($array, $count+1);
}

答案 1 :(得分:3)

在recursiveReference中你正在调用recursiveCopy ...这没有任何意义,在这种情况下你只需要调用recursiveReference一次。纠正你的代码,再次使用基准并返回你的新结果。

此外,我不认为基准测试以递归方式执行此操作是有用的。一个更好的解决方案是在一个循环中调用一个函数1000次 - 一次是直接使用数组,另一次是对该数组的引用。

答案 2 :(得分:3)

出于性能原因,您不需要(因此不应该)通过引用分配或传递变量。 PHP会自动执行此类优化。

由于这些自动优化,您运行的测试存在缺陷。在运行以下测试:

<?php
for($i=0; $i<100000; $i++) {
    $array[] = mt_rand();
}

$time = microtime(1);
for($i=0; $i<1000; $i++) {
    $copy = $array;
    unset($copy);
}
$duration = microtime(1) - $time;
echo "Normal Assignment and don't write: $duration<br />\n";

$time = microtime(1);
for($i=0; $i<1000; $i++) {
    $copy =& $array;
    unset($copy);
}
$duration = microtime(1) - $time;
echo "Assignment by Reference and don't write: $duration<br />\n";

$time = microtime(1);
for($i=0; $i<1000; $i++) {
    $copy = $array;
    $copy[0] = 0;
    unset($copy);
}
$duration = microtime(1) - $time;
echo "Normal Assignment and write: $duration<br />\n";

$time = microtime(1);
for($i=0; $i<1000; $i++) {
    $copy =& $array;
    $copy[0] = 0;
    unset($copy);
}
$duration = microtime(1) - $time;
echo "Assignment by Reference and write: $duration<br />\n";
?>

这是输出:

//Normal Assignment without write: 0.00023698806762695
//Assignment by Reference without write: 0.00023508071899414
//Normal Assignment with write: 21.302103042603
//Assignment by Reference with write: 0.00030708312988281

正如您所看到的那样,在您实际写入副本之前,通过引用进行分配没有明显的性能差异,即当存在功能差异时。

答案 3 :(得分:1)

一般来说,在PHP中,出于性能原因,您不能通过引用进行调用;这是你出于功能原因所做的事情 - 也就是因为你真的希望更新引用的变量。

如果您没有通过引用调用的功能原因,那么您应该坚持使用常规参数传递,因为PHP以这种方式完美地处理事物。

(正如其他人所指出的那样,你的示例代码并不完全符合你的想法;)

答案 4 :(得分:0)

  1. 在recursiveReference()函数中,您调用recursiveCopy()函数。这是你真正打算做的吗?
  2. 你对$ foo变量什么都不做 - 可能它应该用于进一步的方法调用?
  3. 通过引用传递变量通常应该在传递大对象时保存堆栈内存。

答案 5 :(得分:0)

recursiveReference正在调用recursiveCopy。 这并不一定会损害性能,但这可能不是你想要做的。

不确定为什么性能较慢,但它并不反映您尝试进行的测量。