As a rule of thumb, passing by reference or pointer is typically faster than passing by value, if the amount of data passed by value is larger than the size of a pointer.
Passing by const reference is a safer option if you work on a team without comprehensive performance regression tests. You will get bitten by this sooner or later.
From what I understand: when you pass by value, the function makes a local copy of the passed argument and uses that; when the function ends, it goes out of scope. When you pass by const reference, the function uses a reference to the passed argument that can't be modified.
Concerning objects (especially strings), call by reference is faster than call-by-value because the function call does not need to create a copy of the original object. Using const, one can also ensure that the reference is not abused.
If you have reason to suspect there is a worthwhile performance gain to be had, cut it out with the rules of thumb and measure. The purpose of the advise you quote is that you don't copy great amounts of data for no reason, but don't jeopardize optimizations by making everything a reference either. If something is on the edge between "clearly cheap to copy" and "clearly expensive to copy", then you can afford either option. If you must have the decision taken away from you, flip a coin.
A type is cheap to copy if it has no funky copy constructor and its sizeof
is small. There is no hard number for "small" that's optimal, not even on a per-platform basis since it depends very much on the calling code and the function itself. Just go by your gut feeling. One, two, three words are small. Ten, who knows. A 4x4 matrix is not small.
The most appropriate rule of thumb in my opinion is pass by reference when :
sizeof(T) >= sizeof(T*)
The idea behind this is that when you take by reference, at worst your compiler might implement this using a pointer.
This of course doesn't take into account the complexity of your copy constructor and move semantics and all the hell that can be created around your object life cycle.
Also if you don't care about micro optimisations you can pass everything by const reference, on most machines pointer are 4 or 8 bytes, very few types are smaller than that and even in that case you would lose a few (less than 8) bytes copy operation and some indirections which in modern world is most likely not gonna be your bottleneck :)
Passing a value instead of a const reference has the advantage that the compiler knows the value isn't going to change. "const int& x" doesn't mean the value cannot change; it only means that your code is not allowed to change it by using the identifier x (without some cast that the compiler would notice). Take this awful but perfectly legal example:
static int someValue;
void g (int i)
{
--someValue;
}
void f (const int& x)
{
for (int i = 0; i < x; ++i)
g (i);
}
int main (void)
{
someValue = 100;
f (someValue);
return 0;
}
Inside function f, x isn't actually constant! It changes every time that g (i) is called, so the loop only runs from 0 to 49! And since the compiler generally doesn't know whether you wrote awful code like this, it must assume that x might change when g is called. As a result, you can expect the code to be slower than if you had used "int x".
The same is obviously true for many objects as well that might be passed by reference. For example, if you pass an object by const&, and the object has a member that is int or unsigned int, then any assignment using a char*, int*, or unsigned int* might change that member, unless the compiler can prove otherwise. Passed by value, the proof is much easier for the compiler.
I believe I would choose to pass by value whenever possible (that is: when the semantics dictate that I do not need the actual object to work on). I would trust the compiler to perform the appropriate moves and copy-elision.
After my code is semantically correct, I would profile it to see if I am making any unnecessary copies; I would modify those accordingly.
I believe that this approach would help me focus on the most important part of my software: correctness. And I would not get on the way of the compiler---interfere; inhibit---to perform optimizations (I know I cannot beat it).
Having said that, nominally references are implemented as pointers. So in a vaccum, without considering semantics, copy-elisions, move semantics, and stuff like that, it would be more "efficient" to pass by pointer/reference anything whose size is larger than the pointer.
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