75

In PHP, function parameters can be passed by reference by prepending an ampersand to the parameter in the function declaration, like so:

function foo(&$bar)
{
    // ...
}

Now, I am aware that this is not designed to improve performance, but to allow functions to change variables that are normally out of their scope.

Instead, PHP seems to use Copy On Write to avoid copying objects (and maybe also arrays) until they are changed. So, for functions that do not change their parameters, the effect should be the same as if you had passed them by reference.

However, I was wondering if the Copy On Write logic maybe is shortcircuited on pass-by-reference and whether that has any performance impact.

ETA: To be sure, I assume that it's not faster, and I am well aware that this is not what references are for. So I think my own guesses are quite good, I'm just looking for an answer from someone who really knows what's definitely happening under the hood. In five years of PHP development, I've always found it hard to get quality information on PHP internals short from reading the source.

molf
  • 73,644
  • 13
  • 135
  • 118
Hanno Fietz
  • 30,799
  • 47
  • 148
  • 234
  • 1
    See my question for an example where references can slow things down dramatically: http://stackoverflow.com/questions/3117604/why-is-calling-a-function-such-as-strlen-count-etc-on-a-referenced-value-so-sl – John Carter Jun 30 '10 at 10:24
  • What about passing a large array by reference vs. by value? Same result as passing a large string? – David Spector Jan 20 '23 at 01:52

9 Answers9

92

In a test with 100 000 iterations of calling a function with a string of 20 kB, the results are:

Function that just reads / uses the parameter

pass by value:      0.12065005 seconds
pass by reference:  1.52171397 seconds

Function to write / change the parameter

pass by value:      1.52223396 seconds
pass by reference:  1.52388787 seconds

Conclusions

  1. Pass the parameter by value is always faster

  2. If the function change the value of the variable passed, for practical purposes is the same as pass by reference than by value

Svish
  • 152,914
  • 173
  • 462
  • 620
ikary
  • 1,093
  • 1
  • 8
  • 5
38

The Zend Engine uses copy-on-write, and when you use a reference yourself, it incurs a little extra overhead. Can only find this mention at time of writing though, and comments in the manual contain other links.

(EDIT) The manual page on Objects and references contains a little more info on how object variables differ from references.

hakre
  • 193,403
  • 52
  • 435
  • 836
Paul Dixon
  • 295,876
  • 54
  • 310
  • 348
  • 4
    So you're saying that it actually hurts performance (even though maybe the impact is very insignificant)? That's interesting, thanks! – Hanno Fietz Oct 07 '08 at 13:25
  • 7
    The (somewhat academic) overall performance loss should occur only if you never manipulate the original data structure. When you plan to, you should actually *gain* performance instead because you avoid the copy-on-write. – Tomalak Oct 07 '08 at 14:05
  • 3
    Yeah, granted, this is a little academic. It's just that I've had a lot of frustration from misunderstanding how PHP internally works and that made me a bit pedantic about finding out. It seems to me that good sources on PHP internals are harder to find than with other languages, e.g. Python – Hanno Fietz Oct 07 '08 at 19:06
31

I ran some test on this because I was unsure of the answers given.

My results show that passing large arrays or strings by reference IS significantly faster.

Here are my results: Benchmark

The Y axis (Runs) is how many times a function could be called in 1 second * 10

The test was repeated 8 times for each function/variable

And here is the variables I used:

$large_array = array_fill(PHP_INT_MAX / 2, 1000, 'a');
$small_array = array('this', 'is', 'a', 'small', 'array');
$large_object = (object)$large_array;
$large_string = str_repeat('a', 100000);
$small_string = 'this is a small string';
$value = PHP_INT_MAX / 2;

These are the functions:

function pass_by_ref(&$var) {
}

function pass_by_val($var) {
}
Petah
  • 45,477
  • 28
  • 157
  • 213
  • 1
    This test does not reflect a real use case, though. People often pass by reference when they can't return multiple values, e.g. passing in a reference to an errors array. A better test would be something like this: `function pass_by_ref($val, &$errors) { if($val < 0) { $errors []= "val < 0"; return false; } else return true; }` ... and ... `function pass_by_val($val, $errors) { if($val < 0) { $errors []= "val < 0"; return array("errors" => $errors, "result" => false); } else return array("errors" => $errors, "result" => true);}`. – Chris Middleton Sep 11 '14 at 18:57
  • would have been nice to also have variations where inside the array is changed and either the changes returned and taken again, returned by reference and taken again or not returned but due to the referenced parameter taken again. just saying. – hakre Dec 25 '14 at 12:30
  • Neither of the functions changes the data in the array. – David Spector Feb 24 '21 at 13:38
  • I think there are two important pieces of performance to "go fast". CPU + Memory. It is _ALWAYS_ a tradeoff between the two. If you are handling large data sets, when you pass them by value they are used multiple times across the call stack rather than referencing the existing data. IMHO for a rule of thumb -- large data with specific scope? pass by reference. Everything else? Pass by value for immutability when possible. – domdambrogia Oct 12 '22 at 21:19
7

I have experimented with values and references of 10k bytes string passing it to two identical function. One takes argument by value and the second one by reference. They were common functions - take argument, do simple processing and return a value. I did 100 000 calls of both and figured out that references are not designed to increase performance - profit of reference was near 4-5% and it grows only when string becomes large enough (100k and longer, that gave 6-7% improvement). So, my conclusion is do not use references to increase perfomance, this stuff is not for that.

