Zero-malloc memory pool for Boost Intrusive smart pointers
Hi all, I have developed for my needs a C++11 memory pool that can provides C++ object instances in the form of boost::intrusive_ptr<> that automatically return to the pool once their refcount goes to zero. I found the need for such memory pool to have a zero-malloc memory pool algorithm for some performance-oriented applications that were relying on std::shared_ptr<>. Here's the link in case anybody is interested: https://github.com/f18m/boost-intrusive-pool with some (initial) benchmark as well. I thought that this may be useful to anybody else already using Boost intrusive pointers as well... Any comment is welcome! Francesco Montorsi
On 22/03/2019 13:09, Francesco wrote:
I have developed for my needs a C++11 memory pool that can provides C++ object instances in the form of boost::intrusive_ptr<> that automatically return to the pool once their refcount goes to zero.
I found the need for such memory pool to have a zero-malloc memory pool algorithm for some performance-oriented applications that were relying on std::shared_ptr<>.
Here's the link in case anybody is interested: https://github.com/f18m/boost-intrusive-pool with some (initial) benchmark as well.
Did you look at std::make_shared<T>? It avoids the double allocation for the control block that you're complaining about. (Although you couldn't use it to allocate an arena block in this case -- while it does support array allocation, there is then only one control block for the whole array.) Also the use of default construction and explicit recycle methods (and requiring boost_intrusive_pool_item as a base class) is ugly. You should be able to instead keep your pool_item type internal (not exposed to the consumer) and use aligned_storage to hold uninitialised storage for the actual item data, calling T's constructor and destructor as needed to allocate and recycle the slot. (Or just wrap it in std/boost::optional, although that can introduce some nesting ambiguities.)
Hi Gavin, Il giorno ven 22 mar 2019 alle ore 05:44 Gavin Lambert via Boost-users < boost-users@lists.boost.org> ha scritto:
Did you look at std::make_shared<T>? It avoids the double allocation for the control block that you're complaining about. (Although you couldn't use it to allocate an arena block in this case -- while it does support array allocation, there is then only one control block for the whole array.)
Yes I know about std::make_shared<> and indeed I was using that solution before implementing the memory pool based on intrusive pointers. The problem is that in my workload I have several threads that need to get a smart pointer to a fairly-big object every time they process input data and they will release it later. Now allocating with std::make_shared<> a new object on every input data was causing my threads to do lots of mallocs/sec and that became the performace limiting factor. The use of a memory pool instead allows me to do a very large memory allocation from time to time (in blocks of e.g. 1024 items) and still have each single item of the memory pool independently refcounted. This usage pattern is not possible with std::shared_ptr<> and that's why I switched to boost::intrusive_ptr<> instead.
Also the use of default construction and explicit recycle methods (and requiring boost_intrusive_pool_item as a base class) is ugly.
You should be able to instead keep your pool_item type internal (not exposed to the consumer) and use aligned_storage to hold uninitialised storage for the actual item data, calling T's constructor and destructor as needed to allocate and recycle the slot.
My first implementation of the memory pool indeed was using C++11 perfect forwarding to call the ctor/dtor of type T when the item was pulled out the pool or was returning to it. I later removed such feature because I realized that calling the ctor of the items to recycle produced issues with classes having multiple inheritance IIRC. I don't think it's safe and sane to call the ctor of the same instance multiple times... I know that the implementation may be possibly refined from a C++/template style point of view but regarding performances I verified that it satifies well my needs and allows to write code using smart pointers that has zero-malloc when the memory pool has expanded to contain enough elements to handle the worst-simultaneous-active items... (Or just wrap it in
std/boost::optional, although that can introduce some nesting ambiguities.)
This is not very clear to me... but I will take a look at std::optional. Thanks for the suggestions though! Francesco
participants (2)
-
Francesco
-
Gavin Lambert