views:

595

answers:

5
+1  A: 

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.

Chris H
No, you're not safer. If you have 2 differents pools of `shared_ptr` pointing to the same physical object, one pool will delete it before the other...
Matthieu M.
+16  A: 

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.
280Z28
thats what I do - there are safety issues here but they are corner cases. I would rather have a slightly slower correct app than a dead fast app. Was your test app a testbed or the real code ?
pm100
@pm100: Note that I'm passing by *const* reference, not just by reference, which prevents most of the safety issues while offering a significant performance boost. When the alternative is passing the raw pointer contents of the shared_ptr, this method is safer.
280Z28
Artem
280Z28
+2  A: 

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.

Alex Farber
A: 

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.

dash-tom-bang
Did you mean to write f(ds) and g(ds)? I agree that caching the dereferenced value outside the loop would increase performance. Thanks for the suggestion.
Artem
ha hah yes I did. Edit should reflect that.
dash-tom-bang
+2  A: 

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.

Kylotan
Memory locality does not reduce the number of function calls but still usually speed things up.
Matthieu M.