为什么在引用值上调用函数(例如strlen,count等)这么慢?

时间:2010-06-25 11:30:53

标签: php performance reference pass-by-reference

我刚刚在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);
?>

这在我的机器上运行大约需要20秒(在PHP 5.3上)。

但是,如果我将函数更改为按值传递(即function TestCount($aArray)而不是function TestCount(&$aArray)),则它会在大约2毫秒内运行 - 真正快10,000倍

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

发生了什么事?

2 个答案:

答案 0 :(得分:13)

我发现2005年的错误报告正好描述了这个问题:http://bugs.php.net/bug.php?id=34540

所以问题似乎是当将引用的值传递给不接受引用的函数时,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);
?>

此操作很快就会运行,但如果您将function CalledFunc(&$aData)更改为function CalledFunc($aData),您会看到与count示例相似的减速。

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

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

答案 1 :(得分:1)

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

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

不太好,但是好多了。