mirror of
https://github.com/boostorg/container.git
synced 2026-02-26 04:32:21 +00:00
* Fixed adaptive pool bugs
* Improved adaptive pool testing enabling invariant checking in debug mode. * New compile-time and runtime calculated parameters for adaptive_pool. * New test comparing set with default allocator or adaptive pool
This commit is contained in:
@@ -117,9 +117,9 @@ class adaptive_pool
|
||||
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
|
||||
private:
|
||||
//!Not assignable from related adaptive_pool
|
||||
template<class T2, unsigned Version2, std::size_t N2, std::size_t F2>
|
||||
template<class T2, std::size_t N2, std::size_t F2, std::size_t O2, unsigned Version2>
|
||||
adaptive_pool& operator=
|
||||
(const adaptive_pool<T2, Version2, N2, F2>&);
|
||||
(const adaptive_pool<T2, N2, F2, O2, Version2>&);
|
||||
|
||||
#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
|
||||
|
||||
@@ -341,6 +341,266 @@ class adaptive_pool
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template < class T
|
||||
, std::size_t NodesPerBlock = ADP_nodes_per_block
|
||||
, std::size_t MaxFreeBlocks = ADP_max_free_blocks
|
||||
, std::size_t OverheadPercent = ADP_overhead_percent
|
||||
, unsigned Version = 2
|
||||
>
|
||||
class private_adaptive_pool
|
||||
{
|
||||
//!If Version is 1, the allocator is a STL conforming allocator. If Version is 2,
|
||||
//!the allocator offers advanced expand in place and burst allocation capabilities.
|
||||
public:
|
||||
typedef unsigned int allocation_type;
|
||||
typedef private_adaptive_pool
|
||||
<T, NodesPerBlock, MaxFreeBlocks, OverheadPercent
|
||||
BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I Version)
|
||||
> self_t;
|
||||
|
||||
static const std::size_t nodes_per_block = NodesPerBlock;
|
||||
static const std::size_t max_free_blocks = MaxFreeBlocks;
|
||||
static const std::size_t overhead_percent = OverheadPercent;
|
||||
static const std::size_t real_nodes_per_block = NodesPerBlock;
|
||||
|
||||
BOOST_CONTAINER_DOCIGN(BOOST_STATIC_ASSERT((Version <=2)));
|
||||
|
||||
typedef dtl::private_adaptive_node_pool
|
||||
<sizeof(T), NodesPerBlock, MaxFreeBlocks, OverheadPercent> pool_t;
|
||||
pool_t m_pool;
|
||||
|
||||
public:
|
||||
//-------
|
||||
typedef T value_type;
|
||||
typedef T * pointer;
|
||||
typedef const T * const_pointer;
|
||||
typedef typename ::boost::container::
|
||||
dtl::unvoid_ref<T>::type reference;
|
||||
typedef typename ::boost::container::
|
||||
dtl::unvoid_ref<const T>::type const_reference;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
typedef boost::container::dtl::
|
||||
version_type<self_t, Version> version;
|
||||
|
||||
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
|
||||
typedef boost::container::dtl::
|
||||
basic_multiallocation_chain<void*> multiallocation_chain_void;
|
||||
typedef boost::container::dtl::
|
||||
transform_multiallocation_chain
|
||||
<multiallocation_chain_void, T> multiallocation_chain;
|
||||
#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
|
||||
|
||||
//!Obtains private_adaptive_pool from
|
||||
//!private_adaptive_pool
|
||||
template<class T2>
|
||||
struct rebind
|
||||
{
|
||||
typedef private_adaptive_pool
|
||||
< T2
|
||||
, NodesPerBlock
|
||||
, MaxFreeBlocks
|
||||
, OverheadPercent
|
||||
BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I Version)
|
||||
> other;
|
||||
};
|
||||
|
||||
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
|
||||
private:
|
||||
//!Not assignable from related private_adaptive_pool
|
||||
template<class T2, std::size_t N2, std::size_t F2, std::size_t O2, unsigned Version2>
|
||||
private_adaptive_pool& operator=
|
||||
(const private_adaptive_pool<T2, N2, F2, O2, Version2>&);
|
||||
#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
|
||||
|
||||
public:
|
||||
//!Default constructor
|
||||
private_adaptive_pool() BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{}
|
||||
|
||||
//!Copy constructor from other private_adaptive_pool.
|
||||
private_adaptive_pool(const private_adaptive_pool &) BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{}
|
||||
|
||||
//!Copy constructor from related private_adaptive_pool.
|
||||
template<class T2>
|
||||
private_adaptive_pool
|
||||
(const private_adaptive_pool<T2, NodesPerBlock, MaxFreeBlocks, OverheadPercent
|
||||
BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I Version)> &) BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{}
|
||||
|
||||
//!Destructor
|
||||
~private_adaptive_pool() BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{}
|
||||
|
||||
//!Returns the number of elements that could be allocated.
|
||||
//!Never throws
|
||||
size_type max_size() const BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{ return size_type(-1)/sizeof(T); }
|
||||
|
||||
//!Allocate memory for an array of count elements.
|
||||
//!Throws std::bad_alloc if there is no enough memory
|
||||
pointer allocate(size_type count, const void * = 0)
|
||||
{
|
||||
if(BOOST_UNLIKELY(count > this->max_size()))
|
||||
boost::container::throw_bad_alloc();
|
||||
|
||||
if(Version == 1 && count == 1){
|
||||
return pointer(static_cast<T*>(m_pool.allocate_node()));
|
||||
}
|
||||
else{
|
||||
return static_cast<pointer>(dlmalloc_malloc(count*sizeof(T)));
|
||||
}
|
||||
}
|
||||
|
||||
//!Deallocate allocated memory.
|
||||
//!Never throws
|
||||
void deallocate(const pointer &ptr, size_type count) BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{
|
||||
(void)count;
|
||||
if(Version == 1 && count == 1){
|
||||
m_pool.deallocate_node(ptr);
|
||||
}
|
||||
else{
|
||||
dlmalloc_free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
pointer allocation_command(allocation_type command,
|
||||
size_type limit_size,
|
||||
size_type &prefer_in_recvd_out_size,
|
||||
pointer &reuse)
|
||||
{
|
||||
pointer ret = this->priv_allocation_command(command, limit_size, prefer_in_recvd_out_size, reuse);
|
||||
if(BOOST_UNLIKELY(!ret && !(command & BOOST_CONTAINER_NOTHROW_ALLOCATION)))
|
||||
boost::container::throw_bad_alloc();
|
||||
return ret;
|
||||
}
|
||||
|
||||
//!Returns maximum the number of objects the previously allocated memory
|
||||
//!pointed by p can hold.
|
||||
size_type size(pointer p) const BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{ return dlmalloc_size(p); }
|
||||
|
||||
//!Allocates just one object. Memory allocated with this function
|
||||
//!must be deallocated only with deallocate_one().
|
||||
//!Throws bad_alloc if there is no enough memory
|
||||
pointer allocate_one()
|
||||
{
|
||||
return (pointer)m_pool.allocate_node();
|
||||
}
|
||||
|
||||
//!Allocates many elements of size == 1.
|
||||
//!Elements must be individually deallocated with deallocate_one()
|
||||
void allocate_individual(std::size_t num_elements, multiallocation_chain &chain)
|
||||
{
|
||||
m_pool.allocate_nodes(num_elements, static_cast<typename pool_t::multiallocation_chain&>(chain));
|
||||
}
|
||||
|
||||
//!Deallocates memory previously allocated with allocate_one().
|
||||
//!You should never use deallocate_one to deallocate memory allocated
|
||||
//!with other functions different from allocate_one(). Never throws
|
||||
void deallocate_one(pointer p) BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{
|
||||
m_pool.deallocate_node(p);
|
||||
}
|
||||
|
||||
void deallocate_individual(multiallocation_chain &chain) BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{
|
||||
m_pool.deallocate_nodes(chain);
|
||||
}
|
||||
|
||||
//!Allocates many elements of size elem_size.
|
||||
//!Elements must be individually deallocated with deallocate()
|
||||
void allocate_many(size_type elem_size, std::size_t n_elements, multiallocation_chain &chain)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(( Version > 1 ));
|
||||
if(BOOST_UNLIKELY(!dlmalloc_multialloc_nodes
|
||||
(n_elements, elem_size*sizeof(T), DL_MULTIALLOC_DEFAULT_CONTIGUOUS, reinterpret_cast<dlmalloc_memchain *>(&chain)))){
|
||||
boost::container::throw_bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
//!Allocates n_elements elements, each one of size elem_sizes[i]
|
||||
//!Elements must be individually deallocated with deallocate()
|
||||
void allocate_many(const size_type *elem_sizes, size_type n_elements, multiallocation_chain &chain)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(( Version > 1 ));
|
||||
if(BOOST_UNLIKELY(!dlmalloc_multialloc_arrays
|
||||
(n_elements, elem_sizes, sizeof(T), DL_MULTIALLOC_DEFAULT_CONTIGUOUS, reinterpret_cast<dlmalloc_memchain *>(&chain)))){
|
||||
boost::container::throw_bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate_many(multiallocation_chain &chain) BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{
|
||||
dlmalloc_multidealloc(reinterpret_cast<dlmalloc_memchain *>(&chain));
|
||||
}
|
||||
|
||||
//!Deallocates all free blocks of the pool
|
||||
void deallocate_free_blocks() BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{
|
||||
m_pool.deallocate_free_blocks();
|
||||
}
|
||||
|
||||
//!Swaps allocators. Does not throw. If each allocator is placed in a
|
||||
//!different memory segment, the result is undefined.
|
||||
friend void swap(private_adaptive_pool &, private_adaptive_pool &) BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{}
|
||||
|
||||
//!An allocator always compares to true, as memory allocated with one
|
||||
//!instance can be deallocated by another instance
|
||||
friend bool operator==(const private_adaptive_pool &, const private_adaptive_pool &) BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{ return true; }
|
||||
|
||||
//!An allocator always compares to false, as memory allocated with one
|
||||
//!instance can be deallocated by another instance
|
||||
friend bool operator!=(const private_adaptive_pool &, const private_adaptive_pool &) BOOST_NOEXCEPT_OR_NOTHROW
|
||||
{ return false; }
|
||||
|
||||
private:
|
||||
pointer priv_allocation_command
|
||||
(allocation_type command, std::size_t limit_size
|
||||
,size_type &prefer_in_recvd_out_size, pointer &reuse_ptr)
|
||||
{
|
||||
std::size_t const preferred_size = prefer_in_recvd_out_size;
|
||||
dlmalloc_command_ret_t ret = {0 , 0};
|
||||
if(BOOST_UNLIKELY(limit_size > this->max_size() || preferred_size > this->max_size())){
|
||||
return pointer();
|
||||
}
|
||||
std::size_t l_size = limit_size*sizeof(T);
|
||||
std::size_t p_size = preferred_size*sizeof(T);
|
||||
std::size_t r_size;
|
||||
{
|
||||
void* reuse_ptr_void = reuse_ptr;
|
||||
ret = dlmalloc_allocation_command(command, sizeof(T), l_size, p_size, &r_size, reuse_ptr_void);
|
||||
reuse_ptr = ret.second ? static_cast<T*>(reuse_ptr_void) : 0;
|
||||
}
|
||||
prefer_in_recvd_out_size = r_size/sizeof(T);
|
||||
return (pointer)ret.first;
|
||||
}
|
||||
};
|
||||
|
||||
} //namespace container {
|
||||
} //namespace boost {
|
||||
|
||||
|
||||
@@ -39,17 +39,6 @@ namespace boost {
|
||||
namespace container {
|
||||
namespace dtl {
|
||||
|
||||
template<bool AlignOnly>
|
||||
struct select_private_adaptive_node_pool_impl
|
||||
{
|
||||
typedef boost::container::dtl::
|
||||
private_adaptive_node_pool_impl
|
||||
< fake_segment_manager
|
||||
, unsigned(AlignOnly)*::boost::container::adaptive_pool_flag::align_only
|
||||
| ::boost::container::adaptive_pool_flag::size_ordered | ::boost::container::adaptive_pool_flag::address_ordered
|
||||
> type;
|
||||
};
|
||||
|
||||
//!Pooled memory allocator using an smart adaptive pool. Includes
|
||||
//!a reference count but the class does not delete itself, this is
|
||||
//!responsibility of user classes. Node size (NodeSize) and the number of
|
||||
@@ -60,24 +49,38 @@ template< std::size_t NodeSize
|
||||
, std::size_t OverheadPercent
|
||||
>
|
||||
class private_adaptive_node_pool
|
||||
: public select_private_adaptive_node_pool_impl<(OverheadPercent == 0)>::type
|
||||
: public private_adaptive_node_pool_impl_ct
|
||||
< fake_segment_manager
|
||||
, MaxFreeBlocks
|
||||
, NodeSize
|
||||
, NodesPerBlock
|
||||
, OverheadPercent
|
||||
, unsigned(OverheadPercent == 0)*::boost::container::adaptive_pool_flag::align_only
|
||||
| ::boost::container::adaptive_pool_flag::size_ordered
|
||||
| ::boost::container::adaptive_pool_flag::address_ordered
|
||||
>
|
||||
{
|
||||
typedef typename select_private_adaptive_node_pool_impl<OverheadPercent == 0>::type base_t;
|
||||
typedef private_adaptive_node_pool_impl_ct
|
||||
< fake_segment_manager
|
||||
, MaxFreeBlocks
|
||||
, NodeSize
|
||||
, NodesPerBlock
|
||||
, OverheadPercent
|
||||
, unsigned(OverheadPercent == 0)*::boost::container::adaptive_pool_flag::align_only
|
||||
| ::boost::container::adaptive_pool_flag::size_ordered
|
||||
| ::boost::container::adaptive_pool_flag::address_ordered
|
||||
> base_t;
|
||||
|
||||
//Non-copyable
|
||||
private_adaptive_node_pool(const private_adaptive_node_pool &);
|
||||
private_adaptive_node_pool &operator=(const private_adaptive_node_pool &);
|
||||
|
||||
public:
|
||||
typedef typename base_t::multiallocation_chain multiallocation_chain;
|
||||
static const std::size_t nodes_per_block = NodesPerBlock;
|
||||
|
||||
//!Constructor. Never throws
|
||||
private_adaptive_node_pool()
|
||||
: base_t(0
|
||||
, NodeSize
|
||||
, NodesPerBlock
|
||||
, MaxFreeBlocks
|
||||
, (unsigned char)OverheadPercent)
|
||||
: base_t(0)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -94,6 +94,34 @@ inline Integer upper_power_of_2(const Integer & A)
|
||||
return power_of_2;
|
||||
}
|
||||
|
||||
template <typename Integer, bool Loop = true>
|
||||
struct upper_power_of_2_loop_ct
|
||||
{
|
||||
|
||||
template <Integer I, Integer P>
|
||||
struct apply
|
||||
{
|
||||
static const Integer value =
|
||||
upper_power_of_2_loop_ct<Integer, (I > P*2)>::template apply<I, P*2>::value;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Integer>
|
||||
struct upper_power_of_2_loop_ct<Integer, false>
|
||||
{
|
||||
template <Integer I, Integer P>
|
||||
struct apply
|
||||
{
|
||||
static const Integer value = P;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Integer, Integer I>
|
||||
struct upper_power_of_2_ct
|
||||
{
|
||||
static const Integer value = upper_power_of_2_loop_ct<Integer, (I > 1)>::template apply<I, 2>::value;
|
||||
};
|
||||
|
||||
//This function uses binary search to discover the
|
||||
//highest set bit of the integer
|
||||
inline std::size_t floor_log2 (std::size_t x)
|
||||
@@ -114,6 +142,32 @@ inline std::size_t floor_log2 (std::size_t x)
|
||||
return log2;
|
||||
}
|
||||
|
||||
template<std::size_t I1, std::size_t I2>
|
||||
struct gcd_ct
|
||||
{
|
||||
static const std::size_t Max = I1 > I2 ? I1 : I2;
|
||||
static const std::size_t Min = I1 < I2 ? I1 : I2;
|
||||
static const std::size_t value = gcd_ct<Min, Max % Min>::value;
|
||||
};
|
||||
|
||||
template<std::size_t I1>
|
||||
struct gcd_ct<I1, 0>
|
||||
{
|
||||
static const std::size_t value = I1;
|
||||
};
|
||||
|
||||
template<std::size_t I1>
|
||||
struct gcd_ct<0, I1>
|
||||
{
|
||||
static const std::size_t value = I1;
|
||||
};
|
||||
|
||||
template<std::size_t I1, std::size_t I2>
|
||||
struct lcm_ct
|
||||
{
|
||||
static const std::size_t value = I1 * I2 / gcd_ct<I1, I2>::value;
|
||||
};
|
||||
|
||||
} // namespace dtl
|
||||
} // namespace container
|
||||
} // namespace boost
|
||||
|
||||
Reference in New Issue
Block a user