diff --git a/test/cfoa/helpers.hpp b/test/cfoa/helpers.hpp new file mode 100644 index 00000000..45d9af40 --- /dev/null +++ b/test/cfoa/helpers.hpp @@ -0,0 +1,229 @@ +#ifndef BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP +#define BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP + +#include "../helpers/generators.hpp" +#include "../helpers/test.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +constexpr std::size_t const num_threads = 16; + +struct transparent_hash +{ + using is_transparent = void; + + template std::size_t operator()(T const& t) const noexcept + { + return boost::hash()(t); + } +}; + +struct transparent_key_equal +{ + using is_transparent = void; + + template bool operator()(T const& lhs, U const& rhs) const + { + return lhs == rhs; + } +}; + +struct raii +{ + static std::atomic default_constructor; + static std::atomic copy_constructor; + static std::atomic move_constructor; + static std::atomic destructor; + + static std::atomic copy_assignment; + static std::atomic move_assignment; + + int x_ = -1; + + raii() { ++default_constructor; } + raii(int const x) : x_{x} { ++default_constructor; } + raii(raii const& rhs) : x_{rhs.x_} { ++copy_constructor; } + raii(raii&& rhs) noexcept : x_{rhs.x_} + { + rhs.x_ = -1; + ++move_constructor; + } + ~raii() { ++destructor; } + + raii& operator=(raii const& rhs) + { + ++copy_assignment; + if (this != &rhs) { + x_ = rhs.x_; + } + return *this; + } + + raii& operator=(raii&& rhs) noexcept + { + ++move_assignment; + if (this != &rhs) { + x_ = rhs.x_; + rhs.x_ = -1; + } + return *this; + } + + friend bool operator==(raii const& lhs, raii const& rhs) + { + return lhs.x_ == rhs.x_; + } + + friend bool operator!=(raii const& lhs, raii const& rhs) + { + 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==(int const x, raii const& rhs) { return rhs.x_ == x; } + + friend bool operator!=(int const x, raii const& rhs) + { + return !(rhs.x_ == x); + } + + friend std::ostream& operator<<(std::ostream& os, raii const& rhs) + { + os << "{ x_: " << rhs.x_ << " }"; + return os; + } + + friend std::ostream& operator<<( + std::ostream& os, std::pair const& rhs) + { + os << "pair<" << rhs.first << ", " << rhs.second << ">"; + return os; + } + + static void reset_counts() + { + default_constructor = 0; + copy_constructor = 0; + move_constructor = 0; + destructor = 0; + copy_assignment = 0; + move_assignment = 0; + } +}; + +std::atomic raii::default_constructor{0}; +std::atomic raii::copy_constructor{0}; +std::atomic raii::move_constructor{0}; +std::atomic raii::destructor{0}; +std::atomic raii::copy_assignment{0}; +std::atomic raii::move_assignment{0}; + +std::size_t hash_value(raii const& r) noexcept +{ + boost::hash hasher; + return hasher(r.x_); +} + +template +auto make_random_values(std::size_t count, F f) -> std::vector +{ + using vector_type = std::vector; + + vector_type v; + v.reserve(count); + for (std::size_t i = 0; i < count; ++i) { + v.emplace_back(f()); + } + return v; +} + +struct value_type_generator_type +{ + std::pair operator()(test::random_generator rg) + { + int* p = nullptr; + int a = generate(p, rg); + int b = generate(p, rg); + return std::make_pair(raii{a}, raii{b}); + } +} value_type_generator; + +struct init_type_generator_type +{ + std::pair operator()(test::random_generator rg) + { + int* p = nullptr; + int a = generate(p, rg); + int b = generate(p, rg); + return std::make_pair(raii{a}, raii{b}); + } +} init_type_generator; + +template +std::vector > split( + boost::span s, std::size_t const nt /* num threads*/) +{ + std::vector > subslices; + subslices.reserve(nt); + + auto a = s.size() / nt; + auto b = a; + if (s.size() % nt != 0) { + ++b; + } + + auto num_a = nt; + auto num_b = std::size_t{0}; + + if (nt * b > s.size()) { + num_a = nt * b - s.size(); + num_b = nt - num_a; + } + + auto sub_b = s.subspan(0, num_b * b); + auto sub_a = s.subspan(num_b * b); + + for (std::size_t i = 0; i < num_b; ++i) { + subslices.push_back(sub_b.subspan(i * b, b)); + } + + for (std::size_t i = 0; i < num_a; ++i) { + auto const is_last = i == (num_a - 1); + subslices.push_back( + sub_a.subspan(i * a, is_last ? boost::dynamic_extent : a)); + } + + return subslices; +} + +template void thread_runner(std::vector& values, F f) +{ + std::vector threads; + auto subslices = split(values, num_threads); + + for (std::size_t i = 0; i < num_threads; ++i) { + threads.emplace_back([&f, &subslices, i] { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + auto s = subslices[i]; + f(s); + }); + } + for (auto& t : threads) { + t.join(); + } +} + +#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP \ No newline at end of file diff --git a/test/cfoa/insert_tests.cpp b/test/cfoa/insert_tests.cpp index 4ceae25e..79caea1e 100644 --- a/test/cfoa/insert_tests.cpp +++ b/test/cfoa/insert_tests.cpp @@ -2,237 +2,15 @@ // 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/generators.hpp" -#include "../helpers/test.hpp" +#include "helpers.hpp" #include -#include #include -#include -#include - -#include -#include -#include -#include -#include - -constexpr std::size_t const num_threads = 16; - -struct transparent_hash -{ - using is_transparent = void; - - template std::size_t operator()(T const& t) const noexcept - { - return boost::hash()(t); - } -}; - -struct transparent_key_equal -{ - using is_transparent = void; - - template bool operator()(T const& lhs, U const& rhs) const - { - return lhs == rhs; - } -}; - -struct raii -{ - static std::atomic default_constructor; - static std::atomic copy_constructor; - static std::atomic move_constructor; - static std::atomic destructor; - - static std::atomic copy_assignment; - static std::atomic move_assignment; - - int x_ = -1; - - raii() { ++default_constructor; } - raii(int const x) : x_{x} { ++default_constructor; } - raii(raii const& rhs) : x_{rhs.x_} { ++copy_constructor; } - raii(raii&& rhs) noexcept : x_{rhs.x_} - { - rhs.x_ = -1; - ++move_constructor; - } - ~raii() { ++destructor; } - - raii& operator=(raii const& rhs) - { - ++copy_assignment; - if (this != &rhs) { - x_ = rhs.x_; - } - return *this; - } - - raii& operator=(raii&& rhs) noexcept - { - ++move_assignment; - if (this != &rhs) { - x_ = rhs.x_; - rhs.x_ = -1; - } - return *this; - } - - friend bool operator==(raii const& lhs, raii const& rhs) - { - return lhs.x_ == rhs.x_; - } - - friend bool operator!=(raii const& lhs, raii const& rhs) - { - 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==(int const x, raii const& rhs) { return rhs.x_ == x; } - - friend bool operator!=(int const x, raii const& rhs) - { - return !(rhs.x_ == x); - } - - friend std::ostream& operator<<(std::ostream& os, raii const& rhs) - { - os << "{ x_: " << rhs.x_ << " }"; - return os; - } - - friend std::ostream& operator<<( - std::ostream& os, std::pair const& rhs) - { - os << "pair<" << rhs.first << ", " << rhs.second << ">"; - return os; - } - - static void reset_counts() - { - default_constructor = 0; - copy_constructor = 0; - move_constructor = 0; - destructor = 0; - copy_assignment = 0; - move_assignment = 0; - } -}; - -std::atomic raii::default_constructor{0}; -std::atomic raii::copy_constructor{0}; -std::atomic raii::move_constructor{0}; -std::atomic raii::destructor{0}; -std::atomic raii::copy_assignment{0}; -std::atomic raii::move_assignment{0}; - -std::size_t hash_value(raii const& r) noexcept -{ - boost::hash hasher; - return hasher(r.x_); -} - -template -auto make_random_values(std::size_t count, F f) -> std::vector -{ - using vector_type = std::vector; - - vector_type v; - v.reserve(count); - for (std::size_t i = 0; i < count; ++i) { - v.emplace_back(f()); - } - return v; -} namespace { test::seed_t initialize_seed(78937); - struct value_type_generator_type - { - std::pair operator()(test::random_generator rg) - { - int* p = nullptr; - int a = generate(p, rg); - int b = generate(p, rg); - return std::make_pair(raii{a}, raii{b}); - } - } value_type_generator; - - struct init_type_generator_type - { - std::pair operator()(test::random_generator rg) - { - int* p = nullptr; - int a = generate(p, rg); - int b = generate(p, rg); - return std::make_pair(raii{a}, raii{b}); - } - } init_type_generator; - - template - std::vector > split( - boost::span s, std::size_t const nt /* num threads*/) - { - std::vector > subslices; - subslices.reserve(nt); - - auto a = s.size() / nt; - auto b = a; - if (s.size() % nt != 0) { - ++b; - } - - auto num_a = nt; - auto num_b = std::size_t{0}; - - if (nt * b > s.size()) { - num_a = nt * b - s.size(); - num_b = nt - num_a; - } - - auto sub_b = s.subspan(0, num_b * b); - auto sub_a = s.subspan(num_b * b); - - for (std::size_t i = 0; i < num_b; ++i) { - subslices.push_back(sub_b.subspan(i * b, b)); - } - - for (std::size_t i = 0; i < num_a; ++i) { - auto const is_last = i == (num_a - 1); - subslices.push_back( - sub_a.subspan(i * a, is_last ? boost::dynamic_extent : a)); - } - - return subslices; - } - - template void thread_runner(std::vector& values, F f) - { - std::vector threads; - auto subslices = split(values, num_threads); - - for (std::size_t i = 0; i < num_threads; ++i) { - threads.emplace_back([&f, &subslices, i] { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - auto s = subslices[i]; - f(s); - }); - } - for (auto& t : threads) { - t.join(); - } - } - struct lvalue_inserter_type { template void operator()(std::vector& values, X& x)