Added options for small_vector: inplace_alignment and growth_factor. Fixes #47

This commit is contained in:
Ion Gaztañaga
2019-05-09 22:15:43 +02:00
parent 1e9b4a15eb
commit 4c348af1eb
6 changed files with 205 additions and 61 deletions

View File

@ -730,7 +730,7 @@ The configuration for [classref boost::container::static_vector static_vector] i
the last template parameter and defined using the utility class
[classref boost::container::static_vector_options static_vector_options]. The following parameters can be configured:
* [classref boost::container::alignment alignment]: the minimum alignment (in bytes) that the stored value type
* [classref boost::container::inplace_alignment inplace_alignment]: the minimum alignment (in bytes) that the stored value type
needs. This option allows static vectors that need non-default alignments, e.g., to be used in SIMD operations.
* [classref boost::container::throw_on_overflow throw_on_overflow]: A boolean that specifies if the
@ -746,6 +746,33 @@ used to customize `static_vector` container:
[endsect]
[section:configurable_small_vectors Configurable small vector]
The configuration for [classref boost::container::small_vector small_vector] is passed as
the last template parameter and defined using the utility class
[classref boost::container::small_vector_options small_vector_options]. The following parameters can be configured:
* [classref boost::container::inplace_alignment inplace_alignment]: the minimum alignment (in bytes) for the in-place storage
used to build the "small" number of elements. [*The alignment of the dynamic memory must be provided by the allocator
and it is not affected by this option].
* [classref boost::container::growth_factor growth_factor]: the growth policy of the vector.
The rate at which the capacity of a vector grows is implementation dependent and
implementations choose exponential growth in order to meet the amortized constant time requirement for push_back.
A higher growth factor will make it faster as it will require less data movement, but it will have a greater memory
impact (on average, more memory will be unused). A user can provide a custom implementation of the growth factor and some
predefined policies are available: [classref boost::container::growth_factor_50 growth_factor_50],
[classref boost::container::growth_factor_60 growth_factor_60] and
[classref boost::container::growth_factor_50 growth_factor_100].
See the following example to see how [classref boost::container::small_vector_options small_vector_options] can be
used to customize `small_vector` container:
[import ../example/doc_custom_small_vector.cpp]
[doc_custom_small_vector]
[endsect]
[endsect]
[section:extended_allocators Extended functionality: Extended allocators]
@ -1295,6 +1322,7 @@ use [*Boost.Container]? There are several reasons for that:
[section:release_notes_boost_1_71_00 Boost 1.71 Release]
* Fixed bugs:
* [@https://github.com/boostorg/container/pull/47 GitHub #47: ['"added alignment specification for small_vector"]].
* [@https://github.com/boostorg/container/issues/88 GitHub #88: ['"Implement C++17 MoveAssignable requirements for self-move assignments"]].
* [@https://github.com/boostorg/container/issues/107 GitHub #107: ['"Alignment ignored in resource_adaptor"]].
* [@https://github.com/boostorg/container/pull/109 GitHub #109: ['"Get rid of integer overflow in copy_move_algo.hpp (-fsanitize=integer)"]].
@ -1309,7 +1337,10 @@ use [*Boost.Container]? There are several reasons for that:
The block size/bytes can be be specified.
* [classref boost::container::static_vector static_vector] can now have options, using [classref boost::container::static_vector_options static_vector_options].
The alignment and throwing behaviour can be be specified.
Alignment and throwing behaviour can be be specified.
* [classref boost::container::small_vector small_vector] can now have options, using [classref boost::container::small_vector_options small_vector_options].
Alignment and growth factor can be be specified.
[endsect]

View File

@ -77,7 +77,7 @@ namespace container {
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
template<class T, class VoidAllocator>
template<class T, class VoidAllocator, class Options>
class small_vector_allocator;
namespace allocator_traits_detail {
@ -99,8 +99,8 @@ template<class T>
struct is_std_allocator< std::allocator<T> >
{ static const bool value = true; };
template<class T>
struct is_std_allocator< small_vector_allocator<T, std::allocator<T> > >
template<class T, class Options>
struct is_std_allocator< small_vector_allocator<T, std::allocator<T>, Options > >
{ static const bool value = true; };
template<class Allocator>

View File

@ -106,14 +106,20 @@ template <class T
,class Allocator = void >
class stable_vector;
template <class T, std::size_t Capacity, class Options = void>
template < class T
, std::size_t Capacity
, class Options = void>
class static_vector;
template <class T, class Allocator = void >
template < class T
, class Allocator = void
, class Options = void >
class small_vector_base;
template < class T, std::size_t N
, class Allocator = void >
template < class T
, std::size_t N
, class Allocator = void
, class Options = void >
class small_vector;
template <class T

View File

@ -45,8 +45,9 @@ namespace impl{
template <class T>
struct extract_version
: T::version
{};
{
typedef typename T::version type;
};
template <class T>
struct has_version
@ -69,7 +70,7 @@ struct version
template <class T>
struct version<T, true>
{
static const unsigned value = extract_version<T>::value;
static const unsigned value = extract_version<T>::type::value;
};
} //namespace impl

View File

@ -102,6 +102,65 @@ using tree_assoc_options_t = typename boost::container::tree_assoc_options<Optio
#endif
////////////////////////////////////////////////////////////////
//
//
// OPTIONS FOR ASSOCIATIVE HASH-BASED CONTAINERS
//
//
////////////////////////////////////////////////////////////////
#if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
template<bool StoreHash>
struct hash_opt
{
static const bool store_hash = StoreHash;
};
typedef hash_opt<false> hash_assoc_defaults;
#endif //!defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
//!This option setter specifies if node size is optimized
//!storing rebalancing data masked into pointers for ordered associative containers
BOOST_INTRUSIVE_OPTION_CONSTANT(store_hash, bool, Enabled, store_hash)
//! Helper metafunction to combine options into a single type to be used
//! by \c boost::container::hash_set, \c boost::container::hash_multiset
//! \c boost::container::hash_map and \c boost::container::hash_multimap.
//! Supported options are: \c boost::container::store_hash
#if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) || defined(BOOST_CONTAINER_VARIADIC_TEMPLATES)
template<class ...Options>
#else
template<class O1 = void, class O2 = void, class O3 = void, class O4 = void>
#endif
struct hash_assoc_options
{
/// @cond
typedef typename ::boost::intrusive::pack_options
< hash_assoc_defaults,
#if !defined(BOOST_CONTAINER_VARIADIC_TEMPLATES)
O1, O2, O3, O4
#else
Options...
#endif
>::type packed_options;
typedef hash_opt<packed_options::store_hash> implementation_defined;
/// @endcond
typedef implementation_defined type;
};
#if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES)
//! Helper alias metafunction to combine options into a single type to be used
//! by hash-based associative containers
template<class ...Options>
using hash_assoc_options_t = typename boost::container::hash_assoc_options<Options...>::type;
#endif
////////////////////////////////////////////////////////////////
//
//
@ -266,15 +325,15 @@ using vector_options_t = typename boost::container::vector_options<Options...>::
//! A value zero represents the natural alignment.
//!
//!\tparam Alignment An unsigned integer value. Must be power of two.
BOOST_INTRUSIVE_OPTION_CONSTANT(alignment, std::size_t, Alignment, alignment)
BOOST_INTRUSIVE_OPTION_CONSTANT(inplace_alignment, std::size_t, Alignment, inplace_alignment)
#if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
template<class GrowthType, std::size_t Alignment>
template<class GrowthType, std::size_t InplaceAlignment>
struct small_vector_opt
{
typedef GrowthType growth_factor_type;
static const std::size_t alignment = Alignment;
static const std::size_t inplace_alignment = InplaceAlignment;
};
typedef small_vector_opt<void, 0u> small_vector_null_opt;
@ -283,7 +342,7 @@ typedef small_vector_opt<void, 0u> small_vector_null_opt;
//! Helper metafunction to combine options into a single type to be used
//! by \c boost::container::small_vector.
//! Supported options are: \c boost::container::growth_factor and \c boost::container::alignment
//! Supported options are: \c boost::container::growth_factor and \c boost::container::inplace_alignment
#if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) || defined(BOOST_CONTAINER_VARIADIC_TEMPLATES)
template<class ...Options>
#else
@ -301,7 +360,7 @@ struct small_vector_options
#endif
>::type packed_options;
typedef small_vector_opt< typename packed_options::growth_factor_type
, packed_options::alignment> implementation_defined;
, packed_options::inplace_alignment> implementation_defined;
/// @endcond
typedef implementation_defined type;
};
@ -337,11 +396,11 @@ BOOST_INTRUSIVE_OPTION_CONSTANT(throw_on_overflow, bool, ThrowOnOverflow, throw_
#if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
template<bool ThrowOnOverflow, std::size_t Alignment>
template<bool ThrowOnOverflow, std::size_t InplaceAlignment>
struct static_vector_opt
{
static const bool throw_on_overflow = ThrowOnOverflow;
static const std::size_t alignment = Alignment;
static const std::size_t inplace_alignment = InplaceAlignment;
};
typedef static_vector_opt<true, 0u> static_vector_null_opt;
@ -350,7 +409,7 @@ typedef static_vector_opt<true, 0u> static_vector_null_opt;
//! Helper metafunction to combine options into a single type to be used
//! by \c boost::container::static_vector.
//! Supported options are: \c boost::container::throw_on_overflow and \c boost::container::alignment
//! Supported options are: \c boost::container::throw_on_overflow and \c boost::container::inplace_alignment
#if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) || defined(BOOST_CONTAINER_VARIADIC_TEMPLATES)
template<class ...Options>
#else
@ -368,7 +427,7 @@ struct static_vector_options
#endif
>::type packed_options;
typedef static_vector_opt< packed_options::throw_on_overflow
, packed_options::alignment> implementation_defined;
, packed_options::inplace_alignment> implementation_defined;
/// @endcond
typedef implementation_defined type;
};

View File

@ -48,6 +48,49 @@
namespace boost {
namespace container {
namespace dtl{
template<class Options>
struct get_small_vector_opt
{
typedef Options type;
};
template<>
struct get_small_vector_opt<void>
{
typedef small_vector_null_opt type;
};
template<class Options>
struct get_vopt_from_svopt
: get_small_vector_opt<Options>::type
{
typedef typename get_small_vector_opt<Options>::type options_t;
typedef vector_opt< typename options_t::growth_factor_type, void> type;
};
template<>
struct get_vopt_from_svopt<void>
{
typedef void type;
};
template <class T, class SecondaryAllocator, class Options>
struct vector_for_small_vector
{
typedef vector
< T
, small_vector_allocator
< T
, typename allocator_traits<typename real_allocator<T, SecondaryAllocator>::type>::template portable_rebind_alloc<void>::type
, Options>
, typename dtl::get_vopt_from_svopt<Options>::type
> type;
};
} //namespace dtl
//! A non-standard allocator used to implement `small_vector`.
//! Users should never use it directly. It is described here
//! for documentation purposes.
@ -74,7 +117,7 @@ namespace container {
//! `boost::container::vector< T, small_vector_allocator<T, Allocator> >`
//! and internal storage can be obtained downcasting that vector
//! to `small_vector_base<T>`.
template<class T, class VoidAllocator BOOST_CONTAINER_DOCONLY(= void)>
template<class T, class VoidAllocator BOOST_CONTAINER_DOCONLY(= void), class Options BOOST_CONTAINER_DOCONLY(= void)>
class small_vector_allocator
: public allocator_traits<typename real_allocator<T, VoidAllocator>::type>::template portable_rebind_alloc<T>::type
{
@ -146,17 +189,17 @@ class small_vector_allocator
//!Constructor from related small_vector_allocator.
//!Never throws
template<class U, class OtherVoidAllocator>
template<class U, class OtherVoidAllocator, class OtherOptions>
BOOST_CONTAINER_FORCEINLINE small_vector_allocator
(const small_vector_allocator<U, OtherVoidAllocator> &other) BOOST_NOEXCEPT_OR_NOTHROW
(const small_vector_allocator<U, OtherVoidAllocator, OtherOptions> &other) BOOST_NOEXCEPT_OR_NOTHROW
: allocator_type(other.as_base())
{}
//!Move constructor from related small_vector_allocator.
//!Never throws
template<class U, class OtherVoidAllocator>
template<class U, class OtherVoidAllocator, class OtherOptions>
BOOST_CONTAINER_FORCEINLINE small_vector_allocator
(BOOST_RV_REF(small_vector_allocator<U BOOST_MOVE_I OtherVoidAllocator>) other) BOOST_NOEXCEPT_OR_NOTHROW
(BOOST_RV_REF(small_vector_allocator<U BOOST_MOVE_I OtherVoidAllocator BOOST_MOVE_I OtherOptions>) other) BOOST_NOEXCEPT_OR_NOTHROW
: allocator_type(::boost::move(other.as_base()))
{}
@ -183,14 +226,14 @@ class small_vector_allocator
//!Never throws
template<class U, class OtherVoidAllocator>
BOOST_CONTAINER_FORCEINLINE small_vector_allocator &
operator=(BOOST_COPY_ASSIGN_REF(small_vector_allocator<U BOOST_MOVE_I OtherVoidAllocator>) other) BOOST_NOEXCEPT_OR_NOTHROW
operator=(BOOST_COPY_ASSIGN_REF(small_vector_allocator<U BOOST_MOVE_I OtherVoidAllocator BOOST_MOVE_I Options>) other) BOOST_NOEXCEPT_OR_NOTHROW
{ return static_cast<small_vector_allocator&>(this->allocator_type::operator=(other.as_base())); }
//!Move assignment from related small_vector_allocator.
//!Never throws
template<class U, class OtherVoidAllocator>
BOOST_CONTAINER_FORCEINLINE small_vector_allocator &
operator=(BOOST_RV_REF(small_vector_allocator<U BOOST_MOVE_I OtherVoidAllocator>) other) BOOST_NOEXCEPT_OR_NOTHROW
operator=(BOOST_RV_REF(small_vector_allocator<U BOOST_MOVE_I OtherVoidAllocator BOOST_MOVE_I Options>) other) BOOST_NOEXCEPT_OR_NOTHROW
{ return static_cast<small_vector_allocator&>(this->allocator_type::operator=(::boost::move(other.as_base()))); }
//!Move assignment from allocator_type.
@ -271,8 +314,8 @@ class small_vector_allocator
using allocator_type::deallocate_many;*/
typedef vector_alloc_holder< small_vector_allocator, size_type > vector_alloc_holder_t;
typedef vector<value_type, small_vector_allocator> vector_base;
typedef small_vector_base<value_type, allocator_type> derived_type;
typedef typename dtl::vector_for_small_vector<T, allocator_type, Options>::type vector_base;
typedef small_vector_base<value_type, allocator_type, Options> derived_type;
BOOST_CONTAINER_FORCEINLINE bool is_internal_storage(const_pointer p) const
{ return this->internal_storage() == p; }
@ -322,33 +365,30 @@ class small_vector_allocator
//!
//! All `boost::container:vector` member functions are inherited. See `vector` documentation for details.
//!
template <class T, class SecondaryAllocator>
template <class T, class SecondaryAllocator, class Options>
class small_vector_base
: public vector
< T
, small_vector_allocator
< T
, typename allocator_traits<typename real_allocator<T, SecondaryAllocator>::type>::template portable_rebind_alloc<void>::type
>
>
: public dtl::vector_for_small_vector<T, SecondaryAllocator, Options>::type
{
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKEDVECTOR
public:
//Make it public as it will be inherited by small_vector and container
//must have this public member
typedef typename real_allocator<T, SecondaryAllocator>::type secondary_allocator_t;
typedef typename allocator_traits<secondary_allocator_t>::template portable_rebind_alloc<void>::type void_allocator_t;
typedef vector<T, small_vector_allocator<T, void_allocator_t> > base_type;
typedef typename allocator_traits<secondary_allocator_t>::pointer pointer;
typedef typename allocator_traits<secondary_allocator_t>::const_pointer const_pointer;
typedef typename allocator_traits<secondary_allocator_t>::void_pointer void_pointer;
typedef typename real_allocator<T, SecondaryAllocator>::type secondary_allocator_t;
typedef typename allocator_traits<secondary_allocator_t>::
template portable_rebind_alloc<void>::type void_allocator_t;
typedef typename dtl::get_small_vector_opt<Options>::type options_t;
typedef typename dtl::vector_for_small_vector
<T, SecondaryAllocator, Options>::type base_type;
typedef typename allocator_traits<secondary_allocator_t>::pointer pointer;
typedef typename allocator_traits<secondary_allocator_t>::const_pointer const_pointer;
typedef typename allocator_traits<secondary_allocator_t>::void_pointer void_pointer;
typedef typename allocator_traits<secondary_allocator_t>::const_void_pointer const_void_pointer;
typedef small_vector_allocator<T, void_allocator_t> allocator_type;
typedef small_vector_allocator<T, void_allocator_t, Options> allocator_type;
private:
BOOST_COPYABLE_AND_MOVABLE(small_vector_base)
friend class small_vector_allocator<T, void_allocator_t>;
friend class small_vector_allocator<T, void_allocator_t, Options>;
BOOST_CONTAINER_FORCEINLINE
const_pointer internal_storage() const BOOST_NOEXCEPT_OR_NOTHROW
@ -373,9 +413,12 @@ class small_vector_base
base_type &as_base() { return static_cast<base_type&>(*this); }
const base_type &as_base() const { return static_cast<const base_type&>(*this); }
static const std::size_t final_alignment =
options_t::inplace_alignment ? options_t::inplace_alignment : dtl::alignment_of<T>::value;
public:
typedef typename dtl::aligned_storage
<sizeof(T), dtl::alignment_of<T>::value>::type storage_type;
<sizeof(T), final_alignment>::type storage_type;
protected:
@ -441,13 +484,14 @@ struct small_vector_storage_calculator_helper<Needed, Hdr, SSize, true>
static const std::size_t value = 0u;
};
template<class Storage, class Allocator, class T, std::size_t N>
template<class Storage, class Allocator, class T, std::size_t N, class Options>
struct small_vector_storage_calculator
{
typedef small_vector_base<T, Allocator> svh_type;
typedef small_vector_base<T, Allocator, Options> svh_type;
typedef typename real_allocator<T, Allocator>::type value_allocator_t;
typedef typename allocator_traits<value_allocator_t>::template portable_rebind_alloc<void>::type void_allocator_t;
typedef vector<T, small_vector_allocator<T, void_allocator_t> > svhb_type;
typedef typename dtl::vector_for_small_vector<T, void_allocator_t, Options>::type svhb_type;
static const std::size_t s_align = dtl::alignment_of<Storage>::value;
static const std::size_t s_size = sizeof(Storage);
static const std::size_t svh_sizeof = sizeof(svh_type);
@ -474,13 +518,13 @@ template<class Storage>
struct small_vector_storage<Storage, 0>
{};
template<class T, class Allocator, std::size_t N>
template<class T, class Allocator, std::size_t N, class Options>
struct small_vector_storage_definer
{
typedef T value_type;
typedef typename small_vector_base<value_type, Allocator>::storage_type storage_type;
typedef typename small_vector_base<value_type, Allocator, Options>::storage_type storage_type;
static const std::size_t needed_extra_storages =
small_vector_storage_calculator<storage_type, Allocator, value_type, N>::needed_extra_storages;
small_vector_storage_calculator<storage_type, Allocator, value_type, N, Options>::needed_extra_storages;
typedef small_vector_storage<storage_type, needed_extra_storages> type;
};
@ -490,7 +534,7 @@ struct small_vector_storage_definer
//! It contains some preallocated elements in-place, which can avoid the use of dynamic storage allocation
//! when the actual number of elements is below that preallocated threshold.
//!
//! `small_vector<T, N, Allocator>` is convertible to `small_vector_base<T, Allocator>` that is independent
//! `small_vector<T, N, Allocator, Options>` is convertible to `small_vector_base<T, Allocator, Options>` that is independent
//! from the preallocated element capacity, so client code does not need to be templated on that N argument.
//!
//! All `boost::container::vector` member functions are inherited. See `vector` documentation for details.
@ -499,23 +543,26 @@ struct small_vector_storage_definer
//! \tparam N The number of preallocated elements stored inside small_vector. It shall be less than Allocator::max_size();
//! \tparam Allocator The allocator used for memory management when the number of elements exceeds N. Use void
//! for the default allocator
template <class T, std::size_t N, class Allocator BOOST_CONTAINER_DOCONLY(= void) >
class small_vector : public small_vector_base<T, Allocator>
//! |tparam Options A type produced from \c boost::container::small_vector_options.
template <class T, std::size_t N, class Allocator BOOST_CONTAINER_DOCONLY(= void), class Options BOOST_CONTAINER_DOCONLY(= void) >
class small_vector : public small_vector_base<T, Allocator, Options>
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
, private small_vector_storage_definer<T, Allocator, N>::type
, private small_vector_storage_definer<T, Allocator, N, Options>::type
#endif
{
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
typedef small_vector_base<T, Allocator> base_type;
typedef typename small_vector_storage_definer<T, Allocator, N>::type remaining_storage_holder;
typedef small_vector_base<T, Allocator, Options> base_type;
typedef typename small_vector_storage_definer
<T, Allocator, N, Options>::type remaining_storage_holder;
BOOST_COPYABLE_AND_MOVABLE(small_vector)
typedef allocator_traits<typename base_type::allocator_type> allocator_traits_type;
public:
typedef small_vector_storage_calculator< typename small_vector_base<T, Allocator>
::storage_type, Allocator, T, N> storage_test;
typedef small_vector_storage_calculator
< typename small_vector_base<T, Allocator, Options>::storage_type
, Allocator, T, N, Options> storage_test;
static const std::size_t needed_extra_storages = storage_test::needed_extra_storages;
static const std::size_t needed_bytes = storage_test::needed_bytes;