views:

217

answers:

5

Lets say I have a function where the parameter is passed by value instead of const-reference. Further, lets assume that only the value is used inside the function i.e. the function doesn't try to modify it. In that case will the compiler will be able to figure out that it can pass the value by const-reference (for performance reasons) and generate the code accordingly? Is there any compiler which does that?

+4  A: 

Only if the function is not exported there is a chance the compiler to convert call-by-reference to call-by-value (or vise-versa).

Otherwise, due to the calling convention, the function must keep the call-by-value/reference semantic.

KennyTM
But inlined copies of it could of course be optimized, even though the externally-visible version of the function isn't.
Steve Jessop
+8  A: 

If you pass a variable instead of a temporary, the compiler is not allowed to optimize away the copy if the copy constructor of it does anything you would notice when running the program ("observable behavior": inputs/outputs, or changing volatile variables).

Apart from that, the compiler is free to do everything it wants (it only needs to resemble the observable behavior as-if it wouldn't have optimized at all).

Only when the argument is an rvalue (most temporary), the compiler is allowed to optimize the copy to the by-value parameter even if the copy constructor has observable side effects.

Johannes Schaub - litb
Um, isn't copying exempted from the as-if rule? I thought copying is the only thing the compiler could eliminate not worrying about consequences.
sbi
I believe that the compiler does not need to guarantee that the copy constructor has a no 'observable behavior', since that is the same premise why the compiler can elide performing copies from temporaries.
David Rodríguez - dribeas
@sbi copying is exempted if you copy from an rvalue. If you copy from an lvalue, it's not. If you pass an rvalue and the compiler inlines the code of the called function, it can optimize out anything it wants (in fact, i've seen GCC optimize hundreds of assembly lines with three or four nested calls into just two or three lines). See 12.8/15 in the C++03 spec.
Johannes Schaub - litb
@David, the Standard is explicit that a copy can be elided even if the copy constructor has observable behavior. Paragraph 15 reads "When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects.". That's why you can place print statements into the copy constructor and notice when it does and when it doesn't copy.
Johannes Schaub - litb
I did not remember the specific 'criteria' and thought it was general.
David Rodríguez - dribeas
@Johannes: Now I'm confused. "That's why you can place print statements into the copy constructor and notice when it does and when it doesn't copy." seems to directly contradict "the compiler is not allowed to optimize away the copy if the copy constructor of it does anything you would notice when running the program" and also seems to support my claim that the latter isn't true.
sbi
@sbi, the compiler is allowed to do that if the source is an rvalue. So if you do `void f(Foo); int main() { f(Foo(1)); }` you can see from the printings wherher the compiler copied or didn't copy. That's what my last statement in my answer says, of course. In the case of lvalues it will always copy. (of course, return value optimization is another matter. My answer doesn't concern them, it only concerns argument passing, not returning).
Johannes Schaub - litb
Notice that all that applies only *If you pass a variable instead of a temporary* :) Variables aren't rvalues.
Johannes Schaub - litb
+1 to the answer and to the comment which explains why it is possible to find out the copy elision with simple print statements. Excellent.
Francesco
Doesn't 12.8/15 say that if either the original or the copy is not going to be used any longer, the copying can be elided? `void foo(X); int main() { X x; foo(x); /*no copy since x not used any longer*/ }` (Also means that you can't pass something by value for the sole reason to invoke the copy constructor? :) )
UncleBens
@UncleBens i don't see it say so for argument passing. That only applies to returning: `X f() { X x; return x; } /* can be elided */`. Notice that if the copy constructor has no side effects, it can always be elided, also in your example (as is usual the case with as-if).
Johannes Schaub - litb
A: 

This post is an excellent reference to this kind of optimization: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

Francesco
I think you are missing the point of the article (at least in the context of this question). In that article, a copy is going to be made one way or another, and it *will be modified*.
UncleBens
Francesco
A: 

With all optimisations the answer is generally "maybe". The only way to check is to examine the output assembly and see what it's really doing. If the standard allows it, whether or not it really happens is down to the whims of the compiler. You should not rely on it happening because an arbitrary change elsewhere in your codebase may change the heuristics used by the optimizer which might cause it to stop performing a certain optimization.

Play it safe: code it how you intend - pass by reference if that's what you want. However, if you're writing templated code which could work on types of any size, the choice is not so clear. Personally I'd side with passing by const reference - the compiler could also perform a different optimisation, where a small type which can fit inside the size of a reference is passed by value, rather than by const reference. But again, it might happen, it might not.

AshleysBrain
A: 

I'm not aware of any general guarantees that this will be done, but if the called function is inlined, then this would then allow the compiler to see that an unnecessary copy is being made, and if the optimization level is high enough, the copy operation would be eliminated. GCC can do this at least.

You might want to think about whether the class of this parameter value has a copy constructor or not. If it doesn't, then the performance difference between pass-by-value and pass-by-const-ref is probably neglible.

On the other hand, if class does have a copy constructor that does stuff, then the optimization you are hoping for probably will not happen because the compiler cannot remove the call to the constructor--it cannot know that the side effects of the constructor are not important to you.

You might be able to get more useful answers if you say what the class of the parameter is, or if it is a custom class, describe what fields it has and whether it has a copy constructor.

paul