From 4c348af1eb1290f9f20f277f76266df16f91b5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Thu, 9 May 2019 22:15:43 +0200 Subject: [PATCH] Added options for small_vector: inplace_alignment and growth_factor. Fixes #47 --- doc/container.qbk | 35 ++++- include/boost/container/allocator_traits.hpp | 6 +- include/boost/container/container_fwd.hpp | 14 +- .../boost/container/detail/version_type.hpp | 7 +- include/boost/container/options.hpp | 77 +++++++++-- include/boost/container/small_vector.hpp | 127 ++++++++++++------ 6 files changed, 205 insertions(+), 61 deletions(-) diff --git a/doc/container.qbk b/doc/container.qbk index 67344f8..c9db3ae 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -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] diff --git a/include/boost/container/allocator_traits.hpp b/include/boost/container/allocator_traits.hpp index 8cfb037..72d90d1 100644 --- a/include/boost/container/allocator_traits.hpp +++ b/include/boost/container/allocator_traits.hpp @@ -77,7 +77,7 @@ namespace container { #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED -template +template class small_vector_allocator; namespace allocator_traits_detail { @@ -99,8 +99,8 @@ template struct is_std_allocator< std::allocator > { static const bool value = true; }; -template -struct is_std_allocator< small_vector_allocator > > +template +struct is_std_allocator< small_vector_allocator, Options > > { static const bool value = true; }; template diff --git a/include/boost/container/container_fwd.hpp b/include/boost/container/container_fwd.hpp index 8ed29c0..2cfb20a 100644 --- a/include/boost/container/container_fwd.hpp +++ b/include/boost/container/container_fwd.hpp @@ -106,14 +106,20 @@ template class stable_vector; -template +template < class T + , std::size_t Capacity + , class Options = void> class static_vector; -template +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 struct extract_version - : T::version -{}; +{ + typedef typename T::version type; +}; template struct has_version @@ -69,7 +70,7 @@ struct version template struct version { - static const unsigned value = extract_version::value; + static const unsigned value = extract_version::type::value; }; } //namespace impl diff --git a/include/boost/container/options.hpp b/include/boost/container/options.hpp index 4d3bec2..ce85668 100644 --- a/include/boost/container/options.hpp +++ b/include/boost/container/options.hpp @@ -102,6 +102,65 @@ using tree_assoc_options_t = typename boost::container::tree_assoc_options +struct hash_opt +{ + static const bool store_hash = StoreHash; +}; + +typedef hash_opt 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 +#else +template +#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 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 +using hash_assoc_options_t = typename boost::container::hash_assoc_options::type; + +#endif + //////////////////////////////////////////////////////////////// // // @@ -266,15 +325,15 @@ using vector_options_t = typename boost::container::vector_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 +template 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 small_vector_null_opt; @@ -283,7 +342,7 @@ typedef small_vector_opt 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 #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 +template 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 static_vector_null_opt; @@ -350,7 +409,7 @@ typedef static_vector_opt 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 #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; }; diff --git a/include/boost/container/small_vector.hpp b/include/boost/container/small_vector.hpp index 80300af..6529a82 100644 --- a/include/boost/container/small_vector.hpp +++ b/include/boost/container/small_vector.hpp @@ -48,6 +48,49 @@ namespace boost { namespace container { +namespace dtl{ + +template +struct get_small_vector_opt +{ + typedef Options type; +}; + +template<> +struct get_small_vector_opt +{ + typedef small_vector_null_opt type; +}; + +template +struct get_vopt_from_svopt + : get_small_vector_opt::type +{ + typedef typename get_small_vector_opt::type options_t; + typedef vector_opt< typename options_t::growth_factor_type, void> type; +}; + +template<> +struct get_vopt_from_svopt +{ + typedef void type; +}; + +template +struct vector_for_small_vector +{ + typedef vector + < T + , small_vector_allocator + < T + , typename allocator_traits::type>::template portable_rebind_alloc::type + , Options> + , typename dtl::get_vopt_from_svopt::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 >` //! and internal storage can be obtained downcasting that vector //! to `small_vector_base`. -template +template class small_vector_allocator : public allocator_traits::type>::template portable_rebind_alloc::type { @@ -146,17 +189,17 @@ class small_vector_allocator //!Constructor from related small_vector_allocator. //!Never throws - template + template BOOST_CONTAINER_FORCEINLINE small_vector_allocator - (const small_vector_allocator &other) BOOST_NOEXCEPT_OR_NOTHROW + (const small_vector_allocator &other) BOOST_NOEXCEPT_OR_NOTHROW : allocator_type(other.as_base()) {} //!Move constructor from related small_vector_allocator. //!Never throws - template + template BOOST_CONTAINER_FORCEINLINE small_vector_allocator - (BOOST_RV_REF(small_vector_allocator) other) BOOST_NOEXCEPT_OR_NOTHROW + (BOOST_RV_REF(small_vector_allocator) other) BOOST_NOEXCEPT_OR_NOTHROW : allocator_type(::boost::move(other.as_base())) {} @@ -183,14 +226,14 @@ class small_vector_allocator //!Never throws template BOOST_CONTAINER_FORCEINLINE small_vector_allocator & - operator=(BOOST_COPY_ASSIGN_REF(small_vector_allocator) other) BOOST_NOEXCEPT_OR_NOTHROW + operator=(BOOST_COPY_ASSIGN_REF(small_vector_allocator) other) BOOST_NOEXCEPT_OR_NOTHROW { return static_cast(this->allocator_type::operator=(other.as_base())); } //!Move assignment from related small_vector_allocator. //!Never throws template BOOST_CONTAINER_FORCEINLINE small_vector_allocator & - operator=(BOOST_RV_REF(small_vector_allocator) other) BOOST_NOEXCEPT_OR_NOTHROW + operator=(BOOST_RV_REF(small_vector_allocator) other) BOOST_NOEXCEPT_OR_NOTHROW { return static_cast(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 vector_base; - typedef small_vector_base derived_type; + typedef typename dtl::vector_for_small_vector::type vector_base; + typedef small_vector_base 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 +template class small_vector_base - : public vector - < T - , small_vector_allocator - < T - , typename allocator_traits::type>::template portable_rebind_alloc::type - > - > + : public dtl::vector_for_small_vector::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::type secondary_allocator_t; - typedef typename allocator_traits::template portable_rebind_alloc::type void_allocator_t; - typedef vector > base_type; - typedef typename allocator_traits::pointer pointer; - typedef typename allocator_traits::const_pointer const_pointer; - typedef typename allocator_traits::void_pointer void_pointer; + typedef typename real_allocator::type secondary_allocator_t; + typedef typename allocator_traits:: + template portable_rebind_alloc::type void_allocator_t; + typedef typename dtl::get_small_vector_opt::type options_t; + typedef typename dtl::vector_for_small_vector + ::type base_type; + typedef typename allocator_traits::pointer pointer; + typedef typename allocator_traits::const_pointer const_pointer; + typedef typename allocator_traits::void_pointer void_pointer; typedef typename allocator_traits::const_void_pointer const_void_pointer; - typedef small_vector_allocator allocator_type; + typedef small_vector_allocator allocator_type; private: BOOST_COPYABLE_AND_MOVABLE(small_vector_base) - friend class small_vector_allocator; + friend class small_vector_allocator; 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(*this); } const base_type &as_base() const { return static_cast(*this); } + static const std::size_t final_alignment = + options_t::inplace_alignment ? options_t::inplace_alignment : dtl::alignment_of::value; public: + typedef typename dtl::aligned_storage - ::value>::type storage_type; + ::type storage_type; protected: @@ -441,13 +484,14 @@ struct small_vector_storage_calculator_helper static const std::size_t value = 0u; }; -template +template struct small_vector_storage_calculator { - typedef small_vector_base svh_type; + typedef small_vector_base svh_type; typedef typename real_allocator::type value_allocator_t; typedef typename allocator_traits::template portable_rebind_alloc::type void_allocator_t; - typedef vector > svhb_type; + typedef typename dtl::vector_for_small_vector::type svhb_type; + static const std::size_t s_align = dtl::alignment_of::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 struct small_vector_storage {}; -template +template struct small_vector_storage_definer { typedef T value_type; - typedef typename small_vector_base::storage_type storage_type; + typedef typename small_vector_base::storage_type storage_type; static const std::size_t needed_extra_storages = - small_vector_storage_calculator::needed_extra_storages; + small_vector_storage_calculator::needed_extra_storages; typedef small_vector_storage 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` is convertible to `small_vector_base` that is independent +//! `small_vector` is convertible to `small_vector_base` 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 small_vector : public small_vector_base +//! |tparam Options A type produced from \c boost::container::small_vector_options. +template +class small_vector : public small_vector_base #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED - , private small_vector_storage_definer::type + , private small_vector_storage_definer::type #endif { #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED - typedef small_vector_base base_type; - typedef typename small_vector_storage_definer::type remaining_storage_holder; + typedef small_vector_base base_type; + typedef typename small_vector_storage_definer + ::type remaining_storage_holder; BOOST_COPYABLE_AND_MOVABLE(small_vector) typedef allocator_traits allocator_traits_type; public: - typedef small_vector_storage_calculator< typename small_vector_base - ::storage_type, Allocator, T, N> storage_test; + typedef small_vector_storage_calculator + < typename small_vector_base::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;