Passing std:: or boost::shared_ptr as a reference to a function
I was playing with PVS-Studio static analysis tool recently, and it suggested me to replace passing of const std::shared_ptr<type> arg to const std::shared_ptr<type>& arg.
I perfectly understand that it is a good way to optimize performance as when I pass shared_ptrs by value there are increments / decrements of reference counters which are interlocked operations.
So the question is - is there any reason I should not do such optimization? Is there any corner case I did not consider?
I'm now trying to see it is makes sense to optimize this way one heavily-used interface, so would appreciate comments.
Re: Passing std:: or boost::shared_ptr as a reference to a function
References are often implemented as pointers by the compiler (although this is not required). If they are, then every dereference within that function will incurr an extra indirection cost.
This is most likely not significant compared to the cost of copying the shared_ptr, but depending on your usage it's possible it could be. You will need to profile your code both ways to find out.
Re: Passing std:: or boost::shared_ptr as a reference to a function
If the function does not store the pointer somewhere for a future use, just pass plain pointer to it.
Re: Passing std:: or boost::shared_ptr as a reference to a function
Quote:
Originally Posted by
Fahrenheit
So the question is - is there any reason I should not do such optimization? Is there any corner case I did not consider?
If you do this you no longer have a perfectly fine smart pointer. It may cause the problems you wanted to avoid by introducing a smart pointer in the first place. You've planted a mine ready to go off. It beats the purpose of using smart pointers.
If you really need this optimization I suggest you instead pass the object the smart pointer points to by const reference, like
Code:
void foo(const type& arg);
//
std::shared_ptr<type> p;
//
foo(*p);
It's assumed that foo is a low-level function/method that has been identified as a performance bottleneck. It should also be inlined.
It's also a good idea to reconsider whether you really need this smart pointer. If it's used very locally in low-level performance sensitive code and the ownership is perfectly clear it may be overkill.
Re: Passing std:: or boost::shared_ptr as a reference to a function
Lindley, sure, I will yet check out which is actually faster with some benchmark. It is really interesting question. Btw, even if they will tie on single-threaded test in multi-threaded reference to shared_ptr will still be the winner due to less interference with other threads.
Nuzzle, what kind of mine you are talking about? I understand what in this case I will get two copies of the object (one up the stack, and one down the stack) for only 1 reference count, but I can't think of any specific scenario that will break under these circumstances (synchronous call to some function)
Re: Passing std:: or boost::shared_ptr as a reference to a function
Quote:
Originally Posted by
Fahrenheit
what kind of mine you are talking about?
I cannot speak for nuzzle, but given that it makes no sense to pass a shared_ptr to a function not supposed to store somewhere a copy ( both in terms of efficiency and semantics ), I wonder in what reasonable scenario a function that acts by propagating ownership of an object to other objects happens to be in performance sensitive code.
For example, consider an high performance algorithm passing back and forth shared pointers to objects between, say, a number of containers; I think you can always refactor the algorithm in such a way to use plain pointers during calcualtions mantaining a central repository of unique or shared smart pointers to be cleaned/mantained strictly before or after the algorithm performance sensitive core. Moreover, I think such a refactoring would be actually cleaner, being more correct from a semantical POV.
Quote:
Originally Posted by Lindley
If they are, then every dereference within that function will incurr an extra indirection cost.
note that a function taking a const share_ptr& can always immediately make a copy from it, hence avoiding such an issue, especially if the function is inlined. The same flexibility cannot be said for a function taking a shared_ptr directly.
Re: Passing std:: or boost::shared_ptr as a reference to a function
I was thinking more about it, and if the function that gets that const shared_ptr<T>& as argument wants to store it somewhere for future use it is absolutely legal, as the stored copy will get its own reference counted.
Of course if the stored copy is, again, a reference then it won't - but this is a bad design decision to store a reference to some external object - give or take share_ptr
Also - I did more or less realistic benchmark of passing shared_ptr, and following are my conclusions:
1) Passing by reference is about 10 times faster than passing by value
2) Similar cost ratio is for both std::shared_ptr and boost::shared_ptr, but boost's smart pointer is about 15% faster when passed by value
3) It doesn't matter if the object was created with new T(), or with make_shared<T>() - performance numbers are the same
Re: Passing std:: or boost::shared_ptr as a reference to a function
Quote:
Originally Posted by
nuzzle
If you do this you no longer have a perfectly fine smart pointer.
I've changed my mind on this topic. It's perfectly fine to pass a shared_ptr by const reference. In fact it's even best practice.
If the code is sequential or if it's concurrent and properly synchronized it doesn't matter whether you pass by value or by const reference. Both are correct and will work equally fine. So the choise becomes a matter of which is most efficient and it will be const reference.
From a purely conceptual standpoint it still makes sense to treat a shared_ptr exactly like a bald pointer that is passing it by value. But since also a bald pointer would be passed by const reference if that were faster I see no reason why a shared_ptr couldn't be passed by const reference now that this actually is faster. It's the same consideration one makes for any type when deciding how to pass it.
For an excellent clarification of this issue by three top C++ experts see here,
http://channel9.msdn.com/Shows/Going...sk-Us-Anything
It's at [04:34] into the discussion.
In my view knowing that a shared_ptr can be passed efficiently by const reference without causing any harm to the integrity of a program makes it an even better choise over bald pointers. This is always true in sequential code and it's true in concurrent code as long as the shared_ptr implementation sports an atomic reference counter and the code is also otherwise properly synchronized.
Re: Passing std:: or boost::shared_ptr as a reference to a function
Quote:
Originally Posted by
nuzzle
I've changed my mind on this topic. It's perfectly fine to pass a shared_ptr by const reference. In fact it's even best practice.
Thanks for posting details. Now it feels better as everyone agrees (and this is backed up such C++ gurus on channel 9 video)
Re: Passing std:: or boost::shared_ptr as a reference to a function
Quote:
Originally Posted by
nuzzle
I've changed my mind on this topic. It's perfectly fine to pass a shared_ptr by const reference. In fact it's even best practice.
IMO, if it has a constructor, you pass it by const_reference. Simple as that.
If the function then wants to do something mutable with the object, it's the function's own job to create a copy locally. The function signature shouldn't burden the caller with such petty detail.
That said, passing a shared_ptr by const reference doesn't prevent you from mutating the targeted object anyways, it just prevents you from re-seating it.
BTW, most algorithms will swap their objects (and even move them in C++11). AFAIK, there is no extra cost to do this operation on shared_ptr over a naked one.