From e4072747bb9ddc8850c634da108124b19ea42625 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 28 Mar 2023 14:28:42 -0700 Subject: [PATCH] Implement insert_or_visit() --- .../boost/unordered/concurrent_flat_map.hpp | 34 +++ test/cfoa/insert_tests.cpp | 276 +++++++++++++++--- 2 files changed, 277 insertions(+), 33 deletions(-) diff --git a/include/boost/unordered/concurrent_flat_map.hpp b/include/boost/unordered/concurrent_flat_map.hpp index a20a6776..5b31af89 100644 --- a/include/boost/unordered/concurrent_flat_map.hpp +++ b/include/boost/unordered/concurrent_flat_map.hpp @@ -175,6 +175,40 @@ namespace boost { std::forward(obj)); } + template bool insert_or_visit(value_type const& obj, F f) + { + return table_.insert_or_visit(obj, std::move(f)); + } + + template bool insert_or_visit(value_type&& obj, F f) + { + return table_.insert_or_visit(std::move(obj), std::move(f)); + } + + template bool insert_or_visit(init_type const& obj, F f) + { + return table_.insert_or_visit(obj, std::move(f)); + } + + template bool insert_or_visit(init_type&& obj, F f) + { + return table_.insert_or_visit(std::move(obj), std::move(f)); + } + + template + void insert_or_visit(InputIterator first, InputIterator last, F f) + { + for (; first != last; ++first) { + table_.insert_or_visit(*first, f); + } + } + + template + void insert_or_visit(std::initializer_list ilist, F f) + { + this->insert_or_visit(ilist.begin(), ilist.end(), std::move(f)); + } + /// Hash Policy /// void rehash(size_type n) { table_.rehash(n); } diff --git a/test/cfoa/insert_tests.cpp b/test/cfoa/insert_tests.cpp index c860c2ff..3e0ea267 100644 --- a/test/cfoa/insert_tests.cpp +++ b/test/cfoa/insert_tests.cpp @@ -91,19 +91,13 @@ struct raii return !(lhs == rhs); } - friend bool operator==(raii const& lhs, int const x) - { - return lhs.x_ == x; - } + friend bool operator==(raii const& lhs, int const x) { return lhs.x_ == x; } friend bool operator!=(raii const& lhs, int const x) { return !(lhs.x_ == x); } - friend bool operator==(int const x, raii const& rhs) - { - return rhs.x_ == x; - } + friend bool operator==(int const x, raii const& rhs) { return rhs.x_ == x; } friend bool operator!=(int const x, raii const& rhs) { @@ -396,6 +390,8 @@ namespace { }); BOOST_TEST_EQ(raii::default_constructor, 0); + BOOST_TEST_EQ(raii::copy_constructor, 0); + BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); BOOST_TEST_EQ(raii::copy_assignment, 0); BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size()); } @@ -451,6 +447,179 @@ namespace { } } transparent_insert_or_assign_move_assign; + struct lvalue_insert_or_visit_const_visitor_type + { + template void operator()(std::vector& values, X& x) + { + std::atomic num_inserts{0}; + std::atomic num_invokes{0}; + thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { + for (auto& r : s) { + bool b = x.insert_or_visit( + r, [&num_invokes](typename X::value_type const& v) { + (void)v; + ++num_invokes; + }); + + if (b) { + ++num_inserts; + } + } + }); + + BOOST_TEST_EQ(num_inserts, x.size()); + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ(raii::default_constructor, 0); + BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size()); + // don't check move construction count here because of rehashing + BOOST_TEST_GT(raii::move_constructor, 0); + BOOST_TEST_EQ(raii::move_assignment, 0); + } + } lvalue_insert_or_visit_const_visitor; + + struct lvalue_insert_or_visit_mut_visitor_type + { + template void operator()(std::vector& values, X& x) + { + std::atomic num_inserts{0}; + std::atomic num_invokes{0}; + thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { + for (auto& r : s) { + bool b = + x.insert_or_visit(r, [&num_invokes](typename X::value_type& v) { + (void)v; + ++num_invokes; + }); + + if (b) { + ++num_inserts; + } + } + }); + + BOOST_TEST_EQ(num_inserts, x.size()); + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ(raii::default_constructor, 0); + BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size()); + // don't check move construction count here because of rehashing + BOOST_TEST_GT(raii::move_constructor, 0); + BOOST_TEST_EQ(raii::move_assignment, 0); + } + } lvalue_insert_or_visit_mut_visitor; + + struct rvalue_insert_or_visit_const_visitor_type + { + template void operator()(std::vector& values, X& x) + { + std::atomic num_inserts{0}; + std::atomic num_invokes{0}; + thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { + for (auto& r : s) { + bool b = x.insert_or_visit( + std::move(r), [&num_invokes](typename X::value_type const& v) { + (void)v; + ++num_invokes; + }); + + if (b) { + ++num_inserts; + } + } + }); + + BOOST_TEST_EQ(num_inserts, x.size()); + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ(raii::default_constructor, 0); + + if (std::is_same::value) { + BOOST_TEST_EQ(raii::copy_constructor, x.size()); + BOOST_TEST_GE(raii::move_constructor, x.size()); + } else { + BOOST_TEST_EQ(raii::copy_constructor, 0); + BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); + } + } + } rvalue_insert_or_visit_const_visitor; + + struct rvalue_insert_or_visit_mut_visitor_type + { + template void operator()(std::vector& values, X& x) + { + std::atomic num_inserts{0}; + std::atomic num_invokes{0}; + thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span s) { + for (auto& r : s) { + bool b = x.insert_or_visit( + std::move(r), [&num_invokes](typename X::value_type& v) { + (void)v; + ++num_invokes; + }); + + if (b) { + ++num_inserts; + } + } + }); + + BOOST_TEST_EQ(num_inserts, x.size()); + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ(raii::default_constructor, 0); + if (std::is_same::value) { + BOOST_TEST_EQ(raii::copy_constructor, x.size()); + BOOST_TEST_GE(raii::move_constructor, x.size()); + } else { + BOOST_TEST_EQ(raii::copy_constructor, 0); + BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); + } + } + } rvalue_insert_or_visit_mut_visitor; + + struct iterator_range_insert_or_visit_const_visitor_type + { + template void operator()(std::vector& values, X& x) + { + std::atomic num_invokes{0}; + thread_runner(values, [&x, &num_invokes](boost::span s) { + x.insert_or_visit( + s.begin(), s.end(), [&num_invokes](typename X::value_type const& v) { + (void)v; + ++num_invokes; + }); + }); + + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ(raii::default_constructor, 0); + BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size()); + BOOST_TEST_GT(raii::move_constructor, 0); + } + } iterator_range_insert_or_visit_const_visitor; + + struct iterator_range_insert_or_visit_mut_visitor_type + { + template void operator()(std::vector& values, X& x) + { + std::atomic num_invokes{0}; + thread_runner(values, [&x, &num_invokes](boost::span s) { + x.insert_or_visit( + s.begin(), s.end(), [&num_invokes](typename X::value_type const& v) { + (void)v; + ++num_invokes; + }); + }); + + BOOST_TEST_EQ(num_invokes, values.size() - x.size()); + + BOOST_TEST_EQ(raii::default_constructor, 0); + BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size()); + BOOST_TEST_GT(raii::move_constructor, 0); + } + } iterator_range_insert_or_visit_mut_visitor; + template void insert(X*, G gen, F inserter, test::random_generator rg) { @@ -521,30 +690,75 @@ namespace { raii::reset_counts(); { - X x; + { + X x; - thread_runner( - dummy, [&x, &values](boost::span) { x.insert(values); }); + thread_runner( + dummy, [&x, &values](boost::span) { x.insert(values); }); - BOOST_TEST_EQ(x.size(), reference_map.size()); + BOOST_TEST_EQ(x.size(), reference_map.size()); - BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { - BOOST_TEST(reference_map.contains(kv.first)); - BOOST_TEST_EQ(kv.second, reference_map[kv.first]); - })); + BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(kv.second, reference_map[kv.first]); + })); + } + + BOOST_TEST_GE(raii::default_constructor, 0); + BOOST_TEST_GE(raii::copy_constructor, 0); + BOOST_TEST_GE(raii::move_constructor, 0); + BOOST_TEST_GT(raii::destructor, 0); + + BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor + + raii::move_constructor, + raii::destructor); + + BOOST_TEST_EQ(raii::copy_assignment, 0); + BOOST_TEST_EQ(raii::move_assignment, 0); } - BOOST_TEST_GE(raii::default_constructor, 0); - BOOST_TEST_GE(raii::copy_constructor, 0); - BOOST_TEST_GE(raii::move_constructor, 0); - BOOST_TEST_GT(raii::destructor, 0); + { + { + std::atomic num_invokes{0}; - BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor + - raii::move_constructor, - raii::destructor); + X x; - BOOST_TEST_EQ(raii::copy_assignment, 0); - BOOST_TEST_EQ(raii::move_assignment, 0); + thread_runner(dummy, [&x, &values, &num_invokes](boost::span) { + x.insert_or_visit(values, [&num_invokes](typename X::value_type& v) { + (void)v; + ++num_invokes; + }); + + x.insert_or_visit( + values, [&num_invokes](typename X::value_type const& v) { + (void)v; + ++num_invokes; + }); + }); + + BOOST_TEST_EQ(num_invokes, (values.size() - x.size()) + + (num_threads - 1) * values.size() + + num_threads * values.size()); + BOOST_TEST_EQ(x.size(), reference_map.size()); + + BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(kv.second, reference_map[kv.first]); + })); + } + + BOOST_TEST_GE(raii::default_constructor, 0); + BOOST_TEST_GE(raii::copy_constructor, 0); + BOOST_TEST_GE(raii::move_constructor, 0); + BOOST_TEST_GT(raii::destructor, 0); + + BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor + + raii::move_constructor, + raii::destructor); + + BOOST_TEST_EQ(raii::copy_assignment, 0); + BOOST_TEST_EQ(raii::move_assignment, 0); + } } boost::unordered::concurrent_flat_map* map; @@ -567,14 +781,10 @@ UNORDERED_TEST( ((map)) ((value_type_generator)(init_type_generator)) ((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter) - (norehash_lvalue_inserter)(norehash_rvalue_inserter)) - ((default_generator)(sequential)(limited_range))) - -UNORDERED_TEST( - insert, - ((map)) - ((value_type_generator)) - ((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)) + (norehash_lvalue_inserter)(norehash_rvalue_inserter) + (lvalue_insert_or_visit_const_visitor)(lvalue_insert_or_visit_mut_visitor) + (rvalue_insert_or_visit_const_visitor)(rvalue_insert_or_visit_mut_visitor) + (iterator_range_insert_or_visit_const_visitor)(iterator_range_insert_or_visit_mut_visitor)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST(