php foreach,为什么使用数组的引用传递很快?

2021-12-26 00:00:00 arrays foreach php

下面是一个大数组的php foreach循环的测试,我认为如果$v不改变,真正的copy就不会发生,因为copy on write,但为什么按引用传递速度很快?

Below is a test of php foreach loop of a big array, I thought that if the $v don't change, the real copy will not happen because of copy on write, but why it is fast when pass by reference?

代码 1:

function test1($a){
  $c = 0;
  foreach($a as $v){ if($v=='xxxxx') ++$c; }
}

function test2(&$a){
  $c = 0;
  foreach($a as $v){ if($v=='xxxxx') ++$c; }
}

$x = array_fill(0, 100000, 'xxxxx');

$begin = microtime(true);
test1($x);
$end1 = microtime(true);
test2($x);
$end2 = microtime(true);

echo $end1 - $begin . "
";   //0.03320002555847
echo $end2 - $end1;           //0.02147388458252

但这一次,使用传递引用很慢.

But this time, using pass by reference is slow.

代码 2:

function test1($a){
  $cnt = count($a); $c = 0;
  for($i=0; $i<$cnt; ++$i)
    if($a[$i]=='xxxxx') ++$c;
}
function test2(&$a){
  $cnt = count($a); $c = 0;
  for($i=0; $i<$cnt; ++$i)
    if($a[$i]=='xxxxx') ++$c;
}
$x = array_fill(0, 100000, 'xxxxx');

$begin = microtime(true);
test1($x);
$end1 = microtime(true);
test2($x);
$end2 = microtime(true);

echo $end1 - $begin . "
";   //0.024326801300049
echo $end2 - $end1;           //0.037616014480591

谁能解释一下为什么通过引用传递在代码 1 中很快,而在代码 2 中却很慢?

Can someone explain why passing by reference is fast in code1 but slow in code2?

对于代码 2,主要区别在于 count($a),因此循环所用的时间几乎相同.

With Code 2, the count($a) makes the main difference, so the time of the loop took is almost the same.

推荐答案

我认为如果$v 不改变[foreach($a as $v)],真正的复制不会发生,因为写入时复制,但为什么按引用传递时速度很快?

I thought that if the $v don't change [foreach($a as $v)], the real copy will not happen because of copy on write, but why it is fast when pass by reference?

影响的不是$v,而是$a,这个庞大的数组.您可以将它作为值或作为函数的引用传递.在函数内部,它是值 (test1) 或引用 (test2).

The impact is not on $v but on $a, the huge array. You either pass it as value or as reference to the function. Inside the function it's then value (test1) or reference (test2).

您有两个代码(代码 1 和代码 2).

You have two codes (code 1 and code 2).

代码 1: 正在使用 foreach.使用 foreach 你有两个选择:迭代一个值或一个引用(示例).当您迭代一个值时,迭代是在该值的副本上完成的.如果您对引用进行迭代,则不会进行复制.

Code 1: Is using foreach. With foreach you've got two options: iterate over a value or a reference (Example). When you iterate over a value, the iteration is done on a copy of the value. If you iterate over a reference, no copy is done.

当您在 test2 中使用参考时,它会更快.不需要复制这些值.但是在 test1 中,您将数组作为值传递,该数组会被复制.

As you use the reference in test2, it's faster. The values do not need to be copied. But in test1, you pass the array as value, the array gets copied.

代码 2: 正在使用 for.因为在这里实际上什么都不做.在这两种情况下.您访问变量并从数组中读取值.无论是引用还是副本,这几乎都是一样的(感谢 PHP 中的写入时复制优化).

Code 2: Is using for. For does nothing actually here. In both cases. You access the variable and read value from the array. That's pretty much the same regardless if it's a reference or a copy (thanks to the copy on write optimization in PHP).

您现在可能想知道,为什么代码 2 中存在 差异.差异不是因为 for,而是因为 count.如果你传递一个对 count 的引用,PHP 会在内部创建一个它的副本,因为它 count 需要一个副本,而不是一个引用.

You might now wonder, why there is a difference in code 2. The difference is not because of for but because of count. If you pass a reference to count PHP internally creates a copy of it because it count needs a copy, not a reference.

另请阅读:请勿使用PHP 参考作者:Johannes Schlüter

Read as well: Do not use PHP references by Johannes Schlüter

我也编译了一组测试.但我更具体地将代码放入测试函数中.

I've compiled a set of tests as well. But I more specifically put code into the test functions.

  • 空白 - 调用函数有什么区别?
  • 计数 - count 有区别吗?
  • For - foronly (not count) 会发生什么?
  • Foreach - 只是 foreach - 甚至打破第一个元素.
  • Blank - What's the difference in calling the function?
  • Count - Does count make a difference?
  • For - What happens with foronly (not count)?
  • Foreach - Just foreach - even breaking on first element.

