Home > C++11 > No Runtime Overhead

No Runtime Overhead

Since I have the privilege of using C++11/14 on my current project, I’ve been using the new language idioms as fast as I can discover and learn them. For example, instead of writing risky, exception-unsafe, naked “new“, code like this:

nakedptr

I’ve been writing code like this instead:

stdmove1

By using std::unique_ptr instead of a naked pointer, I don’t have to veer away from the local code I’m writing to write matching delete statements in destructors or in catch() exception clauses to prevent inadvertent memory leaks.

I could’ve used a std::shared_ptr (which can be copied instead of “moved“) in place of the std::unique_ptr, but std::shared_ptr is required to maintain a fatter internal state in the form of strong and weak owner counters. Unless I really need shared ownership of a dynamically allocated object, which I haven’t so far, I stick to the slimmer and more performant std::unique_ptr.

sharedunique

When I first wrote the std::unique_ptr code above, I was concerned that using the std::move() function to transfer encapsulated memory ownership into the safeTgtList vector would add some runtime overhead to the code (relative to the C++98/03 style of simply copying the naked pointer into the scaryTgtList vector). It is, after all, a function, so I thought it must insert some code into my own code.

compilerinserted

However, after digging a little deeper into my concern, I discovered (via Stroustrup, Sutter, and Meyers) that std::move() adds zero runtime overhead to the code. Its use is equivalent to performing a static_cast on its argument – which is evaluated at compile time.

staticcast

As Scott Meyers stated at GoingNative13, std::move() doesn’t really move anything. It simply prepares for a subsequent real move by casting its argument from an lvalue to an rvalue – which is required for movement of an object’s innards. In the previous code, the move is actually performed within the std::vector::emplace_back() function.

Quoting Scott Meyers: “think of std::move() as an rvalue_cast“. I’m not sure why the ISO C++ committee didn’t define a new rvalue_cast keyword instead of std::move() to drive home the point that no runtime overhead is imposed, but I’d speculate that the issue was debated. Perhaps they thought rvalue_cast was too technical a term for most users?

Update 10/25/13

As I said early in the post, the code example is “like” the code I’ve been writing. The real code that triggered this post is as shown here:

Real Code

Since each “entryCfarDetState object must be uniquely intialized form it’s associated CfarCrossing object, I can’t simply insert estd::make_unique<CfarDetState>() into the emplace_back() function. All of the members  of each “entry” must be initialized first. Regardless of whether I use emplace_back() or push_back(), std::move(entry) must be used as the argument of the chosen function.

  1. October 16, 2013 at 11:20 am

    To your last question, I strongly suspect it’s because most programmers avoid casts as bad practice, I think for good reason. Move on the other hand is something to be encouraged. Using a cast blurs a fairly solid line.

  2. Jouan Amate
    October 16, 2013 at 11:32 am

    The move constructor itself does have overhead though. And it will be called within emplace_back. Typically, std::move prepares for performance overhead.

  3. robdesbois
    October 16, 2013 at 11:48 am

    To respond to your query of why we have `std::move()` instead of `rvalue_cast`, I think it’s probably because the former name implies intent, whereas the latter implies mechanism. It’s worth remembering that not all casts are free: `dynamic_cast` carries not insignificant runtime overhead.

  4. Martin Moene
    October 16, 2013 at 3:36 pm

    > rvalue_cast keyword instead of std::move()

    By the time it was realized that naming it std::move() isn’t ideal, it was deemed “too late” to change.

    Not sure where/in which presentation I’ve heard it (Bjarne, Scott, GoingNative2013, ACCU?).

  5. October 16, 2013 at 4:39 pm

    Thanks for the input guys.

  6. October 16, 2013 at 5:27 pm

    Why not just do
    safeTgtlist.emplace_back( ) ? You don’t need to create 2 temporaries move between them , and then move again to the vector.
    You could do
    safeTgtList.emplace_back( new MyPointer() ); as well if you have something to put in it.

    • October 16, 2013 at 5:39 pm

      Nevermind in the rare case the vector fails it would leak, put it in a temporary.

  7. Ron
    October 18, 2013 at 1:10 am

    Using std::unique here is an improvement, but it may not be optimal: consider inserting RawSignals themselves into the std::vector, rather than smart pointers to RawSignals, if possible. This can simplify the code (pretty significantly!) and more clearly express ownership.

    • October 18, 2013 at 2:44 am

      The code fragment is simply an example. What if RawSignal was large with lots of data members , some of which were other classes? Copying large objects is much more expensive than copying or moving pointers.

      • October 25, 2013 at 7:51 am

        The code uses emplace_back, which constructs objects in-place instead of performing any copy or move constructors. If, in actual code, the signal was being constructed and then placed into the list, the code could instead use emplace_back to guarantee that you don’t have to copy a RawSignal.

        Additionally, even if you don’t use emplace_back in such a circumstance, the compiler will generally perform copy elision and optimise your code to do exactly the same as what would happen if you *did* use emplace_back.

  8. October 25, 2013 at 8:29 am

    Please see the update.

    • October 25, 2013 at 1:53 pm

      Your compiler would probably end up eliding the copies and constructing in place if dets contained CfarCrossings rather than unique_ptr. When I first discovered copy elision I was freaked out by how good modern compilers are at it.

      • October 25, 2013 at 2:16 pm

        Once again, good point Abigail. When I’m writing app code, I get uncomfortable when I think too much about compiler implementations and making assumptions about what it should do under the covers. Thanks 🙂

  9. Guy Davidson
    November 14, 2013 at 5:42 am

    If you make CfarDetState constructible from (CfarCrossing, int) could you call estd::make_unique in the emplace_back call? Any exception would be thrown before emplace_back was called.

    • November 14, 2013 at 7:54 am

      Yes, I could and I will. Thanks Guy!

  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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: