Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Can the Compiler Optimize a Passing by Value?

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.

like image 552
bweber Avatar asked Jun 09 '17 13:06

bweber


People also ask

How does a compiler optimize?

Compiler optimization is generally implemented using a sequence of optimizing transformations, algorithms which take a program and transform it to produce a semantically equivalent output program that uses fewer resources or executes faster.

Does compiler optimize code?

Compilers are free to optimize code so long as they can guarantee the semantics of the code are not changed. I would suggestion starting at the Compiler optimization wikipedia page as there are many different kinds of optimization that are performed at many different stages.

How do compilers optimize for loops?

The compiler can do the following: create a separate version of the loop for each possible value of the variable operation . The transformation is called loop unswitching, because there is a different version of the loop for each value of the condition.

Does compiler optimize division?

In short, you can rely on the compiler to do a great job of optimizing division by a compile-time-known constant.


2 Answers

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.

like image 66
Nir Friedman Avatar answered Oct 06 '22 22:10

Nir Friedman


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).

like image 35
midor Avatar answered Oct 06 '22 20:10

midor