C ++ 0x performance improvement

One of the improvements in C ++ 0x that will allow writing more efficient C ++ code is the unique unique_ptr pointer (it’s too bad that it will not allow you to navigate memmove () as an operation: this sentence was not part of the project).

What are the other performance improvements in the upcoming standard? Take the following code, for example:

vector<char *> v(10,"astring"); string concat = accumulate(v.begin(),v.end(), string("")); 

The code combines all the lines contained in the vector v . The problem with this neat piece of code is that accumulate () copies things around and doesn't use links. And line () redistributes every time the statement is called. Thus, the code has poor performance compared to the well-optimized analog C code.

Does C ++ 0x provide tools to solve the problem, and possibly others?

+9
c ++ optimization c ++ 11 stl unique-ptr
Jun 10 '09 at 12:52
source share
4 answers

Yes C ++ solves the problem using move semantics.

Basically, this allows one object to accept the internal representation of another object if that object is temporary. Instead of copying each byte in a string using a copy constructor, for example, you can often simply allow the target string to accept the internal representation of the original string. This is only allowed when the source is an r-value.

This is done through the introduction of the move constructor. Its constructor, where you know that the src object is temporary and leaves. Therefore, it is acceptable for the receiver to accept the internal representation of the src object.

The same is true for call forwarding operators.

To distinguish the copy constructor from the move constructor, the language introduced rvalue references. The class defines its move constructor to take the rvalue reference, which will only bind to rvalues ​​(temporary). This way my class will determine something line by line:

  class CMyString { private: char* rawStr; public: // move constructor bound to rvalues CMyString(CMyString&& srcStr) { rawStr = srcStr.rawStr srcStr.rawStr = NULL; } // move assignment operator CMyString& operator=(CMyString&& srcStr) { if(rawStr != srcStr.rawStr) // protect against self assignment { delete[] rawStr; rawStr = srcStr.rawStr srcStr.rawStr = NULL; } return *this; } ~CMyString() { delete [] rawStr; } } 

Here is a very good and detailed article on the semantics of movement and the syntax that allows you to do this.

+13
Jun 10 '09 at 13:01
source share

One performance boost will be generalized constant expressions, which are introduced by the constexpr keyword.

 constexpr int returnSomething() {return 40;} int avalue[returnSomething() + 2]; 

This is not legal C ++ code because returnSomething () + 2 is not a constant expression.

But using the constexpr keyword, C ++ 0x can tell the compiler that the expression is a compile-time constant.

+7
Jun 10 '09 at 13:04
source share

Sorry - you cannot say that string concat = accumulate(v.begin(),v.end(), string("")); must redistribute. Of course, a simple implementation. But compilers are very allowed to do the right thing here.

This already takes place in C ++ 98, and C ++ 0x continues to use both smart and dumb implementations. However, moving semantics will simplify intelligent implementations.

+1
Jun 10 '09 at 14:32
source share
 vector<string> v(10, "foo"); string concat = accumulate(v.begin(), v.end(), string("")); 

This example is just bad programming in any C ++ standard. This is equivalent to this:

 string tmp; tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 

C ++ 11 move semantics will only take care of β€œcopying the result back to tmp” part of the equation. The original copies from tmp will still be copies. This is the classic Schlemiel the Painter algorithm, but even worse than the usual strcat in C.

If accumulate simply used += instead of + and = , then he would have avoided all of these copies.

But C ++ 11 gives us a way to do better by staying concise using the lambda function:

 string concat; for_each(v.begin(), v.end(), [&](const string &s){ concat += s; }); 

EDIT: I suppose a standard library developer could implement accumulate by moving the operand to + , so tmp = tmp + "foo" will become tmp = move(tmp) + "foo" , and this will pretty much solve this problem. I am not sure that such an implementation will be strictly consistent. Currently, neither GCC, nor MSVC, nor LLVM do this. And since accumulate defined in <numeric> , we can assume that it is intended only for use with numeric types.

0
Sep 09 '13 at 17:05
source share



All Articles