Home > C++11 > Move Me, Please!

Move Me, Please!

UPDATE: Don’t read this post. It’s wrong, sort of 🙂 Actually, please do read it and then go read John’s comment below along with my reply to him. D’oh!

Just because all the STL containers are “move enabled” in C++11, it doesn’t mean you get their increased performance for free when you use them as member variables in your own classes. You don’t. You still have to write your own “move” constructor and “move” assignment functions to obtain the superior performance provided by the STL containers. To illustrate my point, consider the code and associated console output for a typical run of that code below.

UserType Move

Please note the 3o-ish millisecond performance measurement we’ll address  later in this post. Also note that since only the “move” operations are defined for the UserType class, if you attempt to “copy” construct or “copy” assign a UserType object, the compiler will barf on you (because in this case the compiler doesn’t auto-generate those member functions for you):

UserType No Copy

However, if you simply add “default” declarations of the copy ctor and copy assignment operations to the UserType class definition, the compiler will indeed generate their definitions for you and the above code will compile cleanly:

UserType Copy

Ok, now remember that 30-ish millisecond performance metric we measured for “move” performance? Let’s compare that number to the performance of a “copy” construct plus “copy” assign pair of operations by adding this code to our main() function just before the return statement:

UserType Copy

After compiling and running the code that now measures both “move” and “copy” performance, here’s the result of a typical run:

Move vs Copy Perf

As expected, we measured a big boost in performance, 5X in this case, by using “moves” instead of old-school “copies“. Having to wrap our args with std::move() to signal the compiler that we want a “move” instead of “copy” was well worth the effort, no?

Summarizing the gist of this post; please don’t forget to write your own simple “move” operations if you want to leverage the “free” STL container “move” performance gains in the copyable classes you write that contain STL containers. 🙂

NOTE1: I ran the code in this post on my Win 8.1 Lenovo A-730 all-in-one desktop using GCC 4.8/MinGW and MSVC++13. I also ran it on the Coliru online compiler (GCC 4.8). All test runs yielded similar results. W00t!

NOTE2: When I first wrote the “move” member functions for the UserType class, I forgot to wrap other.vints with the overhead-free std::move() function. Thus, I was scratching my (bald) head for a while because I didn’t know why I wasn’t getting any performance boost over the standard copy operations. D’oh!

NOTE3: If you want to copy and paste the code from here into your editor and explore this very moving topic for yourself, here is the non-.png listing:

#include <iostream>
#include <chrono>
#include <vector>

class UserType {
public:
// 100 million ints with value 100
UserType() : vints(100000000, 100)  {
}

//move ctor
UserType(UserType&& other) {
//invoke std::vector move assignment
vints = std::move(other.vints);
}

//move assignment
UserType& operator=(UserType&& other) {
//invoke std::vector move assignment
vints = std::move(other.vints);
return *this;
}

UserType(const UserType& other) = default;
UserType& operator=(const UserType& other) = default;

private:
std::vector<int> vints;
};

int main() {
UserType ut1{};
UserType ut2{};

using namespace std;
using namespace std::chrono;

auto start = steady_clock::now();
ut2 = std::move(ut1);
UserType ut3{std::move(ut2)};
auto end = steady_clock::now();

cout << "Time for move ctor + move assignment (micros) = "
<< duration_cast<microseconds>((end-start)).count()
<< endl;

UserType ut4{};
UserType ut5{};

start = steady_clock::now();
ut5 = ut4;
UserType ut6{ut5};
end = steady_clock::now();

cout << "Time for copy ctor + copy assignment (micros) = "
<< duration_cast<microseconds>((end-start)).count()
<< endl;

return 0;
}
Categories: C++11 Tags: ,
  1. February 10, 2014 at 8:00 am

    Great post! Good info….

  2. John
    February 10, 2014 at 8:19 am

    Your example UserType does not need to declare the move constructor/assignment operator. As long as you don’t declare a destructor or copy constructor/assignment operator, and your data members are all moveable, you will get the implicit move semantics.

    • February 10, 2014 at 8:38 am

      Sorrry, but I think you are wrong about the last part. The compiler generated move operations will implement copies. I’ll test it out and update.

    • February 10, 2014 at 11:02 am

      Nope! You are right! UserType needs no explicitly defined move/copy operations. As long as the caller uses std::move(), the std::vector moves will get executed. Thanks for straightening me out.

      #include <iostream>
      #include <chrono>
      #include <vector>
      
      class UserType {
      public:
      // 10 million ints with value 10
      UserType() : vints(10000000, 100)  {
      }
      
      private:
      std::vector<int> vints;
      };
      
      int main() {
      UserType ut1{};
      UserType ut2{};
      
      using namespace std;
      using namespace std::chrono;
      
      auto start = steady_clock::now();
      ut2 = std::move(ut1);
      UserType ut3{std::move(ut2)};
      //ut2 = ut1;
      //UserType ut3{ut2};
      auto end = steady_clock::now();
      
      cout << "Time for move ctor + move assignment (micros) = "
      << duration_cast<microseconds>((end-start)).count()
      << endl;
      
      UserType ut4{};
      UserType ut5{};
      
      start = steady_clock::now();
      ut5 = ut4;
      UserType ut6{ut5};
      end = steady_clock::now();
      
      cout << "Time for copy ctor + copy assignment (micros) = "
      << duration_cast<microseconds>((end-start)).count()
      << endl;
      
      return 0;
      }
      

      Running the above code on Coliru yields:

      Time for move ctor + move assignment (micros) = 16139
      Time for copy ctor + copy assignment (micros) = 97018

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: