From 6215406c02fcb0acc36eb38ffeb9ecbc8715c17b Mon Sep 17 00:00:00 2001 From: joaquintides Date: Wed, 1 May 2024 18:15:14 +0200 Subject: [PATCH] added tests for stats feature --- test/Jamfile.v2 | 2 + test/cfoa/stats_tests.cpp | 6 + test/unordered/stats_test.cpp | 255 ++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 test/cfoa/stats_tests.cpp create mode 100644 test/unordered/stats_test.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index f487247a..dc2cccca 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -228,6 +228,7 @@ local FOA_TESTS = hash_is_avalanching_test fancy_pointer_noleak pmr_allocator_tests + stats_tests ; for local test in $(FOA_TESTS) @@ -333,6 +334,7 @@ local CFOA_TESTS = reentrancy_check_test explicit_alloc_ctor_tests pmr_allocator_tests + stats_tests ; for local test in $(CFOA_TESTS) diff --git a/test/cfoa/stats_tests.cpp b/test/cfoa/stats_tests.cpp new file mode 100644 index 00000000..043f7570 --- /dev/null +++ b/test/cfoa/stats_tests.cpp @@ -0,0 +1,6 @@ +// Copyright 2024 Joaquin M Lopez Muoz. +// 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) + +#define BOOST_UNORDERED_CFOA_TESTS +#include "../unordered/stats_tests.cpp" diff --git a/test/unordered/stats_test.cpp b/test/unordered/stats_test.cpp new file mode 100644 index 00000000..500104cc --- /dev/null +++ b/test/unordered/stats_test.cpp @@ -0,0 +1,255 @@ +// Copyright 2024 Joaquin M Lopez Muoz. +// 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) + +#define BOOST_UNORDERED_ENABLE_STATS + +#ifdef BOOST_UNORDERED_CFOA_TESTS +#include +#include +#include "../cfoa/helpers.hpp" +#else +#include "../helpers/unordered.hpp" +#endif + +#include "../helpers/helpers.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/test.hpp" + +template struct unequal_allocator +{ + typedef T value_type; + + unequal_allocator(int n_ = 0): n{n_} {} + unequal_allocator(unequal_allocator const&) = default; + unequal_allocator(unequal_allocator&&) = default; + + template + unequal_allocator(unequal_allocator const& x): n{x.n} {} + + BOOST_ATTRIBUTE_NODISCARD T* allocate(std::size_t n) + { + return static_cast(::operator new(n * sizeof(T))); + } + + void deallocate(T* p, std::size_t) noexcept { ::operator delete(p); } + + bool operator==(unequal_allocator const& x) const { return n == x.n; } + bool operator!=(unequal_allocator const& x) const { return n != x.n; } + + int n; +}; + +template void check_stat(const Stats& s, bool full) +{ + if (full) { + BOOST_TEST_NE(s.average, 0.0); + if(s.variance) { + BOOST_TEST_NE(s.deviation, 0.0); + } + } + else { + BOOST_TEST_EQ(s.average, 0.0); + BOOST_TEST_EQ(s.variance, 0.0); + BOOST_TEST_EQ(s.deviation, 0.0); + } +} + +template void check_stat(const Stats& s1, const Stats& s2) +{ + BOOST_TEST_EQ(s1.average, s2.average); + BOOST_TEST_EQ(s1.variance, s2.variance); + BOOST_TEST_EQ(s1.deviation, s2.deviation); +} + +template void check_insertion_stats(const Stats& s, bool full) +{ + if (full) { + BOOST_TEST_NE(s.count, 0); + } + else { + BOOST_TEST_EQ(s.count, 0); + } + check_stat(s.probe_length, full); +} + +template +void check_insertion_stats(const Stats& s1, const Stats& s2) +{ + BOOST_TEST_EQ(s1.count, s2.count); + check_stat(s1.probe_length, s2.probe_length); +} + +template void check_lookup_stats(const Stats& s, bool full) +{ + if (full) { + BOOST_TEST_NE(s.count, 0); + } + else { + BOOST_TEST_EQ(s.count, 0); + } + check_stat(s.probe_length, full); + check_stat(s.num_comparisons, full); +} + +template +void check_lookup_stats(const Stats& s1, const Stats& s2) +{ + BOOST_TEST_EQ(s1.count, s2.count); + check_stat(s1.probe_length, s2.probe_length); + check_stat(s1.num_comparisons, s2.num_comparisons); +} + +template void check_container_stats(const Stats& s, bool full) +{ + check_insertion_stats(s.insertion, full); + check_lookup_stats(s.successful_lookup, full); + check_lookup_stats(s.unsuccessful_lookup, full); +} + +template +void check_container_stats(const Stats& s1, const Stats& s2) +{ + check_insertion_stats(s1.insertion, s2.insertion); + check_lookup_stats(s1.successful_lookup, s2.successful_lookup); + check_lookup_stats(s1.unsuccessful_lookup, s2.unsuccessful_lookup); +} + +template void test_stats() +{ + using value_type = Container::value_type; + using allocator_type = Container::allocator_type; + using stats = Container::stats; + const bool full = true, empty = false; + + Container c; + const Container& cc = c; + + stats s = cc.get_stats(); + check_container_stats(s, empty); + + test::reset_sequence(); + +#if defined(BOOST_UNORDERED_CFOA_TESTS) + + test::random_values l(10000, test::sequential); + std::vector v(l.begin(), l.end()); + thread_runner(v, [&c](boost::span s) { + for (auto const& x : s) { + c.insert(x); + } + }); + +#else + + test::random_values v(10000, test::sequential); + c.insert(v.begin(),v.end()); + +#endif + + s = cc.get_stats(); + check_insertion_stats(s.insertion, full); + check_lookup_stats(s.successful_lookup, empty); + check_lookup_stats(s.unsuccessful_lookup, full); + +#if !defined(BOOST_UNORDERED_CFOA_TESTS) + // Due to rehashing, may not hold in concurrent containers + // because of insertion retries + BOOST_TEST_GT( + s.insertion.count, s.unsuccessful_lookup.count); +#endif + + c.reset_stats(); + s = cc.get_stats(); + check_container_stats(s, empty); + + test::reset_sequence(); + +#if defined(BOOST_UNORDERED_CFOA_TESTS) + + test::random_values l2(15000, test::sequential); + std::vector v2(l2.begin(), l2.end()); + std::atomic found = 0, not_found = 0; + thread_runner(v2, [&cc, &found, ¬_found](boost::span s) { + for (auto const& x : s) { + if(cc.contains(test::get_key(x))) ++found; + else ++not_found; + } + }); + +#else + + test::random_values v2(15000, test::sequential); + int found = 0, not_found = 0; + for (const auto& x: v2) { + if (cc.contains(test::get_key(x))) ++found; + else ++not_found; + } + +#endif + + s=cc.get_stats(); + check_lookup_stats(s.successful_lookup, full); + check_lookup_stats(s.unsuccessful_lookup, full); + BOOST_TEST_EQ(s.successful_lookup.count, found); + BOOST_TEST_EQ(s.unsuccessful_lookup.count, not_found); + + c.reset_stats(); + s = cc.get_stats(); + check_container_stats(s, empty); + + test::reset_sequence(); + test::random_values v3(1000, test::sequential); + c.clear(); + c.insert(v.begin(),v.end()); + c.insert(v.begin(),v.end()); // produces successful lookups + + s = cc.get_stats(); + Container c2 = std::move(c); + check_container_stats(c.get_stats(), empty); + check_container_stats(c2.get_stats(), s); + + Container c3(std::move(c2), allocator_type()); + check_container_stats(c2.get_stats(), empty); + check_container_stats(c3.get_stats(), s); + + Container c4(std::move(c3), allocator_type(1)); + check_container_stats(c3.get_stats(), empty); + check_insertion_stats(c4.get_stats().insertion, full); + check_lookup_stats(c4.get_stats().successful_lookup, empty); + check_lookup_stats(c4.get_stats().unsuccessful_lookup, empty); + + // TODO: move assignment + // TODO: concurrent<->unordered interop +} + +UNORDERED_AUTO_TEST (stats) { +#if defined(BOOST_UNORDERED_CFOA_TESTS) + test_stats< + boost::concurrent_flat_map< + int, int, boost::hash, std::equal_to, + unequal_allocator< std::pair< const int, int> >>>(); + test_stats< + boost::concurrent_flat_set< + int, boost::hash, std::equal_to, unequal_allocator>>(); +#elif defined(BOOST_UNORDERED_FOA_TESTS) + test_stats< + boost::unordered_flat_map< + int, int, boost::hash, std::equal_to, + unequal_allocator< std::pair< const int, int> >>>(); + test_stats< + boost::unordered_flat_set< + int, boost::hash, std::equal_to, unequal_allocator>>(); + test_stats< + boost::unordered_node_map< + int, int, boost::hash, std::equal_to, + unequal_allocator< std::pair< const int, int> >>>(); + test_stats< + boost::unordered_node_set< + int, boost::hash, std::equal_to, unequal_allocator>>(); +#else + // Closed-addressing containers do not provide stats +#endif +} + +RUN_TESTS()