Php 为什么对引用值调用函数(如strlen、count等)如此缓慢?

Php 为什么对引用值调用函数(如strlen、count等)如此缓慢?,php,performance,reference,pass-by-reference,Php,Performance,Reference,Pass By Reference,我刚刚在PHP中发现了一些非常奇怪的东西 如果我通过引用将一个变量传递给一个函数,然后对它调用一个函数,它的速度会非常慢 如果在内部函数调用上循环,并且变量很大,那么它可能比按值传递变量慢很多数量级 例如: <?php function TestCount(&$aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); for ($iIter = 0; $iIter < 1

我刚刚在PHP中发现了一些非常奇怪的东西

如果我通过引用将一个变量传递给一个函数,然后对它调用一个函数,它的速度会非常慢

如果在内部函数调用上循环,并且变量很大,那么它可能比按值传递变量慢很多数量级

例如:

<?php
function TestCount(&$aArray)
{
    $aArray = range(0, 100000);
    $fStartTime = microtime(true);

    for ($iIter = 0; $iIter < 1000; $iIter++)
    {
        $iCount = count($aArray);
    }

    $fTaken = microtime(true) - $fStartTime;

    print "took $fTaken seconds\n";
}

$aArray = array();
TestCount($aArray);
?>

在我的机器上(在PHP5.3上)运行这个过程通常需要20秒

但是如果我将函数更改为按值传递(即
函数TestCount($aArray)
而不是
函数TestCount(&$aArray)
),那么它的运行速度大约为2ms-几乎快了10000倍

对于其他内置函数(如
strlen
)和用户定义函数也是如此


发生了什么事?

我发现了一份2005年的bug报告,它准确地描述了这个问题:

所以问题似乎是,当将引用值传递给不接受引用的函数时,PHP需要复制它

这可以通过以下测试代码进行演示:

<?php
function CalledFunc(&$aData)
{
    // Do nothing
}

function TestFunc(&$aArray)
{
    $aArray = range(0, 100000);
    $fStartTime = microtime(true);

    for ($iIter = 0; $iIter < 1000; $iIter++)
    {
        CalledFunc($aArray);
    }

    $fTaken = microtime(true) - $fStartTime;

    print "took $fTaken seconds\n";
}

$aArray = array();
TestFunc($sData);
?>

这运行得很快,但是如果您将
函数CalledFunc(&$aData)
更改为
函数CalledFunc($aData)
,您将看到与
计数
示例类似的减速

这是相当令人担忧的,因为我已经编写了相当长一段时间的PHP代码,我不知道这个问题


幸运的是,有一个简单的解决方法适用于许多情况——在循环中使用临时局部变量,并在最后复制到参考变量。

因此,考虑到您已经给出的答案,您可以通过在迭代工作之前强制复制(如果数据发生更改,则在之后复制)部分避免此问题


不太好,但好得多。

速度慢了10000倍,因为您在基准测试中进行迭代。这不会为
count()
提供正确的度量值。使用探查器,您将看到它的速度大约只慢3倍。有关解释,请参见@Gordon-yes,true,但我们发现这一点的原因是,我们有一些行为与示例非常相似的生产代码(当然要更改变量)。它不像是一个特别深奥的用例,不是说它深奥。只是说数字被大大夸大了。@Gordon-我对问题进行了一些编辑,以提及内部函数的循环。是的,但我认为行为(PHP克隆数组)是正确和合理的,因为我们不希望接受数组作为值的函数在未克隆的情况下修改原始数组。没有它我就活不下去。也许我们作为程序员所能做的就是注意这个场景并避免它。它确实完全被破坏了。5分钟前刚碰到那个虫子。嗯,我也会去看看斯特伦——是的,用一个当地的临时帐篷来避开柏油坑。
<?php
function TestCountNon($aArray)
{
    $aArray = range(0, 100000);
    $fStartTime = microtime(true);
    for ($iIter = 0; $iIter < 1000; $iIter++)
    {
        $iCount = count($aArray);
    }
    $fTaken = microtime(true) - $fStartTime;

    print "Non took $fTaken seconds\n<br>";
}

function TestCount(&$aArray)
{
    $aArray = range(0, 100000);
    $fStartTime = microtime(true);
    for ($iIter = 0; $iIter < 1000; $iIter++)
    {
        $iCount = count($aArray);
    }
    $fTaken = microtime(true) - $fStartTime;

    print "took $fTaken seconds\n<br>";
}

function TestCountA(&$aArray)
{
    $aArray = range(0, 100000);
    $fStartTime = microtime(true);
    $bArray = $aArray;
    for ($iIter = 0; $iIter < 1000; $iIter++)
    {
        $iCount = count($bArray);
    }
    $aArray = $bArray;
    $fTaken = microtime(true) - $fStartTime;

    print "A took $fTaken seconds\n<br>";
}

$nonArray = array();
TestCountNon($nonArray);

$aArray = array();
TestCount($aArray);

$bArray = array();
TestCountA($bArray);
?>
Non took 0.00090217590332031 seconds 
took 17.676940917969 seconds 
A took 0.04144287109375 seconds