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.
count
make a difference?for
only (not count
)?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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With