19

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?

Naveen
  • 74,600
  • 47
  • 176
  • 233
  • 1
    Note that usually the *compiler* only considers one TU at a time. In practice, optimizations which involve the caller doing something different, based on what happens in the callee, only work if the function definition is available in the caller's TU, and/or with link-time optimization. – Steve Jessop Mar 15 '10 at 16:46
  • Related question: http://stackoverflow.com/questions/2043974 – Emile Cormier Mar 15 '10 at 18:28

5 Answers5

15

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
  • 496,577
  • 130
  • 894
  • 1,212
  • 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 Mar 15 '10 at 16:18
  • 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 Mar 15 '10 at 16:20
  • 2
    @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 Mar 15 '10 at 16:21
  • 2
    @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 Mar 15 '10 at 16:24
  • I did not remember the specific 'criteria' and thought it was general. – David Rodríguez - dribeas Mar 15 '10 at 19:06
  • @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 Mar 15 '10 at 19:55
  • @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 Mar 15 '10 at 20:24
  • Notice that all that applies only *If you pass a variable instead of a temporary* :) Variables aren't rvalues. – Johannes Schaub - litb Mar 15 '10 at 20:35
  • +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 Mar 15 '10 at 20:48
  • 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 Mar 15 '10 at 22:57
  • 1
    @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 Mar 16 '10 at 05:57
7

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
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 2
    But inlined copies of it could of course be optimized, even though the externally-visible version of the function isn't. – Steve Jessop Mar 15 '10 at 16:48
1

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
  • 11
  • 1
0

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
  • 22,335
  • 15
  • 88
  • 124
-1

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

Francesco
  • 3,200
  • 1
  • 34
  • 46
  • 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 Mar 15 '10 at 16:24
  • UncleBens: I see what you mean, but I thought that the blog post sheds some light on the kind of optimization that a compiler is capable of, and so I felt that it was not completely off-topic. Think about the comment “does this means that we can completely and forever dump the old pass-by-const-reference & copy operator= ?” to which D. Abrahams answers: “Actually, yes! Dump it yesterday. ” – Francesco Mar 15 '10 at 16:46