Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2006-01-30 16:31:19


"David Maisonave" <dmaisonave_at_[hidden]> writes:

> "David Abrahams" <dave_at_[hidden]> wrote in message
> news:<874q3ld907.fsf_at_[hidden]>...
>> "David Maisonave" <boost_at_[hidden]> writes:
>>
>> > "Daniel Wallin" <dalwan01_at_[hidden]> wrote in message
>> > news:<drkmls$kb5$1_at_[hidden]>...
>>
>> >> It's in the FAQ:
>> >>
>> >> http://www.boost.org/libs/smart_ptr/shared_ptr.htm#FAQ
>> >>
>> >> Q. Why doesn't shared_ptr use a linked list implementation?
>> >>
>> >> A. A linked list implementation does not offer enough advantages
> to
>> >> offset the added cost of an extra pointer. See timings page.
> In
>> >> addition, it is expensive to make a linked list implementation
>> >> thread safe.
>> >
>> >
>> > You can avoid having to make the implementation thread safe by
>> > making the pointee thread safe.
>>
>> One of us here understands nothing about the problem. I don't know
>> that much about threading, but I think I have a grip on this issue at
>> least. As I understand the problem, if two neighboring shared_ptr's
>> in a reference-linked chain are destroyed at the same time, they will
>> be modifying the same pointer values simultaneously -- without a lock
>> in your case. I don't see how making the pointee threadsafe is going
>> to help one bit.
>
>
> IMHO, you don't fully understand my propose solution.
> Please look at the current smart pointer locking method:
> http://code.axter.com/smart_ptr.h
> By using intrusive lock logic, you can lock the pointee, and thereby
> locking all the shared_ptr objects.
> Here's the smart pointer destructor:
> inline ~smart_ptr() throw()
> {
> m_ownership_policy.destructor_lock_policy(m_type);
> CHECKING_POLICY::before_release(m_type);
> m_ownership_policy.release(m_type, m_clone_fct,
> m_ownership_policy);
> CHECKING_POLICY::after_release(m_type);
> }
>
> There's similar logic in constructor and assignment operator.
> This should work on all three main types of reference policies, to
> include reference-link.
> Do you understand intrusive logic? You need to fully understand how
> intrusive logic works, in order to understand the method.

It sounds like you're saying that, essentially, all the shared_ptrs
that point to the same object share a single mutex, and in your case,
that mutex happens to be embedded in the pointee, which is why you
call it "an intrusive lock." Have I got that right?

IIUC, the thread-safety problem with reference-linked implementation
isn't that so much that it's hard to achieve -- anyone can use a
shared mutex -- it's that it's hard to make a thread-safe
implementation efficient. That is to say, you pay for the cost of
locking and unlocking a mutex, and there's no way around it (**). Locking
and unlocking mutexes is way more expensive than performing the
lock-free operations used by boost::shared_ptr.

(**) Or so I thought:
http://www.cs.chalmers.se/~dcs/ConcurrentDataStructures/phd_chap7.pdf
seems to contradict that.

>> > This can be done by using an intrusive lock.
>> >
>> > On my test, reference-link is over 25% faster than reference-count
>> > logic for initialization. With BOOST_SP_USE_QUICK_ALLOCATOR defined,
>
>> > than reference-link is over 30% faster than reference-count logic
>> > for initialization. I get the above results using VC++ 7.1, and if I
>
>> > use the GNU 3.x compiler, than the difference is even greater in
>> > favor of reference-link logic.
>> > With the Borland compiler, the difference is about 22%
>>
>> Are you claiming that using BOOST_SP_USE_QUICK_ALLOCATOR actually
>> slows boost::shared_ptr down on all these compilers?
>
> Of course not....
> When you define BOOST_SP_USE_QUICK_ALLOCATOR, that does not just
> increase the performance of boost objects.
> It increases the performance for all object within the translation unit
> that has the define, and that is using allocators.
> So even though shared_ptr gets an increase performance boost, the
> smart_ptr gets an even greater performance boost, which increases the
> performance ratio.

Oh, I had no idea you were using the allocator for your
reference-linked smart pointers. I see no mention of that macro in
your header. Where do you use it?

Anyway, why would a reference-linked use any dynamic allocation at
all, aside from the pointee? If you're dynamically allocating the
pointee with the "quick allocator," you should be aware that
shared_ptr generally does not impose any allocation constraints on the
pointee, and to make a fair comparison you'd have to use a "quick
allocator" for the pointees in the shared_ptr test.

> If you have any thoughts, please feel free to inspect and perform the
> same test using the following code:
> http://code.axter.com/smart_ptr.h
> http://code.axter.com/shape_test.h
> http://code.axter.com/boost_ptr_containers/main.cpp
> http://code.axter.com/cow_ptr.h
> http://code.axter.com/copy_ptr.h
>
>
> I welcome inspection and critiques of my work.

I don't have time to read through your code right now... but I think
there's plenty to discuss before that's necessary :)

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk