From 6bf84067b3292cd7cfcfba90e90bb3366e926df4 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 22 Jul 2023 17:59:01 +0200 Subject: [PATCH 01/12] added unordered_flat_map(concurrent_flat_map&&) --- .../boost/unordered/concurrent_flat_map.hpp | 4 +++ .../unordered/detail/foa/concurrent_table.hpp | 18 +++++++--- include/boost/unordered/detail/foa/core.hpp | 36 +++++++++++++------ include/boost/unordered/detail/foa/table.hpp | 28 +++++++++++++++ .../boost/unordered/unordered_flat_map.hpp | 7 ++++ 5 files changed, 78 insertions(+), 15 deletions(-) diff --git a/include/boost/unordered/concurrent_flat_map.hpp b/include/boost/unordered/concurrent_flat_map.hpp index 9364d349..a5b968c8 100644 --- a/include/boost/unordered/concurrent_flat_map.hpp +++ b/include/boost/unordered/concurrent_flat_map.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,9 @@ namespace boost { template friend class concurrent_flat_map; + template + friend class unordered_flat_map; using type_policy = detail::foa::flat_map_types; diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index 51615446..84a66c4e 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -281,6 +281,13 @@ struct concurrent_table_arrays:table_arrays template static void delete_(Allocator& al,concurrent_table_arrays& arrays)noexcept + { + delete_group_access(al,arrays); + super::delete_(al,arrays); + } + + template + static void delete_group_access(Allocator& al,concurrent_table_arrays& arrays)noexcept { if(arrays.elements){ using access_alloc= @@ -294,7 +301,6 @@ struct concurrent_table_arrays:table_arrays aal,pointer_traits::pointer_to(*arrays.group_accesses), arrays.groups_size_mask+1); } - super::delete_(al,arrays); } group_access *group_accesses; @@ -391,6 +397,9 @@ inline void swap(atomic_size_control& x,atomic_size_control& y) * over. */ +template +class table; /* concurrent/non-concurrent interop */ + template using concurrent_table_core_impl=table_core< TypePolicy,group15,concurrent_table_arrays, @@ -413,10 +422,6 @@ class concurrent_table: using super::N; using prober=typename super::prober; - template< - typename TypePolicy2,typename Hash2,typename Pred2,typename Allocator2> - friend class concurrent_table; - public: using key_type=typename super::key_type; using init_type=typename super::init_type; @@ -875,6 +880,9 @@ public: } private: + template friend class concurrent_table; + template friend class table; + using mutex_type=rw_spinlock; using multimutex_type=multimutex; // TODO: adapt 128 to the machine using shared_lock_guard=shared_lock; diff --git a/include/boost/unordered/detail/foa/core.hpp b/include/boost/unordered/detail/foa/core.hpp index 239d05d3..ae5c29c7 100644 --- a/include/boost/unordered/detail/foa/core.hpp +++ b/include/boost/unordered/detail/foa/core.hpp @@ -1282,6 +1282,17 @@ public: size_ctrl{initial_max_load(),0} {} + /* bare transfer ctor for concurrent/non-concurrent interop */ + + table_core( + Hash&& h_,Pred&& pred_,Allocator&& al_, + const arrays_type& arrays_,const size_ctrl_type& size_ctrl_): + hash_base{empty_init,h_}, + pred_base{empty_init,pred_}, + allocator_base{empty_init,al_}, + arrays(arrays_),size_ctrl(size_ctrl_) + {} + table_core(const table_core& x): table_core{x,alloc_traits::select_on_container_copy_construction(x.al())}{} @@ -1290,14 +1301,11 @@ public: std::is_nothrow_move_constructible::value&& std::is_nothrow_move_constructible::value&& std::is_nothrow_move_constructible::value): - hash_base{empty_init,std::move(x.h())}, - pred_base{empty_init,std::move(x.pred())}, - allocator_base{empty_init,std::move(x.al())}, - arrays(x.arrays),size_ctrl(x.size_ctrl) + table_core{ + std::move(x.h()),std::move(x.pred()),std::move(x.al()), + x.arrays,x.size_ctrl} { - x.arrays=x.new_arrays(0); - x.size_ctrl.ml=x.initial_max_load(); - x.size_ctrl.size=0; + x.empty_initialize(); } table_core(const table_core& x,const Allocator& al_): @@ -1336,6 +1344,13 @@ public: delete_arrays(arrays); } + void empty_initialize()noexcept + { + arrays=new_arrays(0); + size_ctrl.ml=initial_max_load(); + size_ctrl.size=0; + } + table_core& operator=(const table_core& x) { BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) @@ -1802,9 +1817,10 @@ private: table_core(Hash&& h_,Pred&& pred_,const Allocator& al_): hash_base{empty_init,std::move(h_)}, pred_base{empty_init,std::move(pred_)}, - allocator_base{empty_init,al_},arrays(new_arrays(0)), - size_ctrl{initial_max_load(),0} - {} + allocator_base{empty_init,al_} + { + empty_initialize(); + } arrays_type new_arrays(std::size_t n) { diff --git a/include/boost/unordered/detail/foa/table.hpp b/include/boost/unordered/detail/foa/table.hpp index 85145cb6..3c4ab5ac 100644 --- a/include/boost/unordered/detail/foa/table.hpp +++ b/include/boost/unordered/detail/foa/table.hpp @@ -264,6 +264,9 @@ private: * checking is done by boost::unordered_(flat|node)_(map|set). */ +template +class concurrent_table; /* concurrent/non-concurrent interop */ + template using table_core_impl= table_core,table_arrays, @@ -284,6 +287,8 @@ class table:table_core_impl using group_type=typename super::group_type; using super::N; using prober=typename super::prober; + using arrays_type=typename super::arrays_type; + using size_ctrl_type=typename super::size_ctrl_type; using locator=typename super::locator; public: @@ -323,6 +328,8 @@ public: table(table&& x)=default; table(const table& x,const Allocator& al_):super{x,al_}{} table(table&& x,const Allocator& al_):super{std::move(x),al_}{} + table(concurrent_table&& x): + table(std::move(x),x.exclusive_access()){} ~table()=default; table& operator=(const table& x)=default; @@ -496,6 +503,27 @@ public: friend bool operator!=(const table& x,const table& y){return !(x==y);} private: + template friend class concurrent_table; + using compatible_concurrent_table= + concurrent_table; + + table( + compatible_concurrent_table&& x, + typename compatible_concurrent_table::exclusive_lock_guard): + super{ + std::move(x.h()),std::move(x.pred()),std::move(x.al()), + arrays_type{ + x.arrays.groups_size_index,x.arrays.groups_size_mask, + reinterpret_cast(x.arrays.groups), + reinterpret_cast(x.arrays.elements)}, + size_ctrl_type{ + x.size_ctrl.ml,x.size_ctrl.size}} + { + typename compatible_concurrent_table::arrays_type::delete_group_access( + this->al(),x.arrays); + x.empty_initialize(); + } + struct erase_on_exit { erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{} diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index d74de55a..66ce6faf 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -10,6 +10,7 @@ #pragma once #endif +#include #include #include #include @@ -173,6 +174,12 @@ namespace boost { { } + unordered_flat_map( + concurrent_flat_map&& other) + : table_(std::move(other.table_)) + { + } + ~unordered_flat_map() = default; unordered_flat_map& operator=(unordered_flat_map const& other) From 6994a37b235c78324b3b0c542bad683fb61be547 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 22 Jul 2023 18:49:15 +0200 Subject: [PATCH 02/12] used direct arrays construction in place of empty_initialize (arrays_type not default constructible) --- include/boost/unordered/detail/foa/core.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/unordered/detail/foa/core.hpp b/include/boost/unordered/detail/foa/core.hpp index ae5c29c7..78ff2de6 100644 --- a/include/boost/unordered/detail/foa/core.hpp +++ b/include/boost/unordered/detail/foa/core.hpp @@ -1817,9 +1817,9 @@ private: table_core(Hash&& h_,Pred&& pred_,const Allocator& al_): hash_base{empty_init,std::move(h_)}, pred_base{empty_init,std::move(pred_)}, - allocator_base{empty_init,al_} + allocator_base{empty_init,al_},arrays(new_arrays(0)), + size_ctrl{initial_max_load(),0} { - empty_initialize(); } arrays_type new_arrays(std::size_t n) From 4be37cfdaf57f24515ff1108848972e910902b57 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 22 Jul 2023 19:17:11 +0200 Subject: [PATCH 03/12] avoided premature instantiation of concurrent_table --- include/boost/unordered/detail/foa/table.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/boost/unordered/detail/foa/table.hpp b/include/boost/unordered/detail/foa/table.hpp index 3c4ab5ac..2a8a0b5e 100644 --- a/include/boost/unordered/detail/foa/table.hpp +++ b/include/boost/unordered/detail/foa/table.hpp @@ -507,9 +507,8 @@ private: using compatible_concurrent_table= concurrent_table; - table( - compatible_concurrent_table&& x, - typename compatible_concurrent_table::exclusive_lock_guard): + template + table(compatible_concurrent_table&& x,ExclusiveLockGuard): super{ std::move(x.h()),std::move(x.pred()),std::move(x.al()), arrays_type{ From c046b916f8bf34a8f779777daaee9e04125c913a Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 22 Jul 2023 20:38:16 +0200 Subject: [PATCH 04/12] fix atomic_size_control copy ctor --- include/boost/unordered/detail/foa/concurrent_table.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index 84a66c4e..afe9cdae 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -313,7 +313,7 @@ struct atomic_size_control atomic_size_control(std::size_t ml_,std::size_t size_): pad0_{},ml{ml_},pad1_{},size{size_}{} - atomic_size_control(atomic_size_control& x): + atomic_size_control(const atomic_size_control& x): pad0_{},ml{x.ml.load()},pad1_{},size{x.size.load()}{} /* padding to avoid false sharing internally and with sorrounding data */ From d4adcd9b712db94262fbd0fb84cd79148337c4cf Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sat, 22 Jul 2023 20:48:06 +0200 Subject: [PATCH 05/12] added missing std::move's --- include/boost/unordered/detail/foa/core.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/unordered/detail/foa/core.hpp b/include/boost/unordered/detail/foa/core.hpp index 78ff2de6..f0a2ef5c 100644 --- a/include/boost/unordered/detail/foa/core.hpp +++ b/include/boost/unordered/detail/foa/core.hpp @@ -1287,9 +1287,9 @@ public: table_core( Hash&& h_,Pred&& pred_,Allocator&& al_, const arrays_type& arrays_,const size_ctrl_type& size_ctrl_): - hash_base{empty_init,h_}, - pred_base{empty_init,pred_}, - allocator_base{empty_init,al_}, + hash_base{empty_init,std::move(h_)}, + pred_base{empty_init,std::move(pred_)}, + allocator_base{empty_init,std::move(al_)}, arrays(arrays_),size_ctrl(size_ctrl_) {} From ac1a236de661ab5b8d3138b1e6c7f215cd85c6b7 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 23 Jul 2023 10:15:06 +0200 Subject: [PATCH 06/12] added concurrent_flat_map(unordered_flat_map&&) --- .../boost/unordered/concurrent_flat_map.hpp | 7 +++ .../unordered/detail/foa/concurrent_table.hpp | 50 ++++++++++++++----- .../boost/unordered/unordered_flat_map.hpp | 4 ++ 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/include/boost/unordered/concurrent_flat_map.hpp b/include/boost/unordered/concurrent_flat_map.hpp index a5b968c8..6e2eef98 100644 --- a/include/boost/unordered/concurrent_flat_map.hpp +++ b/include/boost/unordered/concurrent_flat_map.hpp @@ -227,6 +227,13 @@ namespace boost { { } + + concurrent_flat_map( + unordered_flat_map&& other) + : table_(std::move(other.table_)) + { + } + ~concurrent_flat_map() = default; concurrent_flat_map& operator=(concurrent_flat_map const& rhs) diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index afe9cdae..5fcb6ace 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -252,7 +252,21 @@ struct concurrent_table_arrays:table_arrays template static concurrent_table_arrays new_(Allocator& al,std::size_t n) { - concurrent_table_arrays arrays{super::new_(al,n),nullptr}; + super x{super::new_(al,n)}; + BOOST_TRY{ + return new_group_access(al,x); + } + BOOST_CATCH(...){ + super::delete_(al,x); + BOOST_RETHROW + } + BOOST_CATCH_END + } + + template + static concurrent_table_arrays new_group_access(Allocator& al,const super& x) + { + concurrent_table_arrays arrays{x,nullptr}; if(!arrays.elements){ arrays.group_accesses=dummy_group_accesses(); } @@ -261,20 +275,13 @@ struct concurrent_table_arrays:table_arrays typename boost::allocator_rebind::type; using access_traits=boost::allocator_traits; - BOOST_TRY{ - auto aal=access_alloc(al); - arrays.group_accesses=boost::to_address( - access_traits::allocate(aal,arrays.groups_size_mask+1)); + auto aal=access_alloc(al); + arrays.group_accesses=boost::to_address( + access_traits::allocate(aal,arrays.groups_size_mask+1)); - for(std::size_t i=0;i&& x): + super{ + std::move(x.h()),std::move(x.pred()),std::move(x.al()), + arrays_type(arrays_type::new_group_access( + x.al(), + typename arrays_type::super{ + x.arrays.groups_size_index,x.arrays.groups_size_mask, + reinterpret_cast(x.arrays.groups), + reinterpret_cast(x.arrays.elements)})), + size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}} + { + x.empty_initialize(); + } + ~concurrent_table()=default; concurrent_table& operator=(const concurrent_table& x) diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index 66ce6faf..1f7984ec 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -37,6 +37,10 @@ namespace boost { template class unordered_flat_map { + template + friend class concurrent_flat_map; + using map_types = detail::foa::flat_map_types; using table_type = detail::foa::table Date: Sun, 23 Jul 2023 10:49:53 +0200 Subject: [PATCH 07/12] stylistic --- include/boost/unordered/detail/foa/concurrent_table.hpp | 7 ++++--- include/boost/unordered/detail/foa/table.hpp | 9 ++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index 5fcb6ace..15aec185 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -372,7 +372,7 @@ inline void swap(atomic_size_control& x,atomic_size_control& y) * - Parallel versions of [c]visit_all(f) and erase_if(f) are provided based * on C++17 stdlib parallel algorithms. * - * Consult boost::unordered_flat_map docs for the full API reference. + * Consult boost::concurrent_flat_map docs for the full API reference. * Heterogeneous lookup is suported by default, that is, without checking for * any ::is_transparent typedefs --this checking is done by the wrapping * containers. @@ -430,6 +430,8 @@ class concurrent_table: using prober=typename super::prober; using arrays_type=typename super::arrays_type; using size_ctrl_type=typename super::size_ctrl_type; + using compatible_nonconcurrent_table=table; + friend class compatible_nonconcurrent_table; public: using key_type=typename super::key_type; @@ -465,7 +467,7 @@ public: concurrent_table(concurrent_table&& x,const Allocator& al_): concurrent_table(std::move(x),al_,x.exclusive_access()){} - concurrent_table(table&& x): + concurrent_table(compatible_nonconcurrent_table&& x): super{ std::move(x.h()),std::move(x.pred()),std::move(x.al()), arrays_type(arrays_type::new_group_access( @@ -905,7 +907,6 @@ public: private: template friend class concurrent_table; - template friend class table; using mutex_type=rw_spinlock; using multimutex_type=multimutex; // TODO: adapt 128 to the machine diff --git a/include/boost/unordered/detail/foa/table.hpp b/include/boost/unordered/detail/foa/table.hpp index 2a8a0b5e..d8968623 100644 --- a/include/boost/unordered/detail/foa/table.hpp +++ b/include/boost/unordered/detail/foa/table.hpp @@ -290,6 +290,9 @@ class table:table_core_impl using arrays_type=typename super::arrays_type; using size_ctrl_type=typename super::size_ctrl_type; using locator=typename super::locator; + using compatible_concurrent_table= + concurrent_table; + friend class compatible_concurrent_table; public: using key_type=typename super::key_type; @@ -328,7 +331,7 @@ public: table(table&& x)=default; table(const table& x,const Allocator& al_):super{x,al_}{} table(table&& x,const Allocator& al_):super{std::move(x),al_}{} - table(concurrent_table&& x): + table(compatible_concurrent_table&& x): table(std::move(x),x.exclusive_access()){} ~table()=default; @@ -503,10 +506,6 @@ public: friend bool operator!=(const table& x,const table& y){return !(x==y);} private: - template friend class concurrent_table; - using compatible_concurrent_table= - concurrent_table; - template table(compatible_concurrent_table&& x,ExclusiveLockGuard): super{ From bf4a5efd2d8cab5f593f58bc9fde2872ce9b332e Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 23 Jul 2023 12:23:08 +0200 Subject: [PATCH 08/12] documented concurrent/unordered interop --- doc/unordered/changes.adoc | 2 ++ doc/unordered/concurrent.adoc | 26 ++++++++++++++++++++++++++ doc/unordered/concurrent_flat_map.adoc | 16 ++++++++++++++++ doc/unordered/unordered_flat_map.adoc | 17 +++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index 757a7000..1d760650 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -10,6 +10,8 @@ * Added `[c]visit_while` operations to `boost::concurrent_map`, with serial and parallel variants. +* Added efficient move construction of `boost::unordered_flat_map` from +`boost::concurrent_flat_map` and vice versa. == Release 1.83.0 - Major update diff --git a/doc/unordered/concurrent.adoc b/doc/unordered/concurrent.adoc index 7ed1be44..d418cec4 100644 --- a/doc/unordered/concurrent.adoc +++ b/doc/unordered/concurrent.adoc @@ -201,3 +201,29 @@ and the user need not take any special precaution, but overall performance may b Another blocking operation is _rehashing_, which happens explicitly via `rehash`/`reserve` or during insertion when the table's load hits `max_load()`. As with non-concurrent containers, reserving space in advance of bulk insertions will generally speed up the process. + +== Interoperability with non-concurrent containers + +As their internal data structure is basically the same, `boost::unordered_flat_map` can +be efficiently move-constructed from `boost::concurrent_flat_map` and vice versa. +This interoperability comes handy in multistage scenarios where parts of the data processing happen +in parallel whereas other steps are non-concurrent (or non-modifying). In the following example, +we want to construct a histogram from a huge input vector of words: +the population phase can be done in parallel with `boost::concurrent_flat_map` and results +then transferred to the final container. + +[source,c++] +---- +std::vector words = ...; + +// Insert words in parallel +boost::concurrent_flat_map m0; +std::for_each( + std::execution::par, words.begin(), words.end(), + [&](const auto& word) { + m0.try_emplace_or_visit(word, 1, [](auto& x) { ++x.second; }); + }); + +// Transfer to a regular unordered_flat_map +boost::unordered_flat_map m=std::move(m0); +---- diff --git a/doc/unordered/concurrent_flat_map.adoc b/doc/unordered/concurrent_flat_map.adoc index 8038f210..bca4f5bb 100644 --- a/doc/unordered/concurrent_flat_map.adoc +++ b/doc/unordered/concurrent_flat_map.adoc @@ -69,6 +69,7 @@ namespace boost { explicit xref:#concurrent_flat_map_allocator_constructor[concurrent_flat_map](const Allocator& a); xref:#concurrent_flat_map_copy_constructor_with_allocator[concurrent_flat_map](const concurrent_flat_map& other, const Allocator& a); xref:#concurrent_flat_map_move_constructor_with_allocator[concurrent_flat_map](concurrent_flat_map&& other, const Allocator& a); + xref:#concurrent_flat_map_move_constructor_from_unordered_flat_map[concurrent_flat_map](unordered_flat_map&& other); xref:#concurrent_flat_map_initializer_list_constructor[concurrent_flat_map](std::initializer_list il, size_type n = _implementation-defined_ const hasher& hf = hasher(), @@ -501,6 +502,21 @@ Concurrency:;; Blocking on `other`. --- +==== Move Constructor from unordered_flat_map + +```c++ +concurrent_flat_map(unordered_flat_map&& other); +``` + +Move construction from a xref:#unordered_flat_map[`unordered_flat_map`]. +The internal bucket array of `other` is transferred directly to the new container. +The hash function, predicate and allocator are moved-constructed from `other`. + +[horizontal] +Complexity:;; O(`bucket_count()`) + +--- + ==== Initializer List Constructor [source,c++,subs="+quotes"] ---- diff --git a/doc/unordered/unordered_flat_map.adoc b/doc/unordered/unordered_flat_map.adoc index a112e192..543db307 100644 --- a/doc/unordered/unordered_flat_map.adoc +++ b/doc/unordered/unordered_flat_map.adoc @@ -77,6 +77,7 @@ namespace boost { explicit xref:#unordered_flat_map_allocator_constructor[unordered_flat_map](const Allocator& a); xref:#unordered_flat_map_copy_constructor_with_allocator[unordered_flat_map](const unordered_flat_map& other, const Allocator& a); xref:#unordered_flat_map_move_constructor_with_allocator[unordered_flat_map](unordered_flat_map&& other, const Allocator& a); + xref:#unordered_flat_map_move_constructor_from_concurrent_flat_map[unordered_flat_map](concurrent_flat_map&& other); xref:#unordered_flat_map_initializer_list_constructor[unordered_flat_map](std::initializer_list il, size_type n = _implementation-defined_ const hasher& hf = hasher(), @@ -472,6 +473,22 @@ from `other`, and the allocator is copy-constructed from `a`. --- +==== Move Constructor from concurrent_flat_map + +```c++ +unordered_flat_map(concurrent_flat_map&& other); +``` + +Move construction from a xref:#concurrent_flat_map[`concurrent_flat_map`]. +The internal bucket array of `other` is transferred directly to the new container. +The hash function, predicate and allocator are moved-constructed from `other`. + +[horizontal] +Complexity:;; Constant time. +Concurrency:;; Blocking on `other`. + +--- + ==== Initializer List Constructor [source,c++,subs="+quotes"] ---- From b206513a11b61dd7cb4d391320c698a227f18337 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 23 Jul 2023 14:39:23 +0200 Subject: [PATCH 09/12] fixed friend declarations --- include/boost/unordered/detail/foa/concurrent_table.hpp | 2 +- include/boost/unordered/detail/foa/table.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index 15aec185..4a536de0 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -431,7 +431,7 @@ class concurrent_table: using arrays_type=typename super::arrays_type; using size_ctrl_type=typename super::size_ctrl_type; using compatible_nonconcurrent_table=table; - friend class compatible_nonconcurrent_table; + friend compatible_nonconcurrent_table; public: using key_type=typename super::key_type; diff --git a/include/boost/unordered/detail/foa/table.hpp b/include/boost/unordered/detail/foa/table.hpp index d8968623..7c462d01 100644 --- a/include/boost/unordered/detail/foa/table.hpp +++ b/include/boost/unordered/detail/foa/table.hpp @@ -292,7 +292,7 @@ class table:table_core_impl using locator=typename super::locator; using compatible_concurrent_table= concurrent_table; - friend class compatible_concurrent_table; + friend compatible_concurrent_table; public: using key_type=typename super::key_type; From ce076782fdacaac637e3c57b7a48c32894a664d0 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 24 Jul 2023 13:07:34 -0700 Subject: [PATCH 10/12] Add tests for interop constructors --- test/cfoa/constructor_tests.cpp | 108 ++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/test/cfoa/constructor_tests.cpp b/test/cfoa/constructor_tests.cpp index f6e0e069..595cc281 100644 --- a/test/cfoa/constructor_tests.cpp +++ b/test/cfoa/constructor_tests.cpp @@ -775,6 +775,109 @@ namespace { check_raii_counts(); } + template void flat_map_constructor(G gen, test::random_generator rg) + { + auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); + auto reference_map = + boost::unordered_flat_map( + values.begin(), values.end(), values.size()); + + raii::reset_counts(); + + { + boost::unordered_flat_map + flat_map(values.begin(), values.end(), reference_map.size(), hasher(1), + key_equal(2), allocator_type(3)); + + auto const old_dc = +raii::default_constructor; + auto const old_mc = +raii::move_constructor; + auto const old_cc = +raii::copy_constructor; + + BOOST_TEST_EQ(old_dc, 0u); + BOOST_TEST_GT(old_mc, 0u); + BOOST_TEST_GT(old_cc, 0u); + + map_type x(std::move(flat_map)); + + test_fuzzy_matches_reference(x, reference_map, rg); + + BOOST_TEST_EQ(+raii::default_constructor, old_dc); + BOOST_TEST_EQ(+raii::move_constructor, old_mc); + BOOST_TEST_EQ(+raii::copy_constructor, old_cc); + + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal(2)); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + + BOOST_TEST(flat_map.empty()); + } + + check_raii_counts(); + + { + boost::unordered_flat_map + flat_map(0, hasher(1), key_equal(2), allocator_type(3)); + + map_type x(std::move(flat_map)); + + BOOST_TEST(x.empty()); + + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal(2)); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + + BOOST_TEST(flat_map.empty()); + } + + check_raii_counts(); + + { + map_type flat_map(values.begin(), values.end(), reference_map.size(), + hasher(1), key_equal(2), allocator_type(3)); + + auto const old_dc = +raii::default_constructor; + auto const old_mc = +raii::move_constructor; + auto const old_cc = +raii::copy_constructor; + + BOOST_TEST_EQ(old_dc, 0u); + BOOST_TEST_GT(old_mc, 0u); + BOOST_TEST_GT(old_cc, 0u); + + boost::unordered_flat_map + x(std::move(flat_map)); + + BOOST_TEST(x == reference_map); + + BOOST_TEST_EQ(+raii::default_constructor, old_dc); + BOOST_TEST_EQ(+raii::move_constructor, old_mc); + BOOST_TEST_EQ(+raii::copy_constructor, old_cc); + + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal(2)); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + + BOOST_TEST(flat_map.empty()); + } + + check_raii_counts(); + + { + map_type flat_map(0, hasher(1), key_equal(2), allocator_type(3)); + + boost::unordered_flat_map + x(std::move(flat_map)); + + BOOST_TEST(x.empty()); + + BOOST_TEST_EQ(x.hash_function(), hasher(1)); + BOOST_TEST_EQ(x.key_eq(), key_equal(2)); + BOOST_TEST(x.get_allocator() == allocator_type(3)); + + BOOST_TEST(flat_map.empty()); + } + + check_raii_counts(); + } } // namespace // clang-format off @@ -818,6 +921,11 @@ UNORDERED_TEST( ((value_type_generator)) ((default_generator)(sequential)(limited_range))) +UNORDERED_TEST( + flat_map_constructor, + ((value_type_generator)) + ((default_generator)(sequential)(limited_range))) + // clang-format on RUN_TESTS() From e563c89b1c318a5ed76644a08b7591bf15f9f99f Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 24 Jul 2023 13:07:59 -0700 Subject: [PATCH 11/12] Remove extraneous typename --- include/boost/unordered/detail/foa/table.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/unordered/detail/foa/table.hpp b/include/boost/unordered/detail/foa/table.hpp index 7c462d01..6cc11ed8 100644 --- a/include/boost/unordered/detail/foa/table.hpp +++ b/include/boost/unordered/detail/foa/table.hpp @@ -517,7 +517,7 @@ private: size_ctrl_type{ x.size_ctrl.ml,x.size_ctrl.size}} { - typename compatible_concurrent_table::arrays_type::delete_group_access( + compatible_concurrent_table::arrays_type::delete_group_access( this->al(),x.arrays); x.empty_initialize(); } From 4918bb6b7eb3f1f1fcacd92aea3ad5bf761086c6 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 25 Jul 2023 12:05:08 -0700 Subject: [PATCH 12/12] Add tests for interop assignment --- test/cfoa/assign_tests.cpp | 138 +++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/test/cfoa/assign_tests.cpp b/test/cfoa/assign_tests.cpp index 24675d87..f2dd2364 100644 --- a/test/cfoa/assign_tests.cpp +++ b/test/cfoa/assign_tests.cpp @@ -33,6 +33,9 @@ using hasher = stateful_hash; using key_equal = stateful_key_equal; using allocator_type = stateful_allocator >; +using flat_map_type = boost::unordered::unordered_flat_map; + using map_type = boost::unordered::concurrent_flat_map; @@ -843,6 +846,136 @@ namespace { } check_raii_counts(); } + + template void flat_map_move_assign(G gen, test::random_generator rg) + { + auto values = make_random_values(1024 * 16, [&] { return gen(rg); }); + auto reference_map = + boost::unordered_flat_map(values.begin(), values.end()); + + /* + * basically test that a temporary container is materialized and we + * move-assign from that + * + * we don't need to be super rigorous here because we already have tests for + * container assignment, we're just testing that a temporary is materialized + */ + + { + raii::reset_counts(); + + flat_map_type flat_map(values.begin(), values.end(), values.size(), + hasher(1), key_equal(2), allocator_type(3)); + + map_type map(0, hasher(2), key_equal(1), allocator_type(3)); + + BOOST_TEST(flat_map.get_allocator() == map.get_allocator()); + + map = std::move(flat_map); + + BOOST_TEST(flat_map.empty()); + BOOST_TEST_EQ(map.size(), reference_map.size()); + + test_fuzzy_matches_reference(map, reference_map, rg); + + BOOST_TEST_EQ(map.hash_function(), hasher(1)); + BOOST_TEST_EQ(map.key_eq(), key_equal(2)); + + BOOST_TEST_EQ(raii::copy_constructor, 2 * values.size()); + BOOST_TEST_EQ(raii::destructor, 2 * values.size()); + BOOST_TEST_EQ(raii::move_constructor, 2 * reference_map.size()); + BOOST_TEST_EQ(raii::copy_assignment, 0u); + BOOST_TEST_EQ(raii::move_assignment, 0u); + } + + check_raii_counts(); + + { + raii::reset_counts(); + + map_type map(values.begin(), values.end(), values.size(), hasher(1), + key_equal(2), allocator_type(3)); + + flat_map_type flat_map(0, hasher(2), key_equal(1), allocator_type(3)); + + BOOST_TEST(flat_map.get_allocator() == map.get_allocator()); + + flat_map = std::move(map); + + BOOST_TEST(map.empty()); + BOOST_TEST_EQ(flat_map.size(), reference_map.size()); + + BOOST_TEST_EQ(flat_map.hash_function(), hasher(1)); + BOOST_TEST_EQ(flat_map.key_eq(), key_equal(2)); + + BOOST_TEST_EQ(raii::copy_constructor, 2 * values.size()); + BOOST_TEST_EQ(raii::destructor, 2 * values.size()); + BOOST_TEST_EQ(raii::move_constructor, 2 * reference_map.size()); + BOOST_TEST_EQ(raii::copy_assignment, 0u); + BOOST_TEST_EQ(raii::move_assignment, 0u); + } + + check_raii_counts(); + + { + raii::reset_counts(); + + flat_map_type flat_map(values.begin(), values.end(), values.size(), + hasher(1), key_equal(2), allocator_type(3)); + + map_type map(0, hasher(2), key_equal(1), allocator_type(4)); + + BOOST_TEST(flat_map.get_allocator() != map.get_allocator()); + + map = std::move(flat_map); + + BOOST_TEST(flat_map.empty()); + BOOST_TEST_EQ(map.size(), reference_map.size()); + + test_fuzzy_matches_reference(map, reference_map, rg); + + BOOST_TEST_EQ(map.hash_function(), hasher(1)); + BOOST_TEST_EQ(map.key_eq(), key_equal(2)); + + BOOST_TEST_EQ(raii::copy_constructor, 2 * values.size()); + BOOST_TEST_EQ( + raii::destructor, 2 * values.size() + 2 * reference_map.size()); + BOOST_TEST_EQ(raii::move_constructor, 4 * reference_map.size()); + BOOST_TEST_EQ(raii::copy_assignment, 0u); + BOOST_TEST_EQ(raii::move_assignment, 0u); + } + + check_raii_counts(); + + { + raii::reset_counts(); + + map_type map(values.begin(), values.end(), values.size(), hasher(1), + key_equal(2), allocator_type(3)); + + flat_map_type flat_map(0, hasher(2), key_equal(1), allocator_type(4)); + + BOOST_TEST(flat_map.get_allocator() != map.get_allocator()); + + flat_map = std::move(map); + + BOOST_TEST(map.empty()); + BOOST_TEST_EQ(flat_map.size(), reference_map.size()); + + BOOST_TEST_EQ(flat_map.hash_function(), hasher(1)); + BOOST_TEST_EQ(flat_map.key_eq(), key_equal(2)); + + BOOST_TEST_EQ(raii::copy_constructor, 2 * values.size()); + BOOST_TEST_EQ( + raii::destructor, 2 * values.size() + 2 * reference_map.size()); + BOOST_TEST_EQ(raii::move_constructor, 4 * reference_map.size()); + BOOST_TEST_EQ(raii::copy_assignment, 0u); + BOOST_TEST_EQ(raii::move_assignment, 0u); + } + + check_raii_counts(); + } + } // namespace // clang-format off @@ -860,6 +993,11 @@ UNORDERED_TEST( insert_and_assign, ((init_type_generator)) ((default_generator)(sequential)(limited_range))) + +UNORDERED_TEST( + flat_map_move_assign, + ((init_type_generator)) + ((default_generator)(sequential)(limited_range))) // clang-format on RUN_TESTS()