Refactor prime_fmod_size impl to no longer use Boost.Preprocessor

This commit is contained in:
Christian Mazakas 2023-09-01 14:02:32 -07:00
parent e32f72d69c
commit 15318f0e46
3 changed files with 108 additions and 170 deletions

View File

@ -1235,7 +1235,7 @@ namespace boost {
typedef node<value_type, void_pointer> node_type;
typedef boost::unordered::detail::grouped_bucket_array<
bucket<node_type, void_pointer>, value_allocator, prime_fmod_size<> >
bucket<node_type, void_pointer>, value_allocator, prime_fmod_size>
bucket_array_type;
typedef

View File

@ -1,5 +1,5 @@
// Copyright (C) 2022 Joaquin M Lopez Munoz.
// Copyright (C) 2022 Christian Mazakas
// Copyright (C) 2022-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)
@ -7,13 +7,10 @@
#ifndef BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
#define BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
#include <boost/cstdint.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/unordered/detail/narrow_cast.hpp>
#include <boost/config.hpp>
#include <boost/cstdint.hpp>
#include <climits>
#include <cstddef>
@ -35,28 +32,101 @@
namespace boost {
namespace unordered {
namespace detail {
template <class = void> struct prime_fmod_size
{
// Because we compile for C++03, we don't have access to any inline
// initialization for array data members so the definitions must exist
// out-of-line. To keep the library header-only, we introduce a dummy
// template parameter which permits the definition to be included in
// multiple TUs without conflict.
//
static std::size_t sizes[];
static std::size_t const sizes_len;
static std::size_t (*positions[])(std::size_t);
constexpr static std::size_t const prime_fmod_sizes[] = {13ul, 29ul, 53ul,
97ul, 193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul,
6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul,
402653189ul, 805306457ul, 1610612741ul, 3221225473ul,
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
4294967291ul
#else
6442450939ull, 12884901893ull, 25769803751ull, 51539607551ull,
103079215111ull, 206158430209ull, 412316860441ull, 824633720831ull,
1649267441651ull
#endif
};
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
static boost::uint64_t inv_sizes32[];
static std::size_t const inv_sizes32_len;
constexpr static boost::uint64_t const prime_fmod_inv_sizes32[] = {
1418980313362273202ull, 636094623231363849ull, 348051774975651918ull,
190172619316593316ull, 95578984837873325ull, 47420935922132524ull,
23987963684927896ull, 11955116055547344ull, 5991147799191151ull,
2998982941588287ull, 1501077717772769ull, 750081082979285ull,
375261795343686ull, 187625172388393ull, 93822606204624ull,
46909513691883ull, 23456218233098ull, 11728086747027ull,
5864041509391ull, 2932024948977ull, 1466014921160ull, 733007198436ull,
366503839517ull, 183251896093ull, 91625960335ull, 45812983922ull,
22906489714ull, 11453246088ull, 5726623060ull};
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
template <std::size_t SizeIndex,
std::size_t Size = prime_fmod_sizes[SizeIndex]>
static std::size_t prime_fmod_position(std::size_t hash)
{
return hash % Size;
}
constexpr static std::size_t (*prime_fmod_positions[])(std::size_t) = {
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
prime_fmod_position<0>,
prime_fmod_position<1>,
prime_fmod_position<2>,
prime_fmod_position<3>,
prime_fmod_position<4>,
prime_fmod_position<5>,
prime_fmod_position<6>,
prime_fmod_position<7>,
prime_fmod_position<8>,
prime_fmod_position<9>,
prime_fmod_position<10>,
prime_fmod_position<11>,
prime_fmod_position<12>,
prime_fmod_position<13>,
prime_fmod_position<14>,
prime_fmod_position<15>,
prime_fmod_position<16>,
prime_fmod_position<17>,
prime_fmod_position<18>,
prime_fmod_position<19>,
prime_fmod_position<20>,
prime_fmod_position<21>,
prime_fmod_position<22>,
prime_fmod_position<23>,
prime_fmod_position<24>,
prime_fmod_position<25>,
prime_fmod_position<26>,
prime_fmod_position<27>,
prime_fmod_position<28>,
prime_fmod_position<29>,
#else
prime_fmod_position<29>,
prime_fmod_position<30>,
prime_fmod_position<31>,
prime_fmod_position<32>,
prime_fmod_position<33>,
prime_fmod_position<34>,
prime_fmod_position<35>,
prime_fmod_position<36>,
prime_fmod_position<37>,
#endif
};
struct prime_fmod_size
{
constexpr static std::size_t const sizes_len =
sizeof(prime_fmod_sizes) / sizeof(prime_fmod_sizes[0]);
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
constexpr static std::size_t const inv_sizes32_len =
sizeof(prime_fmod_inv_sizes32) / sizeof(prime_fmod_inv_sizes32[0]);
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
static inline std::size_t size_index(std::size_t n)
{
std::size_t i = 0;
for (; i < (sizes_len - 1); ++i) {
if (sizes[i] >= n) {
if (prime_fmod_sizes[i] >= n) {
break;
}
}
@ -65,12 +135,7 @@ namespace boost {
static inline std::size_t size(std::size_t size_index)
{
return sizes[size_index];
}
template <std::size_t Size> static std::size_t modulo(std::size_t hash)
{
return hash % Size;
return prime_fmod_sizes[size_index];
}
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
@ -118,146 +183,20 @@ namespace boost {
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
std::size_t sizes_under_32bit = inv_sizes32_len;
if (BOOST_LIKELY(size_index < sizes_under_32bit)) {
return fast_modulo(
narrow_cast<boost::uint32_t>(hash) + narrow_cast<boost::uint32_t>(hash >> 32),
inv_sizes32[size_index], boost::uint32_t(sizes[size_index]));
return fast_modulo(narrow_cast<boost::uint32_t>(hash) +
narrow_cast<boost::uint32_t>(hash >> 32),
prime_fmod_inv_sizes32[size_index],
boost::uint32_t(prime_fmod_sizes[size_index]));
} else {
return positions[size_index - sizes_under_32bit](hash);
return prime_fmod_positions[size_index - sizes_under_32bit](hash);
}
#else
return positions[size_index](hash);
return prime_fmod_positions[size_index](hash);
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
}
}; // prime_fmod_size
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE \
(13ul)(29ul)(53ul)(97ul)(193ul)(389ul)(769ul)(1543ul)(3079ul)(6151ul)( \
12289ul)(24593ul)(49157ul)(98317ul)(196613ul)(393241ul)(786433ul)( \
1572869ul)(3145739ul)(6291469ul)(12582917ul)(25165843ul)(50331653ul)( \
100663319ul)(201326611ul)(402653189ul)(805306457ul)(1610612741ul)( \
3221225473ul)
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE(4294967291ul)
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
#else
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
// The original sequence here is this:
// (6442450939ul)
// (12884901893ul)
// (25769803751ul)
// (51539607551ul)
// (103079215111ul)
// (206158430209ul)
// (412316860441ul)
// (824633720831ul)
// (1649267441651ul)
//
// but this causes problems on versions of mingw where the `long` type is 32
// bits, even for 64-bit targets. We work around this by replacing the literals
// with compile-time arithmetic, using bitshifts to reconstruct the number.
//
// clang-format off
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT \
((boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(2147483643ul)) \
((boost::ulong_long_type(3ul) << 32) + boost::ulong_long_type(5ul)) \
((boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(4294967271ul)) \
((boost::ulong_long_type(11ul) << 32) + boost::ulong_long_type(4294967295ul)) \
((boost::ulong_long_type(24ul) << 32) + boost::ulong_long_type(7ul)) \
((boost::ulong_long_type(48ul) << 32) + boost::ulong_long_type(1ul)) \
((boost::ulong_long_type(96ul) << 32) + boost::ulong_long_type(25ul)) \
((boost::ulong_long_type(191ul) << 32) + boost::ulong_long_type(4294967295ul)) \
((boost::ulong_long_type(383ul) << 32) + boost::ulong_long_type(4294967283ul))
// clang-format on
#endif /* BOOST_UNORDERED_FCA_HAS_64B_SIZE_T */
#define BOOST_UNORDERED_PRIME_FMOD_SIZES \
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
template <class T>
std::size_t prime_fmod_size<T>::sizes[] = {
BOOST_PP_SEQ_ENUM(BOOST_UNORDERED_PRIME_FMOD_SIZES)};
template <class T>
std::size_t const prime_fmod_size<T>::sizes_len = BOOST_PP_SEQ_SIZE(
BOOST_UNORDERED_PRIME_FMOD_SIZES);
// Similarly here, we have to re-express the integer initialization using
// arithmetic such that each literal can fit in a 32-bit value.
//
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
// clang-format off
template <class T>
boost::uint64_t prime_fmod_size<T>::inv_sizes32[] = {
(boost::ulong_long_type(330382099ul) << 32) + boost::ulong_long_type(2973438898ul) /* = 1418980313362273202 */,
(boost::ulong_long_type(148102320ul) << 32) + boost::ulong_long_type(2369637129ul) /* = 636094623231363849 */,
(boost::ulong_long_type(81037118ul) << 32) + boost::ulong_long_type(3403558990ul) /* = 348051774975651918 */,
(boost::ulong_long_type(44278013ul) << 32) + boost::ulong_long_type(1549730468ul) /* = 190172619316593316 */,
(boost::ulong_long_type(22253716ul) << 32) + boost::ulong_long_type(2403401389ul) /* = 95578984837873325 */,
(boost::ulong_long_type(11041047ul) << 32) + boost::ulong_long_type(143533612ul) /* = 47420935922132524 */,
(boost::ulong_long_type(5585133ul) << 32) + boost::ulong_long_type(106117528ul) /* = 23987963684927896 */,
(boost::ulong_long_type(2783517ul) << 32) + boost::ulong_long_type(1572687312ul) /* = 11955116055547344 */,
(boost::ulong_long_type(1394922ul) << 32) + boost::ulong_long_type(3428720239ul) /* = 5991147799191151 */,
(boost::ulong_long_type(698255ul) << 32) + boost::ulong_long_type(552319807ul) /* = 2998982941588287 */,
(boost::ulong_long_type(349496ul) << 32) + boost::ulong_long_type(3827689953ul) /* = 1501077717772769 */,
(boost::ulong_long_type(174641ul) << 32) + boost::ulong_long_type(3699438549ul) /* = 750081082979285 */,
(boost::ulong_long_type(87372ul) << 32) + boost::ulong_long_type(1912757574ul) /* = 375261795343686 */,
(boost::ulong_long_type(43684ul) << 32) + boost::ulong_long_type(3821029929ul) /* = 187625172388393 */,
(boost::ulong_long_type(21844ul) << 32) + boost::ulong_long_type(3340590800ul) /* = 93822606204624 */,
(boost::ulong_long_type(10921ul) << 32) + boost::ulong_long_type(4175852267ul) /* = 46909513691883 */,
(boost::ulong_long_type(5461ul) << 32) + boost::ulong_long_type(1401829642ul) /* = 23456218233098 */,
(boost::ulong_long_type(2730ul) << 32) + boost::ulong_long_type(2826028947ul) /* = 11728086747027 */,
(boost::ulong_long_type(1365ul) << 32) + boost::ulong_long_type(1411150351ul) /* = 5864041509391 */,
(boost::ulong_long_type(682ul) << 32) + boost::ulong_long_type(2857253105ul) /* = 2932024948977 */,
(boost::ulong_long_type(341ul) << 32) + boost::ulong_long_type(1431073224ul) /* = 1466014921160 */,
(boost::ulong_long_type(170ul) << 32) + boost::ulong_long_type(2862758116ul) /* = 733007198436 */,
(boost::ulong_long_type(85ul) << 32) + boost::ulong_long_type(1431619357ul) /* = 366503839517 */,
(boost::ulong_long_type(42ul) << 32) + boost::ulong_long_type(2863269661ul) /* = 183251896093 */,
(boost::ulong_long_type(21ul) << 32) + boost::ulong_long_type(1431647119ul) /* = 91625960335 */,
(boost::ulong_long_type(10ul) << 32) + boost::ulong_long_type(2863310962ul) /* = 45812983922 */,
(boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(1431653234ul) /* = 22906489714 */,
(boost::ulong_long_type(2ul) << 32) + boost::ulong_long_type(2863311496ul) /* = 11453246088 */,
(boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(1431655764ul) /* = 5726623060 */,
};
// clang-format on
template <class T>
std::size_t const
prime_fmod_size<T>::inv_sizes32_len = sizeof(inv_sizes32) /
sizeof(inv_sizes32[0]);
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
#define BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT(z, _, n) \
prime_fmod_size<T>::template modulo<n>,
template <class T>
std::size_t (*prime_fmod_size<T>::positions[])(std::size_t) = {
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT)
#else
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT)
#endif
};
#undef BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
} // namespace detail
} // namespace unordered
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP

View File

@ -64,9 +64,8 @@ void prime_sizes_test()
BOOST_TEST(!is_prime(100));
BOOST_TEST(!is_prime(49));
std::size_t* sizes = boost::unordered::detail::prime_fmod_size<>::sizes;
std::size_t sizes_len =
boost::unordered::detail::prime_fmod_size<>::sizes_len;
std::size_t const* sizes = boost::unordered::detail::prime_fmod_sizes;
std::size_t sizes_len = boost::unordered::detail::prime_fmod_size::sizes_len;
// prove every number in our sizes array is prime
//
@ -88,11 +87,11 @@ void prime_sizes_test()
// correct amount of them, i.e. one for every entry in sizes[] that fits in 32
// bits
//
boost::uint64_t* inv_sizes32 =
boost::unordered::detail::prime_fmod_size<>::inv_sizes32;
boost::uint64_t const* inv_sizes32 =
boost::unordered::detail::prime_fmod_inv_sizes32;
std::size_t inv_sizes32_len =
boost::unordered::detail::prime_fmod_size<>::inv_sizes32_len;
boost::unordered::detail::prime_fmod_size::inv_sizes32_len;
std::size_t count = 0;
for (std::size_t i = 0; i < sizes_len; ++i) {
@ -133,7 +132,7 @@ void get_remainder_test()
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
struct
{
// boost::unordered::detail::prime_fmod_size<>::get_remainder
// boost::unordered::detail::prime_fmod_size::get_remainder
// uses several internal implementations depending on the availability of
// certain intrinsics or 128 bit integer support, defaulting to a slow,
// portable routine. The following is a transcription of the portable
@ -157,7 +156,7 @@ void get_remainder_test()
boost::uint32_t d = rng() & 0xffffffffu;
boost::uint64_t r1 =
boost::unordered::detail::prime_fmod_size<>::get_remainder(f, d);
boost::unordered::detail::prime_fmod_size::get_remainder(f, d);
boost::uint64_t r2 = get_remainder(f, d);
@ -171,10 +170,10 @@ void get_remainder_test()
void modulo_test()
{
std::size_t* sizes = boost::unordered::detail::prime_fmod_size<>::sizes;
std::size_t const* sizes = boost::unordered::detail::prime_fmod_sizes;
std::size_t const sizes_len =
boost::unordered::detail::prime_fmod_size<>::sizes_len;
boost::unordered::detail::prime_fmod_size::sizes_len;
boost::detail::splitmix64 rng;
@ -190,7 +189,7 @@ void modulo_test()
}
#endif
std::size_t p1 =
boost::unordered::detail::prime_fmod_size<>::position(hash, j);
boost::unordered::detail::prime_fmod_size::position(hash, j);
std::size_t p2 = h % sizes[j];