每个测试都有两个版本,一个称为_copy(将数组作为副本传递到函数中)和一个称为_ref(将数组作为引用传递).

Every test is in two versions, one called _copy (passing the array as copy into the function) and one called _ref (passing the array as reference).

这些微基准并不总是告诉你真相,但如果你能够隔离特定点,你可以很好地进行有根据的猜测,例如不是for而是count 有影响:

It's not always that these micro-benchmarks tell you the truth, but if you're able to isolate specific points, you can quite well do an educated guess, for example that not for but count had the impact:

function blank_copy($a){
}
function blank_ref(&$a){
}
function foreach_copy($a){
    foreach($a as $v) break;
}
function foreach_ref(&$a){
    foreach($a as $v) break;
}
function count_copy($a){
  $cnt = count($a);
}
function count_ref(&$a){
  $cnt = count($a);
}
function for_copy($a){
    for($i=0;$i<100000;$i++)
        $a[$i];
}
function for_ref(&$a){
    for($i=0;$i<100000;$i++)
        $a[$i];
}

$tests = array('blank_copy', 'blank_ref', 'foreach_copy', 'foreach_ref', 'count_copy', 'count_ref', 'for_copy', 'for_ref');


$x = array_fill(0, 100000, 'xxxxx');
$count = count($x);
$runs = 10;

ob_start();

for($i=0;$i<10;$i++)
{
    shuffle($tests);
    foreach($tests as $test)
    {
        $begin = microtime(true);
        for($r=0;$r<$runs;$r++)
            $test($x);
        $end = microtime(true);
        $result = $end - $begin;
        printf("* %'.-16s: %f
", $test, $result);
    }
}

$buffer = explode("
", ob_get_clean());
sort($buffer);
echo implode("
", $buffer);

输出:

* blank_copy......: 0.000011
* blank_copy......: 0.000011
* blank_copy......: 0.000012
* blank_copy......: 0.000012
* blank_copy......: 0.000012
* blank_copy......: 0.000015
* blank_copy......: 0.000015
* blank_copy......: 0.000015
* blank_copy......: 0.000015
* blank_copy......: 0.000020
* blank_ref.......: 0.000012
* blank_ref.......: 0.000012
* blank_ref.......: 0.000014
* blank_ref.......: 0.000014
* blank_ref.......: 0.000014
* blank_ref.......: 0.000014
* blank_ref.......: 0.000015
* blank_ref.......: 0.000015
* blank_ref.......: 0.000015
* blank_ref.......: 0.000015
* count_copy......: 0.000020
* count_copy......: 0.000022
* count_copy......: 0.000022
* count_copy......: 0.000023
* count_copy......: 0.000024
* count_copy......: 0.000025
* count_copy......: 0.000025
* count_copy......: 0.000025
* count_copy......: 0.000026
* count_copy......: 0.000031
* count_ref.......: 0.113634
* count_ref.......: 0.114165
* count_ref.......: 0.114390
* count_ref.......: 0.114878
* count_ref.......: 0.114923
* count_ref.......: 0.115106
* count_ref.......: 0.116698
* count_ref.......: 0.118077
* count_ref.......: 0.118197
* count_ref.......: 0.123201
* for_copy........: 0.190837
* for_copy........: 0.191883
* for_copy........: 0.193080
* for_copy........: 0.194947
* for_copy........: 0.195045
* for_copy........: 0.195944
* for_copy........: 0.198314
* for_copy........: 0.198878
* for_copy........: 0.200016
* for_copy........: 0.227953
* for_ref.........: 0.191918
* for_ref.........: 0.194227
* for_ref.........: 0.195952
* for_ref.........: 0.196045
* for_ref.........: 0.197392
* for_ref.........: 0.197730
* for_ref.........: 0.201936
* for_ref.........: 0.207102
* for_ref.........: 0.208017
* for_ref.........: 0.217156
* foreach_copy....: 0.111968
* foreach_copy....: 0.113224
* foreach_copy....: 0.113574
* foreach_copy....: 0.113575
* foreach_copy....: 0.113879
* foreach_copy....: 0.113959
* foreach_copy....: 0.114194
* foreach_copy....: 0.114450
* foreach_copy....: 0.114610
* foreach_copy....: 0.118020
* foreach_ref.....: 0.000015
* foreach_ref.....: 0.000016
* foreach_ref.....: 0.000016
* foreach_ref.....: 0.000016
* foreach_ref.....: 0.000018
* foreach_ref.....: 0.000019
* foreach_ref.....: 0.000019
* foreach_ref.....: 0.000019
* foreach_ref.....: 0.000019
* foreach_ref.....: 0.000020

相关文章