implemented proper stats handling on move assignment

This commit is contained in:
joaquintides
2024-05-02 20:41:27 +02:00
parent d121c91c88
commit 8452b30608
2 changed files with 81 additions and 34 deletions

View File

@ -1162,6 +1162,7 @@ struct table_core_stats
#define BOOST_UNORDERED_ADD_STATS(stats,args) stats.add args
#define BOOST_UNORDERED_SWAP_STATS(stats1,stats2) std::swap(stats1,stats2)
#define BOOST_UNORDERED_COPY_STATS(stats1,stats2) stats1=stats2
#define BOOST_UNORDERED_RESET_STATS_OF(x) x.reset_stats()
#define BOOST_UNORDERED_STATS_COUNTER(name) std::size_t name=0
#define BOOST_UNORDERED_INCREMENT_STATS_COUNTER(name) ++name
@ -1170,6 +1171,7 @@ struct table_core_stats
#define BOOST_UNORDERED_ADD_STATS(stats,args) ((void)0)
#define BOOST_UNORDERED_SWAP_STATS(stats1,stats2) ((void)0)
#define BOOST_UNORDERED_COPY_STATS(stats1,stats2) ((void)0)
#define BOOST_UNORDERED_RESET_STATS_OF(x) ((void)0)
#define BOOST_UNORDERED_STATS_COUNTER(name) ((void)0)
#define BOOST_UNORDERED_INCREMENT_STATS_COUNTER(name) ((void)0)
@ -1633,9 +1635,11 @@ public:
arrays=x.arrays;
size_ctrl.ml=std::size_t(x.size_ctrl.ml);
size_ctrl.size=std::size_t(x.size_ctrl.size);
BOOST_UNORDERED_COPY_STATS(cstats,x.cstats);
x.arrays=ah.release();
x.size_ctrl.ml=x.initial_max_load();
x.size_ctrl.size=0;
BOOST_UNORDERED_RESET_STATS_OF(x);
}
else{
swap(h(),x.h());
@ -1645,6 +1649,7 @@ public:
noshrink_reserve(x.size());
clear_on_exit c{x};
(void)c; /* unused var warning */
BOOST_UNORDERED_RESET_STATS_OF(x);
/* This works because subsequent x.clear() does not depend on the
* elements' values.

View File

@ -129,6 +129,26 @@ void check_container_stats(const Stats& s1, const Stats& s2)
check_lookup_stats(s1.unsuccessful_lookup, s2.unsuccessful_lookup);
}
template <class Container> void insert_n(Container& c, std::size_t n)
{
#if defined(BOOST_UNORDERED_CFOA_TESTS)
using value_type = typename Container::value_type;
test::reset_sequence();
test::random_values<Container> l(n, test::sequential);
std::vector<value_type> v(l.begin(), l.end());
thread_runner(v, [&c](boost::span<value_type> sp) {
for (auto const& x : sp) {
c.insert(x);
}
});
#else
test::reset_sequence();
test::random_values<Container> l(n, test::sequential);
c.insert(l.begin(), l.end());
#endif
}
template <class Container> void test_stats()
{
using allocator_type = typename Container::allocator_type;
@ -138,49 +158,36 @@ template <class Container> void test_stats()
Container c;
const Container& cc = c;
stats s = cc.get_stats();
// Stats initially empty
stats s = cc.get_stats(); // using cc -> get_stats() is const
check_container_stats(s, empty);
test::reset_sequence();
#if defined(BOOST_UNORDERED_CFOA_TESTS)
using value_type = typename Container::value_type;
test::random_values<Container> l(10000, test::sequential);
std::vector<value_type> v(l.begin(), l.end());
thread_runner(v, [&c](boost::span<value_type> sp) {
for (auto const& x : sp) {
c.insert(x);
}
});
#else
test::random_values<Container> v(10000, test::sequential);
c.insert(v.begin(),v.end());
#endif
// Stats after insertion
insert_n(c, 10000);
s = cc.get_stats();
check_insertion_stats(s.insertion, full);
check_lookup_stats(s.successful_lookup, empty);
check_lookup_stats(s.unsuccessful_lookup, full);
check_insertion_stats(s.insertion, full); // insertions happened
check_lookup_stats(s.successful_lookup, empty); // no duplicate values
check_lookup_stats(s.unsuccessful_lookup, full); // from insertion
#if !defined(BOOST_UNORDERED_CFOA_TESTS)
// Inequality due to rehashing.
// May not hold in concurrent containers because of insertion retries.
// Inequality due to rehashing
// May not hold in concurrent containers because of insertion retries
BOOST_TEST_GT(
s.insertion.count, s.unsuccessful_lookup.count);
#endif
// resets_stats() actually clears stats
c.reset_stats();
s = cc.get_stats();
check_container_stats(s, empty);
check_container_stats(cc.get_stats(), empty);
// Stats after lookup
test::reset_sequence();
#if defined(BOOST_UNORDERED_CFOA_TESTS)
using value_type = typename Container::value_type;
test::random_values<Container> l2(15000, test::sequential);
std::vector<value_type> v2(l2.begin(), l2.end());
std::atomic<int> found{0}, not_found{0};
@ -202,6 +209,7 @@ template <class Container> void test_stats()
#endif
// As many [un]successful lookups as recorded externally
s=cc.get_stats();
check_lookup_stats(s.successful_lookup, full);
check_lookup_stats(s.unsuccessful_lookup, full);
@ -212,28 +220,62 @@ template <class Container> void test_stats()
s = cc.get_stats();
check_container_stats(s, empty);
test::reset_sequence();
test::random_values<Container> v3(1000, test::sequential);
c.clear();
c.insert(v.begin(),v.end());
c.insert(v.begin(),v.end()); // produces successful lookups
// Move constructor tests
c.clear();
insert_n(c, 1000);
insert_n(c, 1000); // produces successful lookups
// Move contructor
// Stats transferred to target and reset in source
s = cc.get_stats();
Container c2 = std::move(c);
check_container_stats(c.get_stats(), empty);
check_container_stats(c2.get_stats(), s);
// Move constructor with equal allocator
// Stats transferred to target and reset in source
Container c3(std::move(c2), allocator_type());
check_container_stats(c2.get_stats(), empty);
check_container_stats(c3.get_stats(), s);
// Move constructor with unequal allocator
// Target only has insertions, stats reset in source
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
// Move assignment tests
// Move assignment with equal allocator
// Stats transferred to target and reset in source
Container c5, c6;
insert_n(c5,1000);
insert_n(c5,1000); // produces successful lookups
insert_n(c6,500);
insert_n(c6,500); // produces successful lookups
s = c5.get_stats();
check_container_stats(s, full);
check_container_stats(c6.get_stats(), full);
c6 = std::move(c5);
check_container_stats(c5.get_stats(), empty);
check_container_stats(c6.get_stats(), s);
// Move assignment with unequal allocator
// Target only has insertions (if reset previously), stats reset in source
Container c7(allocator_type(1));
insert_n(c7,250);
insert_n(c7,250); // produces successful lookups
check_container_stats(c7.get_stats(), full);
c7.reset_stats();
c7 = std::move(c6);
check_container_stats(c6.get_stats(), empty);
check_insertion_stats(c7.get_stats().insertion, full);
check_lookup_stats(c7.get_stats().successful_lookup, empty);
check_lookup_stats(c7.get_stats().unsuccessful_lookup, empty);
// TODO: concurrent<->unordered interop
}