diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 2a6c44bd..4310e0e7 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -752,6 +754,19 @@ struct table_arrays value_type *elements; }; +struct no_mix +{ + static inline std::size_t mix(std::size_t x)noexcept{return x;} +}; + +struct xmx_mix +{ + static inline std::size_t mix(std::size_t x)noexcept + { + return xmx(x); + } +}; + struct if_constexpr_void_else{void operator()()const{}}; template @@ -827,6 +842,11 @@ table:empty_value,empty_value,empty_value static constexpr auto N=group_type::N; using size_policy=pow2_size_policy; using prober=pow2_quadratic_prober; + using mix_policy=typename std::conditional< + hash_traits::is_avalanching::value, + no_mix, + xmx_mix + >::type; using alloc_traits=boost::allocator_traits; public: @@ -1158,7 +1178,7 @@ public: template BOOST_FORCEINLINE iterator find(const Key& x) { - auto hash=h()(x); + auto hash=hash_for(x); return find_impl(x,position_for(hash),hash); } @@ -1285,6 +1305,12 @@ private: return std::get<0>(k); } + template + inline std::size_t hash_for(const Key& x) + { + return mix_policy::mix(h()(x)); + } + inline std::size_t position_for(std::size_t hash)const { return position_for(hash,arrays); @@ -1341,7 +1367,7 @@ private: BOOST_FORCEINLINE std::pair emplace_impl(Args&&... args) { const auto &k=key_from(std::forward(args)...); - auto hash=h()(k); + auto hash=hash_for(k); auto pos0=position_for(hash); auto it=find_impl(k,pos0,hash); @@ -1398,13 +1424,13 @@ private: template void unchecked_insert(Value&& x) { - auto hash=h()(key_from(x)); + auto hash=hash_for(key_from(x)); unchecked_emplace_at(position_for(hash),hash,std::forward(x)); } void nosize_transfer_element(value_type* p,const arrays_type& arrays_) { - auto hash=h()(key_from(*p)); + auto hash=hash_for(key_from(*p)); nosize_unchecked_emplace_at( arrays_,position_for(hash,arrays_),hash,type_policy::move(*p)); destroy_element(p); diff --git a/include/boost/unordered/detail/foa_mixer.hpp b/include/boost/unordered/detail/foa_mixer.hpp deleted file mode 100644 index 7d2f2ebe..00000000 --- a/include/boost/unordered/detail/foa_mixer.hpp +++ /dev/null @@ -1,175 +0,0 @@ -/* Hash mixer for boost::unordered::unordered_flat_[map|set]. - * - * Copyright 2022 Joaquin M Lopez Munoz. - * Copyright 2022 Peter Dimov. - * 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) - * - * See https://www.boost.org/libs/unordered for library home page. - */ - -#ifndef BOOST_UNORDERED_DETAIL_FOA_MIXER_HPP -#define BOOST_UNORDERED_DETAIL_FOA_MIXER_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost{ -namespace unordered{ -namespace detail{ -namespace foa{ - -/* mixer is functionally equivalent to Hash except if Hash is - * boost::hash for any of the Ts where boost::hash implements a - * trivial hashing function not fit for open-addressing hash container: - * in these cases, the result of boost::hash is post-mixed using - * - * - 64 bits: xmx (TODO: Peter Dimov to explain) - * - 32 bits: xmx33 (TODO: Peter Dimov to explain) - */ - -#if defined(BOOST_GCC) -/* GCC's -Wshadow triggers at scenarios like this: - * - * struct foo{}; - * template - * struct derived:Base - * { - * void f(){int foo;} - * }; - * - * derivedx; - * x.f(); // declaration of "foo" in derived::f shadows base type "foo" - * - * This makes shadowing warnings unavoidable in general when a class template - * derives from a user-provided class, as is the case with mixer_impl. - */ - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#endif - -template -class mixer_impl:empty_value -{ -public: - using base=empty_value; - -#if BOOST_CXX_VERSION<201703L - using argument_type=typename Hash::argument_type; - using result_type=std::size_t; -#endif - - mixer_impl()=default; - template - mixer_impl(Args&&... args):base{empty_init,std::forward(args)...}{} - - Hash& get_base()noexcept{return base::get();} - const Hash& get_base()const noexcept{return base::get();} - - template< - typename Key, - typename std::enable_if< - std::is_same< - std::size_t, - decltype(std::declval()(std::declval())) - >::value - >::type* =nullptr - > - std::size_t operator()(const Key& x)const - noexcept(noexcept(std::declval()(x))) - { - return MixPolicy::mix(get_base()(x)); - } - - friend void swap(mixer_impl& x,mixer_impl& y) - { - using std::swap; - swap(x.get_base(),y.get_base()); - } -}; - -#if defined(BOOST_GCC) -#pragma GCC diagnostic pop /* ignored "-Wshadow" */ -#endif - -struct no_mix -{ - static inline std::size_t mix(std::size_t x)noexcept{return x;} -}; - -#if defined(SIZE_MAX) -#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0 -#define BOOST_UNORDERED_64B_ARCHITECTURE /* >64 bits assumed as 64 bits */ -#endif -#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */ -#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0 -#define BOOST_UNORDERED_64B_ARCHITECTURE -#endif -#endif - -struct xmx_mix -{ - static inline std::size_t mix(std::size_t x)noexcept - { -#if defined(BOOST_UNORDERED_64B_ARCHITECTURE) - - boost::uint64_t z=x; - - z^=z>> 23; - z*=0xff51afd7ed558ccdull; - z^=z>>23; - - return (std::size_t)z; - -#else /* 32 bits assumed */ - - x^=x>>18; - x*=0x56b5aaadu; - x^=x>>16; - - return x; - -#endif - } -}; - -#ifdef BOOST_UNORDERED_64B_ARCHITECTURE -#undef BOOST_UNORDERED_64B_ARCHITECTURE -#endif - -template struct is_boost_hash:std::false_type{}; -template struct is_boost_hash>:std::true_type{}; - -template struct boost_hash_key_impl{using type=void;}; -template struct boost_hash_key_impl> -{ - using type=Key; -}; -template using boost_hash_key= - typename boost_hash_key_impl::type; - -template -using mixer=typename std::conditional< - is_boost_hash::value&&( - std::is_integral>::value|| - std::is_enum>::value|| - std::is_floating_point>::value|| // TODO: not sure about this one - std::is_pointer>::value), - mixer_impl, - mixer_impl ->::type; - -} /* namespace foa */ -} /* namespace detail */ -} /* namespace unordered */ -} /* namespace boost */ - -#endif diff --git a/include/boost/unordered/detail/xmx.hpp b/include/boost/unordered/detail/xmx.hpp new file mode 100644 index 00000000..4b24fd80 --- /dev/null +++ b/include/boost/unordered/detail/xmx.hpp @@ -0,0 +1,75 @@ +/* 32b/64b xmx mix function. + * + * Copyright 2022 Peter Dimov. + * Copyright 2022 Joaquin M Lopez Munoz. + * 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) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_XMX_HPP +#define BOOST_UNORDERED_DETAIL_XMX_HPP + +#include +#include +#include + +namespace boost{ +namespace unordered{ +namespace detail{ + +/* Bit mixer for improvement of statistical properties of hash functions. + * The implementation is different on 64bit and 32bit architectures: + * + * - 64bit: same as xmx function in + * http://jonkagstrom.com/bit-mixer-construction/index.html + * - 32bit: generated by Hash Function Prospector + * (https://github.com/skeeto/hash-prospector) and selected as the + * best overall performer in benchmarks of Boost.Unordered flat containers. + * Score assigned by Hash Prospector: 333.7934929677524 + */ + +#if defined(SIZE_MAX) +#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0 +#define BOOST_UNORDERED_64B_ARCHITECTURE /* >64 bits assumed as 64 bits */ +#endif +#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */ +#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0 +#define BOOST_UNORDERED_64B_ARCHITECTURE +#endif +#endif + +static inline std::size_t xmx(std::size_t x)noexcept +{ +#if defined(BOOST_UNORDERED_64B_ARCHITECTURE) + + boost::uint64_t z=(boost::uint64_t)x; + + z^=z>>23; + z*=0xff51afd7ed558ccdull; + z^=z>>23; + + return (std::size_t)z; + +#else /* 32 bits assumed */ + + x^=x>>18; + x*=0x56b5aaadu; + x^=x>>16; + + return x; + +#endif +} + +#ifdef BOOST_UNORDERED_64B_ARCHITECTURE +#undef BOOST_UNORDERED_64B_ARCHITECTURE +#endif + +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +#endif diff --git a/include/boost/unordered/hash_traits.hpp b/include/boost/unordered/hash_traits.hpp new file mode 100644 index 00000000..9409c02b --- /dev/null +++ b/include/boost/unordered/hash_traits.hpp @@ -0,0 +1,52 @@ +/* Hash function characterization. + * + * Copyright 2022 Joaquin M Lopez Munoz. + * 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) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_HASH_TRAITS_HPP +#define BOOST_UNORDERED_HASH_TRAITS_HPP + +#include +#include + +namespace boost{ +namespace unordered{ + +namespace detail{ + +template +struct hash_is_avalanching +{ + using type=std::false_type; +}; + +template +struct hash_is_avalanching> +{ + using type=std::true_type; +}; + +} /* namespace detail */ + +/* Partially specializable by users for concrete hash functions when + * actual characterization differs from default. + */ + +template +struct hash_traits +{ + /* std::true_type if the type Hash::is_avalanching is present, + * std::false_type otherwise. + */ + using is_avalanching=typename detail::hash_is_avalanching::type; +}; + +} /* namespace unordered */ +} /* namespace boost */ + +#endif