在 PHP (> = 5.0)中,通过引用传递更快吗?

在 PHP 中,函数参数可以通过引用传递,方法是在函数声明中的参数前加一个与号,如下所示:

function foo(&$bar)
{
// ...
}

现在,我知道这是 没有的设计目的是提高性能,但是允许函数更改通常超出其作用域的变量。

相反,PHP 似乎使用“写时复制”来避免复制对象(可能也包括数组) ,直到它们被更改。因此,对于不更改其参数的函数,其效果应该与通过引用传递它们的效果相同。

但是,我想知道在写时复制逻辑是否可能在引用传递时短路,以及这是否会对性能产生影响。

埃塔: 可以肯定的是,我认为它不会更快,而且我很清楚这不是参考资料的用途。所以我认为我自己的猜测是相当不错的,我只是在寻找一个真正知道在引擎盖下发生了什么的人的答案。在 PHP 开发的五年中,我总是发现很难通过阅读源代码获得关于 PHP 内部的高质量信息。

35222 次浏览

我很确定,不,不会更快。 此外,它在手册中特别指出,不要尝试使用引用来提高性能。

编辑: 找不到上面写的地方,但是它就在那儿!

Zend 引擎使用写上复制,当您自己使用引用时,会产生一点额外的开销。但是在写作时只能找到 这个提及,而且 手册中的注释包含其他链接。

(编辑) 对象和引用的手册页包含了一些关于对象变量与引用如何不同的更多信息。

传递对象时不需要添加 & 操作符。在 PHP5 + 中,对象是通过引用传递的。

在调用字符串为20kB 的函数的100000次迭代测试中,结果如下:

函数只读取/使用参数

pass by value:      0.12065005 seconds
pass by reference:  1.52171397 seconds

函数写入/更改参数

pass by value:      1.52223396 seconds
pass by reference:  1.52388787 seconds

结论

  1. 通过值传递参数总是更快

  2. 如果函数更改传递的变量的值,那么出于实际目的,通过引用传递与通过值传递是相同的

我做了一些测试,因为我不确定给出的答案。

我的结果表明,通过引用传递大型数组或字符串的速度要快得多。

以下是我的结果: Benchmark

Y 轴(Runs)是一个函数在1秒内可以调用的次数 * 10

对每个函数/变量重复测试8次

下面是我使用的变量:

$large_array = array_fill(PHP_INT_MAX / 2, 1000, 'a');
$small_array = array('this', 'is', 'a', 'small', 'array');
$large_object = (object)$large_array;
$large_string = str_repeat('a', 100000);
$small_string = 'this is a small string';
$value = PHP_INT_MAX / 2;

这些功能是:

function pass_by_ref(&$var) {
}


function pass_by_val($var) {
}

我曾经试验过将10k 字节的字符串的值和引用传递给两个相同的函数。一个通过值获取参数,第二个通过引用获取参数。它们都是常见的函数——接受参数,执行简单的处理并返回一个值。我调用了10万次,发现引用并不是为了提高性能而设计的——引用的利润接近4-5% ,只有当字符串变得足够大(10万或更长时间,提高了6-7%)时,它才会增长。我的结论是 不要使用引用来提高性能,这个东西不是为了那个。

我使用的是 PHP Version 5.3.1

没有什么比一段测试代码更好的了

<?PHP
$r = array();


for($i=0; $i<500;$i++){
$r[]=5;
}


function a($r){
$r[0]=1;
}
function b(&$r){
$r[0]=1;
}


$start = microtime(true);
for($i=0;$i<9999;$i++){
//a($r);
b($r);
}
$end = microtime(true);


echo $end-$start;
?>

最终结果!数组越大(或调用次数越多) ,差异就越大。因此在这种情况下,通过引用调用会更快,因为该值在函数内部已更改。

否则,“通过引用”和“通过值”之间没有真正的区别,编译器足够聪明,不会在没有必要的情况下每次都创建一个新副本。

我试图用一个基于我正在做的项目的真实世界的例子来做基准测试。和往常一样,这些差异微不足道,但结果却有些出人意料。对于我看到的大多数基准测试,被调用的函数实际上并不更改传入的值。我对它执行了一个简单的 str _ place ()。

**Pass by Value Test Code:**


$originalString=''; // 1000 pseudo-random digits


function replace($string) {
return str_replace('1', 'x',$string);
}
$output = '';
/* set start time */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tstart = $mtime;
set_time_limit(0);


for ($i = 0; $i < 10; $i++ ) {
for ($j = 0; $j < 1000000; $j++) {
$string = $originalString;
$string = replace($string);
}
}


/* report how long it took */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tend = $mtime;
$totalTime = ($tend - $tstart);
$totalTime = sprintf("%2.4f s", $totalTime);
$output .= "\n" . 'Total Time' .
': ' . $totalTime;
$output .= "\n" . $string;
echo $output;

通过参考测试代码

一样,除了

function replace(&$string) {
$string = str_replace('1', 'x',$string);
}
/* ... */
replace($string);

以秒为单位的结果(1000万次迭代) :

PHP 5
Value:     14.1007
Reference: 11.5564


PHP 7
Value:     3.0799
Reference: 2.9489

每个函数调用之间的差异不到一毫秒,但是对于这个用例,在 PHP5和 PHP7中通过引用传递更快。

(注意: PHP 7测试是在更快的机器上执行的—— PHP 7更快,但可能没有那么快。)

很简单,没有必要测试任何东西。 取决于用例。

对于少量参数,通过值传递通常比通过引用传递的速度更快。这取决于体系结构允许通过寄存器(ABI)传递多少变量。

例如,x64允许通过寄存器传递4个64位的值。 Https://en.wikipedia.org/wiki/x86_calling_conventions

这是因为您不必去引用指针,只需直接使用 value。

如果需要传递的数据大于 ABI,则其余值将进入堆栈。 在这种情况下,数组或对象(在实例中是一个类,或者一个结构 + 头)通过引用总是更快。

这是因为引用只是一个指向数据(而不是数据本身)的指针,固定大小,比如32或64位,取决于机器。该指针将适合于一个 CPU 寄存器。

PHP 是用 C/C + + 编写的,所以我希望它的行为也是一样的。