33

Possible Duplicate:
Is it better to pass by value or by reference for basic datatypes?
Reasons to not pass simple types by reference?

I did some testing where I had two scenarios, each with two identical functions- one passing the parameter by reference and the other by value. The scenario with strings showed a massive performance increase (because a copy of string is made, calling a constructor) whereas the test with long didn't show any performance increase when passing the value by reference. In fact, sometimes the performance was worse.

Is this expected with primitive types? Is there no point passing them by reference?

I was expecting a copy of a primitive type is made when not using by reference and therefore expected a small performance-boost.

Community
  • 1
  • 1
user997112
  • 29,025
  • 43
  • 182
  • 361
  • 3
    I assume you mean passing by const reference (`const long &`), otherwise there's a significant semantic difference, which must be considered before even thinking about performance. Apart from that, I'm pretty sure this has been asked a dozen times already, I'll be right back. –  Dec 23 '12 at 17:43
  • 1
    Note that when the function is inlined, both pass-by-value and pass-by-const-reference are likely to result in "use the source directly; do not pass go; do not collect 200 dollars; erm, I mean, do not pass anything at all". – R. Martinho Fernandes Dec 23 '12 at 17:47
  • @R.MartinhoFernandes: Spend 200 cycles in jail. Not lock-free, of course. – Kerrek SB Dec 23 '12 at 17:54
  • 1
    I ran down the dupe-path, and believe [this question](http://stackoverflow.com/questions/270408/is-it-better-in-c-to-pass-by-value-or-pass-by-constant-reference) is likely a decent end of the road, though [this related question](http://stackoverflow.com/questions/2139224/how-to-pass-objects-to-functions-in-c/2139254#2139254) is pretty solid side-track. – WhozCraig Dec 23 '12 at 18:44

7 Answers7

52

You get the best performance from passing primitive types by value. This is because:

  • primitives are blitable, so the cost of the copy depends on the size
  • primitives are small, only double and long long are bigger than a reference in most environments
  • pass-by-value avoids aliasing, allowing the optimizer to really do its thing

This last point is often overlooked but can make a considerable difference.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Is this still true with modern compilers? – user7934593 Jun 30 '23 at 12:42
  • 1
    @user7934593: Yes of course. There are so many cases where the function call crosses module boundaries and the parameter passing has to obey the system ABI. In such cases it makes no difference how good the compiler is at optimization -- its hands are tied. Only when there's no function call at all (it got removed during inlining) can you hope to escape these rules. – Ben Voigt Jun 30 '23 at 14:15
  • Okay thanks! And `double` and `long long` should also not be passed by reference even though they are bigger than references right? Due to the avoidance of aliasing. – user7934593 Jun 30 '23 at 15:33
  • 1
    @user7934593: Yes. Even pairs of doubles (for example X,Y coordinates) are better to copy. Once you get up to 32 bytes or more (which means we're talking about a structure now rather than a primitive), you might start asking "would it be faster if I avoid this copy?" and the answer is probably still "copy is best" but if it's a function you call very often, it might be worth measuring instead of just using a rule of thumb. – Ben Voigt Jun 30 '23 at 15:37
12

Yes, that's the expected behavior. When you're passing parameters by reference, you're actually passing an address of the variable (like with pointer). Usually address is a 4 or 8-byte integer, so unless your primitive type is larger than that, you won't gain any performance improvement (and even if it's larger, you probably won't)

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85
10

Modern compilers are pretty clever, so if the function isn't "hidden" (that is, part of something the compiler can't see at the time of producing the code), it may well make no difference at all. HOwever, if it the compiler follows your instructions, passing simple types as reference does potentially make a big difference. Particularly if the value is updated many times in the code.

I saw some code where I worked, which did something like this:

void SomeClass::FindLength(int &len)
{
     listEntry* list = theList;   // theList is a member variable. 
     len = 0;
     while (list)
     {
         len++;
         list = list->next;
     }
 }

By alterning the code to do:

void SomeClass::FindLength(int &len)
{
     listEntry* list = theList;   // theList is a member variable. 
     int tempLen = 0;
     while (list)
     {
         tempLen++;
         list = list->next;
     }
     len = tempLen;
 }

the whole code ran some 30% faster, and called from a lot of places (and I think there was some if-condition in the middle, so we couldn't just keep track of the length). And since it was part of an API function, it wasn't possible to change the function signature.

The reason it was slower using the reference is that the compiler would write to the reference value EVERY time it was updated, which was a load from memory to register, increment register and a store register to memory. With the tempLen solution, the compiler could use a register, which is much faster.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
4

In c++ reference is just convinient way to use pointers. When you use pointer you adding additional indirection. Copying primitive types as cheap as copying pointer. That is why primitive types passed by reference a bit slower

kassak
  • 3,974
  • 1
  • 25
  • 36
2

When you pass a value by reference the function must dereference it to obtain the value, and each time you modify the value a dereference must occur too since you are writing it in a memory location. I guess compilers are able to understand when something won't be stored back in its reference location so that the value is modified just on registers and stored back just when needed but I'm not sure how powerful this is.

So there is an indirect step that is not present while passing parameters by value, which can cause worse performance but it's really foggy since compiler optimizations come into place. Think about the fact that you are passing a pointer, every time you need the value you must fetch the pointer from the stack and then fetch the value pointed (so there are two accesses), while with a normal parameter you have just one (fetching the value).

In any case, a reference is used for purposes that are surely different from performance, like modifying the passed parameter locally.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • 1
    The function does not have to dereference a pointer to obtain its value, that would make references useless. In fact, functions do not even have to obtain values, they are just fine with the pointers, that can later be passed to other functions. Consider this example: `double len(const double& t_a, const double& t_b) { return std::sqrt(square_sum(a, b)); } double square_sum(const double& t_a, const double& t_b) { return a * a + b * b; }`. Here the function `len` does not even have to know the values of its parameters, why would it dereference them? – Adam Hunyadi Jun 20 '17 at 08:09
1

Because you used c tag, I guess you are speaking about pointers (not explicit references from C++).

With pointers, you have two memory access: the pointer and the pointed value. So there is no particular gain of performances. Moreover, the compiler can do more optimisations with values: there is no aliasing problems, for example.

md5
  • 23,373
  • 3
  • 44
  • 93
1

Is this expected with primitive types?

I'd say absolutely. They don't have constructors, so that doesn't need to be called.

Is there no point passing them by reference?

There is: when you want to have output parameters, then, in C++, passing by reference is considered better pracitce than passing a pointer.

I was expecting a copy of a primitive type is made when not using by reference and therefore expected a small performance-boost.

Well, since passing by reference is usually implemented using pointers, then the compiler has to emit code that pushes something onto the stack, either the value, or a pointer to the value - and it really does not matter which one is done.