Merge pull request #176 from boostorg/feature/foa_fast_copy_fix

feature/foa fast copy fix
This commit is contained in:
Christian Mazakas
2022-12-13 13:52:59 -08:00
committed by GitHub
3 changed files with 307 additions and 9 deletions

View File

@ -31,6 +31,7 @@
#include <cstring>
#include <iterator>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
@ -1025,6 +1026,47 @@ inline void prefetch(const void* p)
struct try_emplace_args_t{};
template<typename Allocator>
struct is_std_allocator:std::false_type{};
template<typename T>
struct is_std_allocator<std::allocator<T>>:std::true_type{};
/* std::allocator::construct marked as deprecated */
#if defined(_LIBCPP_SUPPRESS_DEPRECATED_PUSH)
_LIBCPP_SUPPRESS_DEPRECATED_PUSH
#elif defined(_STL_DISABLE_DEPRECATED_WARNING)
_STL_DISABLE_DEPRECATED_WARNING
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4996)
#endif
template<typename Allocator,typename Ptr,typename... Args>
struct alloc_has_construct
{
private:
template<typename Allocator2>
static decltype(
std::declval<Allocator2&>().construct(
std::declval<Ptr>(),std::declval<Args&&>()...),
std::true_type{}
) check(int);
template<typename> static std::false_type check(...);
public:
static constexpr bool value=decltype(check<Allocator>(0))::value;
};
#if defined(_LIBCPP_SUPPRESS_DEPRECATED_POP)
_LIBCPP_SUPPRESS_DEPRECATED_POP
#elif defined(_STL_RESTORE_DEPRECATED_WARNING)
_STL_RESTORE_DEPRECATED_WARNING
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
#if defined(BOOST_GCC)
/* GCC's -Wshadow triggers at scenarios like this:
*
@ -1590,6 +1632,9 @@ private:
#else
std::is_trivially_copy_constructible<value_type>::value
#endif
&&(
is_std_allocator<Allocator>::value||
!alloc_has_construct<Allocator,value_type*,const value_type&>::value)
>{}
);
}

View File

@ -230,18 +230,18 @@ namespace test {
std::size_t operator()(int x) const
{
int result;
unsigned result;
switch (type_) {
case 1:
result = x;
result = static_cast<unsigned>(x);
break;
case 2:
result = x * 7;
result = static_cast<unsigned>(x) * 7;
break;
default:
result = x * 256;
result = static_cast<unsigned>(x) * 256;
}
return static_cast<std::size_t>(result);
return result;
}
friend bool operator==(hash const& x1, hash const& x2)

View File

@ -225,11 +225,227 @@ namespace copy_tests {
}
}
template <class T>
void copy_construct_tests_std_allocator1(
T*, test::random_generator const& generator)
{
typename T::hasher hf;
typename T::key_equal eq;
typename T::allocator_type al;
{
test::check_instances check_;
T x;
T y(x);
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
T x(0);
T y(x);
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
test::random_values<T> v(1000, generator);
T x(v.begin(), v.end());
T y(x);
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
// In this test I drop the original containers max load factor, so it
// is much lower than the load factor. The hash table is not allowed
// to rehash, but the destination container should probably allocate
// enough buckets to decrease the load factor appropriately.
test::random_values<T> v(1000, generator);
T x(v.begin(), v.end());
x.max_load_factor(x.load_factor() / 4);
T y(x);
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
// This isn't guaranteed:
BOOST_TEST(y.load_factor() < y.max_load_factor());
test::check_equivalent_keys(y);
}
}
template <class T>
void copy_construct_tests_std_allocator2(
T*, test::random_generator const& generator)
{
typename T::hasher hf(1);
typename T::key_equal eq(1);
typename T::allocator_type al;
{
test::check_instances check_;
T x(0, hf, eq, al);
T y(x);
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
T x(10000, hf, eq, al);
T y(x);
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
T x(0, hf, eq, al);
T y(x, al);
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) == 0);
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
T x(1000, hf, eq, al);
T y(x, al);
BOOST_TEST(y.empty());
BOOST_TEST(test::equivalent(y.hash_function(), hf));
BOOST_TEST(test::equivalent(y.key_eq(), eq));
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(x.max_load_factor() == y.max_load_factor());
BOOST_TEST(test::selected_count(y.get_allocator()) == 0);
test::check_equivalent_keys(y);
}
{
test::check_instances check_;
test::random_values<T> v;
T x(v.begin(), v.end(), 0, hf, eq, al);
T y(x);
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
test::check_equivalent_keys(y);
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
}
{
test::check_instances check_;
test::random_values<T> v(1000, generator);
T x(v.begin(), v.end(), 0, hf, eq, al);
T y(x);
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
test::check_equivalent_keys(y);
BOOST_TEST(test::equivalent(y.get_allocator(), al));
}
{
test::check_instances check_;
test::random_values<T> v;
T x(v.begin(), v.end(), 0, hf, eq, al);
T y(x, al);
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
test::check_equivalent_keys(y);
BOOST_TEST(test::selected_count(y.get_allocator()) == 0);
BOOST_TEST(test::equivalent(y.get_allocator(), al));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
}
{
test::check_instances check_;
test::random_values<T> v(500, generator);
T x(v.begin(), v.end(), 0, hf, eq, al);
T y(x, al);
test::unordered_equivalence_tester<T> equivalent(x);
BOOST_TEST(equivalent(y));
test::check_equivalent_keys(y);
BOOST_TEST(test::selected_count(y.get_allocator()) == 0);
BOOST_TEST(test::equivalent(y.get_allocator(), al));
}
}
using test::default_generator;
using test::generate_collisions;
using test::limited_range;
#ifdef BOOST_UNORDERED_FOA_TESTS
template <class T> struct allocator
{
using value_type = T;
allocator() = default;
allocator(allocator const&) = default;
allocator(allocator&&) = default;
template <class U> allocator(allocator<U> const&) {}
T* allocate(std::size_t n)
{
return static_cast<T*>(::operator new(sizeof(value_type) * n));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
friend inline bool operator==(allocator const&, allocator const&)
{
return true;
}
friend inline bool operator!=(allocator const&, allocator const&)
{
return false;
}
};
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_set;
boost::unordered_flat_map<test::object, test::object, test::hash,
@ -249,13 +465,50 @@ namespace copy_tests {
test::equal_to, test::cxx11_allocator<test::object, test::no_select_copy> >*
test_map_no_select_copy;
boost::unordered_flat_set<int, test::hash, test::equal_to,
test::allocator1<int> >* test_set_trivially_copyable;
boost::unordered_flat_map<int, int, test::hash, test::equal_to,
test::allocator1<std::pair<int const, int> > >* test_map_trivially_copyable;
boost::unordered_flat_set<int, test::hash, test::equal_to,
std::allocator<int> >* test_set_trivially_copyable_std_allocator;
boost::unordered_flat_map<int, int, test::hash, test::equal_to,
std::allocator<std::pair<int const, int> > >*
test_map_trivially_copyable_std_allocator;
boost::unordered_flat_set<int, test::hash, test::equal_to, allocator<int> >*
test_set_trivially_copyable_no_construct;
boost::unordered_flat_map<int, int, test::hash, test::equal_to,
allocator<std::pair<int const, int> > >*
test_map_trivially_copyable_no_construct;
// clang-format off
UNORDERED_TEST(copy_construct_tests1,
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)(test_set_no_select_copy)(test_map_no_select_copy))(
(default_generator)(generate_collisions)(limited_range)))
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)
(test_set_no_select_copy)(test_map_no_select_copy)
(test_set_trivially_copyable)(test_map_trivially_copyable))
((default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(copy_construct_tests2,
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)(test_set_no_select_copy)(test_map_no_select_copy))(
(default_generator)(generate_collisions)(limited_range)))
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)
(test_set_no_select_copy)(test_map_no_select_copy)
(test_set_trivially_copyable)(test_map_trivially_copyable))
((default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(copy_construct_tests_std_allocator1,
((test_set_trivially_copyable_std_allocator)
(test_map_trivially_copyable_std_allocator)
(test_set_trivially_copyable_no_construct)
(test_map_trivially_copyable_no_construct))
((default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(copy_construct_tests_std_allocator2,
((test_set_trivially_copyable_std_allocator)
(test_map_trivially_copyable_std_allocator)
(test_set_trivially_copyable_no_construct)
(test_map_trivially_copyable_no_construct))
((default_generator)(generate_collisions)(limited_range)))
// clang-format on
#else
boost::unordered_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_set;