From 21afc698941bb920dd68404ad1bbf5d14fc6b6b6 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Wed, 10 May 2023 13:22:02 -0700 Subject: [PATCH] Add initial tests for rehash(), reserve() --- .../boost/unordered/concurrent_flat_map.hpp | 12 ++ test/Jamfile.v2 | 1 + test/cfoa/helpers.hpp | 3 +- test/cfoa/rehash_tests.cpp | 181 ++++++++++++++++++ 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 test/cfoa/rehash_tests.cpp diff --git a/include/boost/unordered/concurrent_flat_map.hpp b/include/boost/unordered/concurrent_flat_map.hpp index e3283a80..3f2bb793 100644 --- a/include/boost/unordered/concurrent_flat_map.hpp +++ b/include/boost/unordered/concurrent_flat_map.hpp @@ -742,6 +742,18 @@ namespace boost { /// Hash Policy /// + size_type bucket_count() const noexcept { + return table_.capacity(); + } + + float load_factor() const noexcept { return table_.load_factor(); } + float max_load_factor() const noexcept + { + return table_.max_load_factor(); + }; + void max_load_factor(float) {} + size_type max_load() const noexcept { return table_.max_load(); } + void rehash(size_type n) { table_.rehash(n); } void reserve(size_type n) { table_.reserve(n); } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 57be13da..ce98085c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -187,6 +187,7 @@ local CFOA_TESTS = clear_tests swap_tests merge_tests + rehash_tests ; for local test in $(CFOA_TESTS) diff --git a/test/cfoa/helpers.hpp b/test/cfoa/helpers.hpp index 59a35be7..116664a6 100644 --- a/test/cfoa/helpers.hpp +++ b/test/cfoa/helpers.hpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -398,7 +399,7 @@ void check_raii_counts() raii::destructor); } -template void shuffle_values(std::vector v) +template void shuffle_values(std::vector& v) { std::random_device rd; std::mt19937 g(rd()); diff --git a/test/cfoa/rehash_tests.cpp b/test/cfoa/rehash_tests.cpp new file mode 100644 index 00000000..2d9d9fe1 --- /dev/null +++ b/test/cfoa/rehash_tests.cpp @@ -0,0 +1,181 @@ +// Copyright (C) 2023 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include "helpers.hpp" + +#include + +using test::default_generator; +using test::limited_range; +using test::sequential; + +using hasher = stateful_hash; +using key_equal = stateful_key_equal; +using allocator_type = stateful_allocator >; + +using map_type = boost::unordered::concurrent_flat_map; + +using map_value_type = typename map_type::value_type; + +namespace { + test::seed_t initialize_seed{748775921}; + + UNORDERED_AUTO_TEST (rehash_no_insert) { + map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + BOOST_TEST_EQ(x.bucket_count(), 0u); + + x.rehash(1024); + BOOST_TEST_GE(x.bucket_count(), 1024u); + + x.rehash(512); + BOOST_TEST_GE(x.bucket_count(), 512u); + BOOST_TEST_LT(x.bucket_count(), 1024u); + + x.rehash(0); + BOOST_TEST_EQ(x.bucket_count(), 0u); + } + + UNORDERED_AUTO_TEST (reserve_no_insert) { + using size_type = map_type::size_type; + + map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + + auto f = [&x](double c) { + return static_cast(std::ceil(c / x.max_load_factor())); + }; + + BOOST_TEST_EQ(x.bucket_count(), f(0.0)); + + x.reserve(1024); + BOOST_TEST_GE(x.bucket_count(), f(1024.0)); + + x.reserve(512); + BOOST_TEST_GE(x.bucket_count(), f(512.0)); + BOOST_TEST_LT(x.bucket_count(), f(1024.0)); + + x.reserve(0); + BOOST_TEST_EQ(x.bucket_count(), f(0.0)); + } + + template + void insert_and_erase_with_rehash(G gen, test::random_generator rg) + { + auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); }); + + auto erase_indices = std::vector(vals1.size()); + for (std::size_t idx = 0; idx < erase_indices.size(); ++idx) { + erase_indices[idx] = idx; + } + shuffle_values(erase_indices); + + auto ref_map = boost::unordered_flat_map(); + ref_map.insert(vals1.begin(), vals1.end()); + + { + raii::reset_counts(); + + map_type x(0, hasher(1), key_equal(2), allocator_type(3)); + + std::thread t1, t2, t3; + boost::latch l(2); + + std::mutex m; + std::condition_variable cv; + std::atomic_bool done1{false}, done2{false}; + std::atomic call_count{0}; + bool ready = false; + + auto const old_mc = +raii::move_constructor; + BOOST_TEST_EQ(old_mc, 0u); + + t1 = std::thread([&x, &vals1, &l, &done1, &cv, &ready, &m] { + l.arrive_and_wait(); + + for (std::size_t idx = 0; idx < vals1.size(); ++idx) { + auto const& val = vals1[idx]; + x.insert(val); + + if (idx % (vals1.size() / 128) == 0) { + { + std::unique_lock lk(m); + ready = true; + } + cv.notify_all(); + std::this_thread::yield(); + } + } + + done1 = true; + { + std::unique_lock lk(m); + ready = true; + } + cv.notify_all(); + }); + + t2 = + std::thread([&x, &vals1, &erase_indices, &l, &done2, &cv, &m, &ready] { + l.arrive_and_wait(); + + for (std::size_t idx = 0; idx < erase_indices.size(); ++idx) { + auto const& val = vals1[erase_indices[idx]]; + x.erase(val.first); + if (idx % 100 == 0) { + std::this_thread::yield(); + } + } + + done2 = true; + { + std::unique_lock lk(m); + ready = true; + } + cv.notify_all(); + }); + + t3 = + std::thread([&x, &vals1, &m, &cv, &done1, &done2, &call_count, &ready] { + while (x.empty()) { + } + + do { + { + std::unique_lock lk(m); + cv.wait(lk, [&ready] { return ready; }); + ready = false; + } + + auto const bc = static_cast(rand()) % vals1.size(); + x.rehash(bc); + call_count += 1; + + std::this_thread::yield(); + } while (!done1 || !done2); + + BOOST_TEST(done1); + BOOST_TEST(done2); + }); + + t1.join(); + t2.join(); + t3.join(); + + BOOST_TEST_GT(call_count, 1u); + + test_fuzzy_matches_reference(x, ref_map, rg); + } + + check_raii_counts(); + } +} // namespace + +// clang-format off +UNORDERED_TEST( + insert_and_erase_with_rehash, + ((value_type_generator)) + ((default_generator)(sequential)(limited_range))) +// clang-format on + +RUN_TESTS()