From af58c7d226553d52ebf10dba259d99d66936cd6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Sun, 13 Oct 2024 00:29:12 +0200 Subject: [PATCH] Added `stored_size` option to `small_vector` --- doc/container.qbk | 10 ++++- example/doc_custom_small_vector.cpp | 8 ++++ include/boost/container/options.hpp | 15 +++++--- include/boost/container/small_vector.hpp | 8 +++- test/small_vector_options_test.cpp | 48 ++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 8 deletions(-) diff --git a/doc/container.qbk b/doc/container.qbk index 4bd752be..b74084f5 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -803,6 +803,12 @@ the last template parameter and defined using the utility class [classref boost::container::growth_factor_60 growth_factor_60] and [classref boost::container::growth_factor_50 growth_factor_100]. +* [classref boost::container::stored_size stored_size]: the type that will be used to store size-related + parameters inside of the vector. Sometimes, when the maximum capacity to be used is much less than the + theoretical maximum that a vector can hold, it's interesting to use smaller unsigned integer types to represent + `size()` and `capacity()` inside vector, so that the size of an empty vector is minimized and cache + performance might be improved. See [classref boost::container::stored_size stored_size] for more details. + See the following example to see how [classref boost::container::small_vector_options small_vector_options] can be used to customize `small_vector`: @@ -1417,7 +1423,9 @@ use [*Boost.Container]? There are several reasons for that: [section:release_notes_boost_1_87_00 Boost 1.87 Release] -* Added [classref boost::container::stored_size stored_size] option to [classref boost::container::static_vector static_vector]. +* Added [classref boost::container::stored_size stored_size] option to + [classref boost::container::static_vector static_vector] and + [classref boost::container::small_vector small_vector]. * Fixed bugs/issues: * [@https://github.com/boostorg/container/issues/261 GitHub #261: ['"End iterators are not dereferencable"]]. diff --git a/example/doc_custom_small_vector.cpp b/example/doc_custom_small_vector.cpp index f42e5c6d..60a9e4e3 100644 --- a/example/doc_custom_small_vector.cpp +++ b/example/doc_custom_small_vector.cpp @@ -41,6 +41,14 @@ int main () growth_50_vector.push_back(1); assert(growth_50_vector.capacity() == old_cap*3/2); + //This option specifies that a vector that will use "unsigned char" as + //the type to store capacity or size internally. + typedef small_vector_options< stored_size >::type size_option_t; + + //Size-optimized vector is smaller than the default one. + typedef small_vector, size_option_t > size_optimized_vector_t; + assert((sizeof(size_optimized_vector_t) < sizeof(small_vector))); + return 0; } //] diff --git a/include/boost/container/options.hpp b/include/boost/container/options.hpp index b467db1f..13b168f1 100644 --- a/include/boost/container/options.hpp +++ b/include/boost/container/options.hpp @@ -359,20 +359,23 @@ BOOST_INTRUSIVE_OPTION_CONSTANT(inplace_alignment, std::size_t, Alignment, inpla #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) -template +template struct small_vector_opt { - typedef GrowthType growth_factor_type; + typedef GrowthType growth_factor_type; BOOST_STATIC_CONSTEXPR std::size_t inplace_alignment = InplaceAlignment; + typedef StoredSizeType stored_size_type; }; -typedef small_vector_opt small_vector_null_opt; +typedef small_vector_opt small_vector_null_opt; #endif //!defined(BOOST_CONTAINER_DOXYGEN_INVOKED) //! 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::inplace_alignment +//! Supported options are: \c boost::container::growth_factor, +//! \c boost::container::inplace_alignment and +//! \c boost::container::stored_size. #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) || defined(BOOST_CONTAINER_VARIADIC_TEMPLATES) template #else @@ -390,7 +393,9 @@ struct small_vector_options #endif >::type packed_options; typedef small_vector_opt< typename packed_options::growth_factor_type - , packed_options::inplace_alignment> implementation_defined; + , packed_options::inplace_alignment + , typename packed_options::stored_size_type + > implementation_defined; /// @endcond typedef implementation_defined type; }; diff --git a/include/boost/container/small_vector.hpp b/include/boost/container/small_vector.hpp index 3f260ac9..afed9108 100644 --- a/include/boost/container/small_vector.hpp +++ b/include/boost/container/small_vector.hpp @@ -69,7 +69,9 @@ 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; + typedef vector_opt< typename options_t::growth_factor_type + , typename options_t::stored_size_type + > type; }; template<> @@ -340,9 +342,11 @@ struct small_vector_storage //! template class small_vector_base +#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED : public dtl::vector_for_small_vector::type +#endif { - #ifndef BOOST_CONTAINER_DOXYGEN_INVOKEDVECTOR + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED public: //Make it public as it will be inherited by small_vector and container //must have this public member diff --git a/test/small_vector_options_test.cpp b/test/small_vector_options_test.cpp index dfa486f3..a085b254 100644 --- a/test/small_vector_options_test.cpp +++ b/test/small_vector_options_test.cpp @@ -8,6 +8,7 @@ // ////////////////////////////////////////////////////////////////////////////// #include +#include #include #include using namespace boost::container; @@ -100,11 +101,58 @@ void test_growth_factor_100() BOOST_TEST(new_capacity == 2*old_capacity); } +template +void test_stored_size_type_impl() +{ + #ifndef BOOST_NO_EXCEPTIONS + VectorType v; + typedef typename VectorType::size_type size_type; + typedef typename VectorType::value_type value_type; + size_type const max = Unsigned(-1); + v.resize(5); + v.resize(max); + BOOST_TEST_THROWS(v.resize(max+1), std::exception); + BOOST_TEST_THROWS(v.push_back(value_type(1)), std::exception); + BOOST_TEST_THROWS(v.insert(v.begin(), value_type(1)), std::exception); + BOOST_TEST_THROWS(v.emplace(v.begin(), value_type(1)),std::exception); + BOOST_TEST_THROWS(v.reserve(max+1), std::exception); + BOOST_TEST_THROWS(VectorType v2(max+1), std::exception); + #endif +} + +template +void test_stored_size_type() +{ + #if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) + using options_t = small_vector_options_t< stored_size >; + #else + typedef typename small_vector_options + < stored_size >::type options_t; + #endif + + typedef small_vector normal_small_vector_t; + + //Test first with a typical allocator + { + typedef small_vector, options_t> small_vector_t; + test_stored_size_type_impl(); + BOOST_CONTAINER_STATIC_ASSERT(sizeof(normal_small_vector_t) > sizeof(small_vector_t)); + } + //Test with a V2 allocator + { + typedef small_vector, options_t> small_vector_t; + test_stored_size_type_impl(); + BOOST_CONTAINER_STATIC_ASSERT(sizeof(normal_small_vector_t) > sizeof(small_vector_t)); + } +} + int main() { test_alignment(); test_growth_factor_50(); test_growth_factor_60(); test_growth_factor_100(); + test_stored_size_type(); + test_stored_size_type(); return ::boost::report_errors(); }