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
for
only (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
", $test, $result);
}
}
$buffer = explode("
", ob_get_clean());
sort($buffer);
echo implode("
", $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