PHP 性能:复制与参考

发布于 2024-09-29 18:04:13 字数 1007 浏览 13 评论 0原文

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

<?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 的这种行为吗?

Hey there. Today I wrote a small benchmark script to compare performance of copying variables vs. creating references to them. I was expecting, that creating references to large arrays for example would be significantly slower than copying the whole array. Here is my benchmark code:

<?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);

The actual result I got was, that recursiveReference took about 20 times (!) as long as recursiveCopy.

Can somebody explain this PHP behaviour?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

独夜无伴 2024-10-06 18:04:13

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

PHP will very likely implement copy-on-write for its arrays, meaning when you "copy" an array, PHP doesn't do all the work of physically copying the memory until you modify one of the copies and your variables can no longer reference the same internal representation.

Your benchmarking is therefore fundamentally flawed, as your recursiveCopy function doesn't actually copy the object; if it did, you would run out of memory very quickly.

Try this: By assigning to an element of the array you force PHP to actually make a copy. You'll find you run out of memory pretty quickly as none of the copies go out of scope (and aren't garbage collected) until the recursive function reaches its maximum depth.

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

    $foo = $array;
    $foo[9492] = 3; // Force PHP to copy the array
    recursiveCopy($array, $count+1);
}
绅士风度i 2024-10-06 18:04:13

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

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

in recursiveReference you're calling recursiveCopy... this doesn't make any sense, in this case you're calling recursiveReference just once. correct your code, rund the benchmark again and come back with your new results.

in addition, i don't think it's useful for a benchmark to do this recursively. a better solution would be to call a function 1000 times in a loop - once with the array directly and one with a reference to that array.

梦行七里 2024-10-06 18:04:13

您不需要(因此不应该)仅仅出于性能原因通过引用分配或传递变量。 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

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

You don't need to (and thus shouldn't) assign or pass variables by reference just for performance reasons. PHP does such optimizations automatically.

The test you ran is flawed because of these automatic optimizations. In ran the following test instead:

<?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";
?>

This was the output:

//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

As you can see there is no significant performance difference in assigning by reference until you actually write to the copy, i.e. when there is also a functional difference.

习惯成性 2024-10-06 18:04:13

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

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

(也就是说,正如其他人指出的那样,您的示例代码无论如何并不完全按照您的想法进行;))

Generally speaking in PHP, calling by reference is not something you'd do for performance reasons; it's something you'd do for functional reasons - ie because you actually want the referenced variable to be updated.

If you don't have a functional reason for calling by reference then you should stick with regular parameter passing, because PHP handles things perfectly efficiently that way.

(that said, as others have pointed out, your example code isn't exactly doing what you think it is anyway ;))

清引 2024-10-06 18:04:13
  1. 在 recursiveReference() 函数中,您调用 recursiveCopy() 函数。这是你真正想要做的吗?
  2. 您对 $foo 变量不执行任何操作 - 也许它应该在进一步的方法调用中使用?
  3. 在传递大对象的情况下,通过引用传递变量通常应该节省堆栈内存。
  1. In recursiveReference() function you call recursiveCopy() function. It it what you really intended to do?
  2. You do nothing with $foo variable - probably it was supposed to be used in further method call?
  3. Passing variable by reference should generally save stack memory in case of passing large objects.
舂唻埖巳落 2024-10-06 18:04:13

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

不确定为什么性能会变慢,但它并不能反映您想要进行的测量。

recursiveReference is calling recursiveCopy.
Not that that would necessarily harm performance, but that's probably not what you're trying to do.

Not sure why performance is slower, but it doesn't reflect the measurement you're trying to make.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文