【问题标题】:PHP Performance : Copy vs. ReferencePHP 性能:复制与参考
【发布时间】:2011-05-01 21:38:00
【问题描述】:

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

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

【问题讨论】:

  • 除了不正确的递归之外,为什么还要麻烦递归呢?为什么不只是设置一个 for 循环并在每次迭代后取消设置变量(这将比递归调用具有更少的开销,并且不会占用你所有的内存)......但最终,差异会如此之小以至于在 99.9999% 的情况下,使用语义上适当的分配(在需要的地方引用,在不需要的地方正常)比尝试微优化更有意义。
  • 这不是为了优化东西,我只是好奇。而且我使用了递归,因为我不想取消设置变量和循环等...编写递归要快得多,而且我不关心开销,因为两者都是一样的。跨度>
  • 避免使用大型数组以获得良好的性能。就是这样。

标签: php reference benchmarking php-internals


【解决方案1】:

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

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

【讨论】:

  • 对不起,我真的不知道它是怎么到那里的,那是复制和粘贴 wtf... 在我运行的版本中,它不存在。
【解决方案2】:

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

【讨论】:

    【解决方案3】:
    1. 在 recursiveReference() 函数中调用 recursiveCopy() 函数。这是你真正打算做的吗?
    2. 您对 $foo 变量什么都不做 - 可能它应该在进一步的方法调用中使用?
    3. 通过引用传递变量通常应该在传递大对象的情况下节省堆栈内存。

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

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

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

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

        【讨论】:

        • 这是试图衡量通过引用分配与按值分配。调用任何东西都不会真正进入图片(除了测试是递归的)。否则+1
        【解决方案6】:

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

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

        【讨论】:

          猜你喜欢
          • 2016-06-25
          • 2023-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多