8

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

<?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 大约需要 recursiveCopy 的 20 倍(!)。

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

4

6 回答 6

17

PHP 很可能会为其数组实现时复制,这意味着当您“复制”一个数组时,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);
}
于 2010-10-28T13:46:29.767 回答
3

在 recursiveReference 中,您正在调用 recursiveCopy ... 这没有任何意义,在这种情况下,您只需调用 recursiveReference 一次。更正您的代码,再次运行基准测试并返回您的新结果。

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

于 2010-10-28T13:44:41.707 回答
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

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

于 2011-07-26T16:31:42.727 回答
1

一般来说,在 PHP 中,出于性能原因,您不会使用引用调用。这是您出于功能原因而要做的事情 - 即因为您实际上希望更新引用的变量。

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

(也就是说,正如其他人所指出的那样,您的示例代码并没有完全按照您的想法做;))

于 2010-10-28T13:55:35.267 回答
0
  1. 在 recursiveReference() 函数中,您调用 recursiveCopy() 函数。这是你真正打算做的吗?
  2. 你对 $foo 变量什么都不做 - 可能它应该在进一步的方法调用中使用?
  3. 通过引用传递变量通常应该在传递大对象的情况下节省堆栈内存。
于 2010-10-28T13:47:18.250 回答
0

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

不知道为什么性能会变慢,但这并不能反映您尝试进行的测量。

于 2010-10-28T13:47:46.690 回答