I used PHP Version 5.3.1

Vladimir Fesko
  • 222
  • 2
  • 3
3

I'm pretty sure that no, it's not faster. Additionally, it says specifically in the manual not to try using references to increase performance.

Edit: Can't find where it says that, but it's there!

Greg
  • 316,276
  • 54
  • 369
  • 333
  • 11
    It says not to *return* by reference to increase performance. It doesn't say to not *pass* by reference to increase performance. http://www.php.net/manual/en/language.references.return.php – Drew LeSueur Oct 17 '13 at 13:48
2

I tried to benchmark this with a real-world example based on a project I was working on. As always, the differences are trivial, but the results were somewhat unexpected. For most of the benchmarks I've seen, the called function doesn't actually change the value passed in. I performed a simple str_replace() on it.

**Pass by Value Test Code:**

$originalString=''; // 1000 pseudo-random digits

function replace($string) {
    return str_replace('1', 'x',$string);
}
$output = '';
/* set start time */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tstart = $mtime;
set_time_limit(0);

for ($i = 0; $i < 10; $i++ ) {
    for ($j = 0; $j < 1000000; $j++) {
        $string = $originalString;
        $string = replace($string);
    }
}

/* report how long it took */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tend = $mtime;
$totalTime = ($tend - $tstart);
$totalTime = sprintf("%2.4f s", $totalTime);
$output .= "\n" . 'Total Time' .
    ': ' . $totalTime;
$output .= "\n" . $string;
echo $output;

Pass by Reference Test Code

The same except for

function replace(&$string) {
    $string = str_replace('1', 'x',$string);
}
/* ... */
replace($string);

Results in seconds (10 million iterations):

PHP 5
    Value:     14.1007
    Reference: 11.5564

PHP 7
    Value:     3.0799
    Reference: 2.9489

The difference is a fraction of a millisecond per function call, but for this use case, passing by reference is faster in both PHP 5 and PHP 7.

(Note: the PHP 7 tests were performed on a faster machine -- PHP 7 is faster, but probably not that much faster.)

Bob Ray
  • 1,105
  • 10
  • 20
1

There is nothing better than a testing piece of code

<?PHP
$r = array();

for($i=0; $i<500;$i++){
$r[]=5;
}

function a($r){
$r[0]=1;
}
function b(&$r){
$r[0]=1;
}

$start = microtime(true);
for($i=0;$i<9999;$i++){
  //a($r);
  b($r);
}
$end = microtime(true);

echo $end-$start;
?>

Final result! The bigger the array (or the greater the count of calls) the bigger the difference. So in this case, calling by reference is faster because the value is changed inside the function.

Otherwise there is no real difference between "by reference" and "by value", the compiler is smart enough not to create a new copy each time if there is no need.

Melsi
  • 1,462
  • 1
  • 15
  • 21
  • 6
    It might be more accurate to say "interpreter" instead of "compiler"? – mpet Sep 11 '16 at 22:39
  • when you benchmark, please show the resulting time values. Also, since you are testing, you should have also tested your claim that it doesn't matter, if no value is changed. Otherwise, a reader can't easily determine what you tested, what you are simply asserting. – ToolmakerSteve Jul 18 '20 at 00:04
-2

Is simple, there is no need to test anything. Depends on use-case.

Pass by value will ALWAYS BE FASTER BY VALUE than reference for small amount of arguments. This depends by how many variables that architecture allows to be passed through registers (ABI).

For example x64 will allow you 4 values 64 bit each to be passed through registers. https://en.wikipedia.org/wiki/X86_calling_conventions

This is because you don't have to de-referentiate the pointers, just use value directly.

If your data that needs to be passed is bigger than ABI, rest of values will go to stack. In this case, a array or a object (which in instance is a class, or a structure + headers) will ALWAYS BE FASTER BY REFERENCE.

This is because a reference is just a pointer to your data (not data itself), fixed size, say 32 or 64 bit depending on machine. That pointer will fit in one CPU register.

PHP is written in C/C++ so I'd expect to behave the same.

yo3hcv
  • 1,531
  • 2
  • 17
  • 27
  • PHP values are dynamically typed, so they are always passed as a reference to some structure describing the value and type. Whether the variable is passed by reference or not does not make a difference here. – Ivo Smits Mar 08 '19 at 15:16
-3

There is no need for adding & operator when passing objects. In PHP 5+ objects are passed by reference anyway.

Michał Niedźwiedzki
  • 12,859
  • 7
  • 45
  • 47
  • 1
    Or, actually, the way they are represented has been changed so that what's being passed around is always just a handler/reference/pointer anyway. But that wasn't exactly my question. – Hanno Fietz Oct 07 '08 at 13:22
  • 7
    -1 Objects in PHP 5 are not passed by reference. They are passed by value exactly as in Java. The key here is to understand that variables dont hold objects but pointers to objects. Hence what you are passing by value (in PHP and Java) is a pointer. – GetFree Sep 06 '11 at 18:03
  • The question does not specify that it is only about php *objects*. There most definitely are situations where a parameter should be specified by reference. (If there weren't then the reference operator would not exist.) – ToolmakerSteve Jul 18 '20 at 00:13