The cost of passing by shared_ptr

。_饼干妹妹 提交于 2019-11-28 15:31:10

Always pass your shared_ptr by const reference:

void f(const shared_ptr<Dataset const>& pds) {...} 
void g(const shared_ptr<Dataset const>& pds) {...} 

Edit: Regarding the safety issues mentioned by others:

  • When using shared_ptr heavily throughout an application, passing by value will take up a tremendous amount of time (I've seen it go 50+%).
  • Use const T& instead of const shared_ptr<T const>& when the argument shall not be null.
  • Using const shared_ptr<T const>& is safer than const T* when performance is an issue.

You need shared_ptr only to pass it to functions/objects which keep it for future use. For example, some class may keep shared_ptr for using in an worker thread. For simple synchronous calls it's quite enough to use plain pointer or reference. shared_ptr should not replace using plain pointers completely.

If you're not using make_shared, could you give that a go? By locating the reference count and the object in the same area of memory you may see a performance gain associated with cache coherency. Worth a try anyway.

Any object creation and destruction, especially redundant object creation and destruction, should be avoided in performance-critical applications.

Consider what shared_ptr is doing. Not only is it creating a new object and filling it in, but it's also referencing the shared state to increment reference information, and the object itself presumably lives somewhere else completely which is going to be nightmarish on your cache.

Presumably you need the shared_ptr (because if you could get away with a local object you wouldn't allocate one off of the heap), but you could even "cache" the result of the shared_ptr dereference:

void fn(shared_ptr< Dataset > pds)
{
   Dataset& ds = *pds;

   for (i = 0; i < 1000; ++i)
   {
      f(ds);
      g(ds);
   }
}

...because even *pds requires hitting more memory than is absolutely necessary.

It sounds like you really know what you're doing. You've profiled your application, and you know exactly where cycles are being used. You understand that calling the constructor to a reference counting pointer is expensive only if you do it constantly.

The only heads up I can give you is: suppose inside function f(t *ptr), if you call another function that uses shared pointers, and you do other(ptr) and other makes a shared pointer of the raw pointer. When that second shared pointers' reference count hits 0 then you have effectively deleted your object....even though you didn't want to. you said you used reference counting pointers a lot, so you have to watch out for corner cases like that.

EDIT: You can make the destructor private, and only a friend of the shared pointer class, so that way the destructor can only be called by a shared pointer, then you're safe. Doesn't prevent multiple deletions from shared pointers. As per comment from Mat.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!