1

I have a method

structA
{
     shared_ptr<B> m_b;

     // 2 options to set m_b

     void setB1(shared_ptr<B> b)
     {
         m_b = move(b);
     }

     void setB2(shared_ptr<B> const&b)
     {
         m_b = b;
     }
};

Which one is better in performance? They both do copies if I call setB1 as setB1(b) other than setB1(move(b)). I am more concerned about its performance when b can be nullified and b can only be copied.

My Testing Answers on VC2015:

setB1 is faster than setB2

  • by 30% for lvalue b
  • by 8% for rvalue b
user1899020
  • 13,167
  • 21
  • 79
  • 154
  • 3
    Presumably you intended for the type of `m_b` to be `shared_ptr` and not just `B`. – Bill Lynch Jul 16 '16 at 15:30
  • 2
    These two do different things: copy creates new `shared_ptr` pointing to the same thing, moving nullifies original `shared_ptr`. Rather than about performance, you should think about what your goal is. – GingerPlusPlus Jul 16 '16 at 15:43
  • @GingerPlusPlus : Moving nullifies the local `b` object which is destroyed upon function exit anyway. – ildjarn Jul 16 '16 at 15:47
  • @GingerPlusPlus They both do copies if I call `setB1` as `setB1(b)` other than `setB1(move(b))`. I am more concerned about its performance, for example when b can be nullified. – user1899020 Jul 16 '16 at 16:28

2 Answers2

0

The difference in performance is going to be tiny here.

One of them creates a copy into the argument, then moves out of it.

The other refers to an external copy in the argument, and copies out of it.

If you call the first with a shared ptr creating function (like make_shared), you'll get 1 move and 0 copies and 1 trivial destroy. For the second, you get 0 moves and 1 copy and 1 non-trivial destroy.

If you call the first just passing in some random shared ptr by value, you get 1 copy and 1 move and 1 trivial destroy. In the second, you get 1 copy and 0 moves.

Copying a shared ptr is an atomic increment. Trivial destroy is a branch. Non-trivial is a branch and an atomic decrement. Moving a shared ptr is just setting some pointers.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • The reason I downvoted is the unsupported assertion that "The difference in performance is going to be tiny". I've seen that shared_ptr can add significant overhead in some cases, mostly because copying the shared_ptr causes atomic operations that can hit both single and multi-thread performance. OP' small perf test reflects that. Moving the shared_ptr is clearly preferable when possible. – aberaud May 24 '21 at 16:33
  • @aber I ipvoted yours for the same reason. Except unless you probably shouldn't normally be using shared ptr if almost every use is a creation use. ;) – Yakk - Adam Nevraumont May 24 '21 at 18:16
0

The answer is Yes, and setB1 is generally better.

  • Copying a shared-pointer makes expensive (slow) atomic increment/decrement operations on the reference count.

  • Moving a shared-pointer is almost free and is basically just copying a pointer non-atomically (could be a single assembly instruction).

In your sample, setB1 provides the user the choice to either move or copy the shared_ptr, allowing for optimal performance when a move is possible for the user, while setB2 forces a copy.

See a detailed explanation for the performance difference here: Why would I std::move an std::shared_ptr?

aberaud
  • 909
  • 1
  • 11
  • 24