tags:

views:

286

answers:

12

The question: Is there benefit to passing an integral type by const reference as opposed to simply by value.

ie.

void foo(const int& n); // case #1

vs

void foo(int n); // case #2

The answer is clear for user defined types, case #1 avoids needless copying while ensuring the constness of the object. However in the above case, the reference and the integer (at least on my system) are the same size, so I can't imagine there being a whole lot of difference in terms of how long it takes for the function call (due to copying). However, my question is really related to the compiler inlining the function:

For very small inline functions, will the compiler have to make a copy of the integer in case #2? By letting the compiler know we won't change the reference can it inline the function call without needless copying of the integer?

Any advice is welcome.

+4  A: 

It's usually not worth it. Even for inline function, the compiler won't be stupid. The only time I would say it's appropriate is if you had a template; it might not be worth the extra effort to specialize for builtins just to take a copy instead of a reference.

rlbond
+1  A: 

I can't think of any benefit. I've even seen recommendation that when writing templates, you use meta-programming to pass integral types by value and only use const reference for non-integral types.

R Samuel Klatchko
+4  A: 

I actually find it irritating when somebody uses const references like this for the basic datatypes. I can't see any benefit of doing this, although it may be argued that for datatypes bigger than sizeof(pointer) it may be more efficient. Although, I really don't care about such minute 'optimizations'.

Naveen
A: 

well the cost of a reference is the same typically of that of an integral type, but with the reference you have an indirection that has to take place, because the reference to some memory has to be resolved into a value.

Just copy by value, stick to an immutable convention for built-in types.

John Leidegren
+6  A: 

It depends on the compiler, but I'd expect that any reasonable optimizer would give you the same results either way.

I tested with gcc, and the results were indeed the same. here's the code I tested:

inline int foo(const int& n) {
  return n * 2;
}

int bar(int x) {
  int y = foo(x);
  return y;
}

(with and without const & on foo's n parameter)

I then compiled with gcc 4.0.1 with the following command line:

g++ -O3 -S -o foo.s foo.cc

The outputs of the two compiles were identical.

Laurence Gonsalves
+1 Thanks for testing.
DeusAduro
I wonder if they'd be the same assembly output if bar() was in another file? Probably not.
asveikau
@asveikau: If the function is inline, there is no way bar() can be in another compilation unit. Inline functions must be defined within the header file, and all compilation units will include that header, thus including the whole definition. The keyword there is 'inline' and that makes the whole difference as the compiler at all call sites knows the exact internals of the function.
David Rodríguez - dribeas
@dribeas Well obviously. I meant if it were not inline. I see my point is lost; in the example above it works because the compiler _can_ and _does_ inline, but if they were separate compilation units it couldn't do that.
asveikau
+8  A: 

Passing a built-in int type by const ref will actually be a minor de-optimization (generally). At least for a non-inline function. The compiler may have to actually pass a pointer that has to be de-referenced to get the value. You might think it could always optimize this away, but aliasing rules and the need to support separate compilation might force the compiler's hand.

However, for your secondary question:

For very small inline functions, will the compiler have to make a copy of the integer in case #2? By letting the compiler know we won't change the reference can it inline the function call without needless copying of the integer?

The compiler should be able to optimize away the copy or the dereference if semantics allow it, since in that situation the compiler has full knowledge of the state at the call site and the function implementation. It'll likely just load the value into a register have its way with it and just use the register for something else when it's done with the parameter. Of course,all this is very dependent on the actual implementation of the function.

Michael Burr
+1  A: 

Don't do this. int is the same size as pointer/reference on common 32-bit plattforms, and smaller on 64-bit, thus you could get yourself a performance disadvantage instead of a benefit. I mean, all function arguments are pushed onto stack in order so that a function can read them, and it will either be your int, or its address in the case of reference. Another disadvantage is that the callee will either access your n through an indirection (dereferencing an address), or it will make copy on its stack as an optimization.

If you make some changes to an int passed by value, it might be written either back onto the place on the stack where it was passed, or onto a new stack position. The second case naturally isn't advantagous, but shouldn't happen. By consting you bar yourself from making such a change, but this would work the same with const int.

In the proper inline case it doesn't matter, naturally, but keep in mind that not everything where you write inline, will be.

3yE
+2  A: 

When writing or using templates, you may end up with (const int &) because the template writer can't know what the type actually is. If the object is heavyweight, passing a reference is the right thing to do; if it's an int or something, the compiler may be able to optimize it away.

In the absence of some kind of external requirement, there is generally no reason to do something like this for a one-off function -- it's just extra typing, plus throwing around references actually tends to inhibit optimization. Copying small data in registers is much cheaper than reloading it from memory in case it's changed!

comingstorm
+2  A: 

A lot of people are saying there's no difference between the two. I can see one (perhaps contrived) case in which the difference would matter...

int i = 0;

void f(const int &j)
{
    i++;

    if (j == 0)
    {        
        // Do something.
    }
}

void g()
{
    f(i);
}

But.. As others have mentioned... integers and pointers are likely to be of similar size. For something as small as an integer, references will decrease your performance. It probably won't be too noticeable unless your method is called a lot, but it will be there. On the other hand, under some circumstances the compiler may optimize it out.

asveikau
A: 
Henrik
A: 

You can use boost::call_traits<your type>::param_type for optimal parameter passing. Which defaults to simple parameter passing of primitive types and passing by const reference of structs and classes.

denisenkom
A: 

Please read Want Speed? Pass by Value by Dave Abrahams.

tony