I'm known for telling "Don't use references" (also as video) as those cause different problems (i.e. with foreach) and hurt performance. The reason for the performance loss is that references disable copy-on-write while most places in PHP assume copy-on-write. Meanwhile we have PHP 7. In PHP 7 the internal variable handling changed a lot among other things the reference counting moved from the zval, the container representing a variable, to the actual element. So I decided to run a little test to verify my performance assumption was still valid.
In my test code I'm calling a function which calls strlen (one of the cheapest functions in PHP - PHP strings carry their length, so it simply returns that property) from a loop. Once the function takes a parameter by reference, once per value. Here's the code:
<?php
function takeRef(&$string) {
strlen($string);
}
function takeVal($string) {
strlen($string);
}
function loopRef() {
$start = microtime(true);
for ($i = 0; $i < 50000000; ++$i) {
$s = "hello world";
takeRef($s);
}
return microtime(true) - $start;
}
function loopVal() {
$start = microtime(true);
for ($i = 0; $i < 50000000; ++$i) {
$s = "hello world";
takeVal($s);
}
return microtime(true) - $start;
}
$ref = $val = PHP_INT_MAX;
for ($i = 0; $i < 10; ++$i) {
$ref = min($ref, loopRef());
$val = min($val, loopVal());
}
echo "Ref: $ref\nVal: $val\n";
?>
If I run this in PHP 5, in order to have a baseline, I get this result:
Ref: 10.679290056229
Val: 9.5635061264038
So using a reference costs 10%.
Now let's try PHP 7:
Ref: 10.631688117981
Val: 9.0047070980072
Overall we saw a small performance improvement, like we expect with PHP 7 but still using a reference costs 10% throughput. So I still stand with my mantra: Don't use references in PHP!
If you wonder about the second loop in the bottom and the min() call: The code takes multiple samples and then takes the measurement with the least noise as my system isn't 100% idle and there might be unrelated events I don't want to measure so the fastest run is closest to raw system performance.
Friday, February 19. 2016 at 18:56 (Link) (Reply)
I definitely would in the case of your example, because the reference actually doesn't do anything, but there are legit cases to use references, that can't be resolved by simply 'not using references' such as your example.
Friday, February 19. 2016 at 20:14 (Link) (Reply)
Monday, February 22. 2016 at 22:39 (Link) (Reply)
Saturday, February 20. 2016 at 13:44 (Reply)
Very good content never the less, looking forward to others like this
Saturday, February 20. 2016 at 15:00 (Reply)
The exception I hardly see (I mentioned one case in a comment above) however regularly I see code copied from other languages or by developers coming from other background trying to "tune" their code without understanding PHP.
Monday, February 22. 2016 at 19:18 (Link) (Reply)
At times when you dont use objects, you use primitives like numbers and strings, which are supposed to be immutable anyway and shouldnt change over a method. ie when you pass a string to a method, you dont use ampersand, since the string is meant to be immutable. If you modify a string parameter in the entire program rather than just within the method itself, then you aint following good programming practices.
If you do find yourself using references(with ampersand) a lot, it means that your program is poorly designed. Just like reflection is slow, but people dont complain about it, because if you use reflection to an extent that it slows down your program noticeably, you are using it wrong in the first place.
Tuesday, February 23. 2016 at 10:30 (Reply)
Why would you make a function with a reference? To change the value - or a part of it - of the referenced variable. Unless otherwise a reference makes no sense at all.
So, to compare a call-by-reference in a function to a call-by-write, you had to return the changed value.
I have done it ... and Ref wins by a very little %, even on PHP 5.4.4.
Also, I have out the assigmnet outside the loop as that has nothing to with what you want to measure, but is done every time the loop runs!
Here's the code I used:
$runs=500000;
function takeRef(&$string) {
$string['len'] = strlen($string['tx']);
}
function takeVal($string) {
return strlen($string['tx']);
}
function loopRef() {
$runs = $GLOBALS['runs'];
$start = microtime(true);
$s['tx'] = "hello world";
for ($i = 0; $i < $runs; ++$i) {
takeRef($s);
}
return microtime(true) - $start;
}
function loopVal() {
$runs = $GLOBALS['runs'];
$start = microtime(true);
$s['tx'] = "hello world";
for ($i = 0; $i < $runs; ++$i) {
$s['len'] = takeVal($s);
}
return microtime(true) - $start;
}
$ref = $val = PHP_INT_MAX;
for ($i = 0; $i < 10; ++$i) {
$ref = min($ref, loopRef());
$val = min($val, loopVal());
}
echo "Ref: $ref\nVal: $val\n";
Tuesday, February 23. 2016 at 14:08 (Reply)