15

One commonly known compiler optimisation is is the so-called return value optimisation. This optimisation basically allows the compiler to not copy a local variable that is being returned from a function, but instead moving it.

However, I was wondering if the same is also possible for passing arguments to a function by value if it is known that the return value of the function will overwrite the original argument.

Here is an example. Let's assume we have the following function:

std::vector<Foo> modify(std::vector<Foo> data) {
    /* Do some funny things to data */
    return data;
}

This function is then used in the following way:

std::vector<Foo> bigData = /* big data */;
bigData = modify(bigData); // Here copying the data into the function could be omitted

Now, in this case it can be clearly determined that the return value of the function call will override the argument that is passed into the function per value. My question is whether current compilers are able to optimise this code in a way so that the argument data is not copied when passed to the function, or if this might even be a part of the so-called return value optimisation.

Update

Let's take C++11 into account. I wonder if the following understanding is correct: If the value passed to a function parameter by value is an r-value, and the type of the parameter has a move-constructor, the move constructor will be used instead of the copy constructor.

For example:

std::vector<Foo> bigData = /* big data */;
bigData = modify(std::move(bigData));

If this is assumption is correct, this eliminates the copy operation when passing the value. From the answers already given it seems that the optimisation I referred to earlier is not commonly undertaken. Looking at this manual approach I don't really understand why, as appears to be pretty straightforward to apply.

bweber
  • 3,772
  • 3
  • 32
  • 57
  • 3
    It's hard to answer definitively but if I had to bet: I don't think you will ever get this optimization *unless* the function is inlined. – Nir Friedman Jun 09 '17 at 13:39
  • 4
    "It's hard to answer definitively" - well, it's kind of easy, it just requires taking the compiler you're interested in and then looking at the generated asm. – UKMonkey Jun 09 '17 at 13:48
  • 4
    @UKMonkey It's easy to answer, for a specific function, for a specific compiler, on specific architecture, at specific flags, yes, I agree. The question is much more general and the body of the function is not specified so I wouldn't say it's "easy". Anyway I put in my best attempt at an answer. – Nir Friedman Jun 09 '17 at 13:50

3 Answers3

4

It's hard to say for sure because in principle compilers can optimize many things, as long as they are certain it has the same behavior. However, in my experience, this optimization will not occur without inlining. Consider the following code:

__attribute__((noinline)) std::vector<double> modify(std::vector<double> data) {
    std::sort(data.begin(), data.end());
    return data;
}

std::vector<double> blah(std::vector<double> v) {
    v = modify(v);
    return v;
}

You can look at the assembly generated for this for various compilers; here I have clang 4.0 with O3 optimization: https://godbolt.org/g/xa2Dhf. If you look at the assembly carefully, you'll see a call to operator new in blah. This proves that blah is indeed performing a copy in order to call modify.

Of course, if inlining occurs, it should be pretty trivial for the compiler to remove the copy.

Nir Friedman
  • 17,108
  • 2
  • 44
  • 72
2

In C++11 the compiler could determine that bigData is reassigned after use in the function and pass it as rvalue, but there is no guarantee for that, unlike for the RVO (from c++17).

For std::vector at least you can make sure this happens by calling the function as modify(std::move(bigData)), which will construct the value in modify from the rvalue reference, which it cannot optimize with the RVO afaik, because it is the function parameter, which is explicitly excluded from this optimization (3rd point here). However the compiler should understand that the return value is an r-value, and move it into big-data again.

Whether some compilers elide a move from an object into a function and out of the function back into the object I don't know for sure, but I know nothing that explicitly allows it, and since the move-constructor could have observable side-effects, that probably means, that it is not allowed (cf. the Notes section in above link).

midor
  • 5,487
  • 2
  • 23
  • 52
2

That is really compiler specific and depends on how you perform operations(whether we are modifying the data or not) with the data. Mostly you shouldn't expect the compiler to do such kind of optimizations unless you really benchmark it. I did some tests with VS2012 compiler that performs copy operations though we don't modify it.

Please have a look at this post(Does the compiler optimize the function parameters passed by value?), that may give you a better idea I hope.

iamrameshkumar
  • 1,328
  • 1
  • 14
  • 34