Home » C++ » Is it a premature optimization to use std::move()?

Is it a premature optimization to use std::move()?

Posted by: admin November 29, 2017 Leave a comment

Questions:

Suppose I have the following code:

int main()
{
    std::vector<std::string> strs;

    std::string var("Hello World");
    // Make some modifications to 'var'
    strs.push_back(std::move(var));
}

The part of the sample I want to point out is the usage of std::move(). Basically I’m worried about a copy on the push_back() call. Suppose the string I’m adding is really big. I’m still learning C++11 r-value references, so I’m not sure how the compiler would optimize away the copy (if at all) without the std::move().

Could anyone explain if this is a premature optimization (forcing moves in all cases where you want to avoid copies, in general)? If so, what patterns should I expect the compiler to follow (or most likely follow) that would result in an optimized and automatic move here?

EDIT

I want to add that I understand how automatic moves happen on function return values, because NRVO/RVO applies. The specific example I gave here wouldn’t apply RVO, so I’m uncertain.

Answers:

The other answers focus too much on the technical aspects of the question for my taste, so I will try to give a more general answer.

In short: No, using a “trick” like std::move in the way it’s described in the question isn’t a premature optimization. Not using std::move when it can be used is fine too, unless the code is already known to be performance critical.

The need to avoid premature optimization is sometimes understood as “don’t optimize, until you can prove it’s necessary”, but I prefer to read it as: “don’t waste time solving problems unless you know they need to be solved”.

Premature optimizations require spending effort in order to optimize what may not need to be optimized, and usually transform a simple problem into a complex problem while doing so. From that perspective, I would classify any long pondering of the question itself as premature optimization.

A related example: People working in performance critical code will often pass arguments as const references ( const std::string& ). Because that’s what they are used to do, they will use the same pattern in code that isn’t performance critical, even though they could just use pass-by-copy ( const std::string, or even std::string ). That isn’t a premature optimization either.

Questions:
Answers:

I’m not sure how the compiler would optimize away the copy (if at all) without the std::move().

Only a very clever compiler could optimise that away, so if the copy could be expensive (e.g. a very long string) then it is better to move it.

Without the move the code is effectively a sequences of calls to:

strlen  // count "Hello World"
malloc  // allocate memory for string var
strcpy  // copy data into var
malloc  // re-allocate vector
free    // deallocate old vector
malloc  // allocate new string
strcpy  // copy from var to new string
free    // destroy var

With the move it becomes:

strlen  // count "Hello World"
malloc  // allocate memory for string var
strcpy  // copy data into var
malloc  // re-allocate vector
free    // deallocate old vector

In theory a smart compiler could do that transformation automatically, but for the compiler to see through all the layers of abstraction introduced by the constructors and destructor and the vector member functions is quite difficult, so proving that the code could be transformed to remove a malloc and free is complicated.

Questions:
Answers:

After std::move, the original object, in this case var, must be in a valid state but could hold any value, e.g. it might be empty.

If you know you are not going to use var and you only created it to put into the vector then it isn’t really “premature” optimisation as the intention of what you are trying to do.

vector has a new method emplace_back() which is the same thing but clearer, and uses forward-arguments (here you just do emplace_back("Hello World") if all you are doing is constructing it). In your case as you are “making some modifications with var” emplace_back isn’t likely to not be appropriate.

In old C++ you could optimise the copy by doing push_back() on an empty string then swapping it.

Questions:
Answers:

I don’t know why everyone is suggesting you use emplace_back(). The purpose of emplace_back() is to avoid copy/move operations by constructing the object in place. In this case, you have already constructed the object, so at least 1 copy/move is unavoidable. There is no advantage to using emplace_back() over push_back() in this case.

Otherwise, I agree with everyone else saying that it’s not a premature optimization because the move semantics models what you are trying to do more closely than making a copy of the object.

Questions:
Answers:

Yes, it is premature optimization if it is premature optimization.

Let me elaborate:
Premature optimization is defined by whether you are optimizing a part of the code that you don’t know to be performance critical. That is, premature optimization is never defined by the method of optimization, it is always defined by the place of optimization and your knowledge about what you are doing.


Now, to the advisability to optimize using std::move():

With std::move() you avoid the heavy lifting of copy construction, like memory allocation, deallocation, copy constructing contained elements, deconstructing contained elements, etc. That is good. But one part remeains: the construction/destruction of the container object.

emplace_back() has the virtue of entirely avoiding the construction of the temporary object. So, whenever you can avoid a temporary object by using emplace_back(), you have a small win.

Considering code quality (read readability/maintainability): std::move() has the downside of leaving you with an unusable object, which can be a source of bugs. This does not happen with emplace_back(). So, again, the later is clearly preferable.

Of course, emplace_back() cannot be used in all contexts that allow the use of std::move(). So, if these are performance critical, std::move() may be the way to go. There are some very valid use-cases for optimization with std::move(). However, you might also find out that you can write the code in a way that does not require any copy/move/emplace construction at all. Never stop looking for a better solution when optimizing!

In the end, it remains that the validity of optimizing with std::move() depends entirely on the context: It is good optimization wherever it is good optimization.