PHP的foreach,为何使用通由数组引用是快? [英] php foreach, why using pass by reference of a array is fast?

查看:105
本文介绍了PHP的foreach,为何使用通由数组引用是快?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是一个大数组的PHP foreach循环的考验,我认为如果 $ V 不改变,真正的副本,不会因为发生复制上写,但为什么它的速度很快,当通过引用传递?

code 1:

 功能测试1($ A){
  $ C = 0;
  的foreach($ A $为V){如果($ V =='XXXXX')+ $ C; }
}功能测试2(安培; $ A){
  $ C = 0;
  的foreach($ A $为V){如果($ V =='XXXXX')+ $ C; }
}$ X = array_fill(0,100000,'XXXXX');$开始= microtime中(真);
TEST1($ X);
$ END1 = microtime中(真);
TEST2($ X);
$ END2 = microtime中(真);回声$ END1 - $开头。 \\ n; //0.03320002555847
回声$ END2 - $ END1; //0.02147388458252

但是,这一次,使用通过引用传递是缓慢的。

code 2:

 功能测试1($ A){
  $ CNT =计数($ A); $ C = 0;
  为($ I = 0; $ I< $ CNT ++ $ I)
    如果($ A [$ i] =='XXXXX')+ $ C;
}
功能测试2(安培; $ A){
  $ CNT =计数($ A); $ C = 0;
  为($ I = 0; $ I< $ CNT ++ $ I)
    如果($ A [$ i] =='XXXXX')+ $ C;
}
$ X = array_fill(0,100000,'XXXXX');$开始= microtime中(真);
TEST1($ X);
$ END1 = microtime中(真);
TEST2($ X);
$ END2 = microtime中(真);回声$ END1 - $开头。 \\ n; //0.024326801300049
回声$ END2 - $ END1; //0.037616014480591

有人能解释为什么引用传递在code1快,但在code2慢?

编辑:
随着code 2,计数($ A)使得主要区别,所以循环的时候了几乎是相同的。


解决方案

  

我认为,如果 $ V 不改[的foreach($ A $为V)] ,真正的副本将不会发生,因为复制上写,但为什么它的速度很快,当通过引用传递?


的影响是不是在 $ V $ A ,庞大的阵列。你要么把它作为价值或参考作用。在函数内部它是那么值(TEST1)或引用(TEST2)。

您有两个codeS(code 1和code 2)。

code 1:中使用的foreach 。随着的foreach 你有两种选择:遍历一个值或引用()。当你迭代值,迭代上的复制的价值的实现。如果你遍历一个参考,没有复制就完成了。

正如你在test2的使用参考,它的速度更快。值不需要被复制。但是,在为test1,你通过数组值,数组被复制。

code 2:中使用。对于不执行任何操作实际上是在这里。在这两种情况下。访问的变量和从阵列读出的值。这是pretty大致相同,无论它是否是一个引用或复印件(感谢的复制上写的PHP中优化)。

您现在可能会问,为什么有的在code 2.区别不同的是,不是因为但由于计数。如果你传递给引用计数 PHP内部创建一个副本,因为它计数需要一个副本,而不是引用

阅读以及:不要使用PHP引用约翰内斯SCHLÜTER


我编了一套测试以及。但我更具体把code到测试功能。


  • 空白 - 什么是在调用函数的区别

  • 计数 - ?是否计数有所作为

  • 有关吗 - 与会发生什么事了只(不计数

  • Foreach源 - 只需的foreach - 甚至打破第一个元素

每个测试有两个版本,一个叫 _copy (传递数组复制到功能)和一个名为 _ref (传递数组作为参考)。

这并不总是这些微基准测试实话告诉你,但如果你能够分离出特定的点,可以很好地做一个受过教育的猜测,例如,并非,但计数产生的影响:

 函数blank_copy($ A){
}
功能blank_ref(安培; $ A){
}
功能foreach_copy($ A){
    的foreach($ A $为v)的突破;
}
功能foreach_ref(安培; $ A){
    的foreach($ A $为v)的突破;
}
功能count_copy($ A){
  $ CNT =计数($ A);
}
功能count_ref(安培; $ A){
  $ CNT =计数($ A);
}
功能for_copy($ A){
    为($ I = 0; $ I< 100000; $ I ++)
        美元[$ i];
}
功能for_ref(安培; $ A){
    为($ I = 0; $ I< 100000; $ I ++)
        美元[$ i];
}$测试=阵列('blank_copy','blank_ref','foreach_copy','foreach_ref','count_copy','count_ref','for_copy','for_ref');
$ X = array_fill(0,100000,'XXXXX');
$数= COUNT($ X);
$奔跑= 10;ob_start();为($ I = 0; $ I小于10; $ I ++)
{
    洗牌($测试);
    的foreach($测试,测试$)
    {
        $开始= microtime中(真);
        为($ R = 0; $为r $运行; $ R ++)
            $试验($ X);
        $结束= microtime中(真);
        $结果= $结束 - $开头;
        的printf(*%'.- 16S:%F \\ n,$测试,$结果);
    }
}$缓冲=爆炸(\\ n,ob_get_clean());
排序($缓存);
回声破灭(\\ n,$缓存);

输出:

  * 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

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?

Code 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 . "\n";   //0.03320002555847
echo $end2 - $end1;           //0.02147388458252

But this time, using pass by reference is slow.

Code 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 . "\n";   //0.024326801300049
echo $end2 - $end1;           //0.037616014480591

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

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

解决方案

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?

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).

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

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.

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.

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).

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.

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.

  • 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.

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).

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\n", $test, $result);
    }
}

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

Output:

* 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

这篇关于PHP的foreach,为何使用通由数组引用是快?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