diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h index a9ad7dd..bf8b644 100644 --- a/libstdc++-v3/include/bits/hashtable_policy.h +++ b/libstdc++-v3/include/bits/hashtable_policy.h @@ -457,6 +457,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// smallest prime that keeps the load factor small enough. struct _Prime_rehash_policy { + using __has_load_factor = std::true_type; + _Prime_rehash_policy(float __z = 1.0) noexcept : _M_max_load_factor(__z), _M_next_resize(0) { } @@ -501,6 +503,136 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION mutable std::size_t _M_next_resize; }; + /// Range hashing function assuming that second args is a power of 2. + struct _Mask_range_hashing + { + typedef std::size_t first_argument_type; + typedef std::size_t second_argument_type; + typedef std::size_t result_type; + + result_type + operator()(first_argument_type __num, + second_argument_type __den) const noexcept + { return __num & (__den - 1); } + }; + + + /// Helper type to compute next power of 2. + template + struct _NextPower2 + { + static _SizeT + _Get(_SizeT __n) + { + _SizeT __next = _NextPower2<_SizeT, (_N >> 1), false>::_Get(__n); + __next |= __next >> _N; + if (_Increment) + ++__next; + + return __next; + } + }; + + template + struct _NextPower2<_SizeT, 1, false> + { + static _SizeT + _Get(_SizeT __n) + { + --__n; + return __n |= __n >> 1; + } + }; + + /// Rehash policy providing power of 2 bucket numbers. Ease modulo + /// operations. + struct _Power2_rehash_policy + { + using __has_load_factor = std::true_type; + + _Power2_rehash_policy(float __z = 1.0) noexcept + : _M_max_load_factor(__z), _M_next_resize(0) { } + + float + max_load_factor() const noexcept + { return _M_max_load_factor; } + + // Return a bucket size no smaller than n (as long as n is not above the + // highest power of 2). + std::size_t + _M_next_bkt(std::size_t __n) const + { + constexpr auto __max_bkt + = std::size_t(1) << ((sizeof(std::size_t) << 3) - 1); + + std::size_t __res = _NextPower2::_Get(__n); + + if (__res == 0) + __res = __max_bkt; + + if (__res == __max_bkt) + // Set next resize to the max value so that we never try to rehash again + // as we already reach the biggest possible bucket number. + // Note that it might result in max_load_factor not being respected. + _M_next_resize = std::size_t(0) - 1; + else + _M_next_resize + = __builtin_floor(__res * (long double)_M_max_load_factor); + + return __res; + } + + // Return a bucket count appropriate for n elements + std::size_t + _M_bkt_for_elements(std::size_t __n) const + { return __builtin_ceil(__n / (long double)_M_max_load_factor); } + + // __n_bkt is current bucket count, __n_elt is current element count, + // and __n_ins is number of elements to be inserted. Do we need to + // increase bucket count? If so, return make_pair(true, n), where n + // is the new bucket count. If not, return make_pair(false, 0). + std::pair + _M_need_rehash(std::size_t __n_bkt, std::size_t __n_elt, + std::size_t __n_ins) const + { + if (__n_elt + __n_ins >= _M_next_resize) + { + long double __min_bkts = (__n_elt + __n_ins) + / (long double)_M_max_load_factor; + if (__min_bkts >= __n_bkt) + return std::make_pair(true, + _M_next_bkt(std::max(__builtin_floor(__min_bkts) + 1, + __n_bkt * _S_growth_factor))); + + _M_next_resize + = __builtin_floor(__n_bkt * (long double)_M_max_load_factor); + return std::make_pair(false, 0); + } + else + return std::make_pair(false, 0); + } + + typedef std::size_t _State; + + _State + _M_state() const + { return _M_next_resize; } + + void + _M_reset() noexcept + { _M_next_resize = 0; } + + void + _M_reset(_State __state) + { _M_next_resize = __state; } + + static const std::size_t _S_growth_factor = 2; + + float _M_max_load_factor; + mutable std::size_t _M_next_resize; + }; + // Base classes for std::_Hashtable. We define these base classes // because in some cases we want to do different things depending on // the value of a policy class. In some cases the policy class @@ -775,8 +907,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename _ExtractKey, typename _Equal, typename _H1, typename _H2, typename _Hash, typename _RehashPolicy, typename _Traits, - bool _Constant_iterators = _Traits::__constant_iterators::value, - bool _Unique_keys = _Traits::__unique_keys::value> + bool _Constant_iterators = _Traits::__constant_iterators::value> struct _Insert; /// Specialization. @@ -785,65 +916,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename _H1, typename _H2, typename _Hash, typename _RehashPolicy, typename _Traits> struct _Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, - _RehashPolicy, _Traits, true, true> + _RehashPolicy, _Traits, true> : public _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits> { using __base_type = _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>; - using value_type = typename __base_type::value_type; - using iterator = typename __base_type::iterator; - using const_iterator = typename __base_type::const_iterator; - - using __unique_keys = typename __base_type::__unique_keys; - using __hashtable = typename __base_type::__hashtable; - using __node_gen_type = typename __base_type::__node_gen_type; - - using __base_type::insert; - std::pair - insert(value_type&& __v) - { - __hashtable& __h = this->_M_conjure_hashtable(); - __node_gen_type __node_gen(__h); - return __h._M_insert(std::move(__v), __node_gen, __unique_keys()); - } - - iterator - insert(const_iterator __hint, value_type&& __v) - { - __hashtable& __h = this->_M_conjure_hashtable(); - __node_gen_type __node_gen(__h); - return __h._M_insert(__hint, std::move(__v), __node_gen, - __unique_keys()); - } - }; + using __hashtable_base = _Hashtable_base<_Key, _Value, _ExtractKey, + _Equal, _H1, _H2, _Hash, + _Traits>; - /// Specialization. - template - struct _Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, - _RehashPolicy, _Traits, true, false> - : public _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, - _H1, _H2, _Hash, _RehashPolicy, _Traits> - { - using __base_type = _Insert_base<_Key, _Value, _Alloc, _ExtractKey, - _Equal, _H1, _H2, _Hash, - _RehashPolicy, _Traits>; using value_type = typename __base_type::value_type; using iterator = typename __base_type::iterator; using const_iterator = typename __base_type::const_iterator; using __unique_keys = typename __base_type::__unique_keys; + using __ireturn_type = typename __hashtable_base::__ireturn_type; using __hashtable = typename __base_type::__hashtable; using __node_gen_type = typename __base_type::__node_gen_type; using __base_type::insert; - iterator + __ireturn_type insert(value_type&& __v) { __hashtable& __h = this->_M_conjure_hashtable(); @@ -865,9 +961,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template + typename _RehashPolicy, typename _Traits> struct _Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, - _RehashPolicy, _Traits, false, _Unique_keys> + _RehashPolicy, _Traits, false> : public _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits> { @@ -911,28 +1007,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + template + using __has_load_factor = typename _Policy::__has_load_factor; + /** * Primary class template _Rehash_base. * * Give hashtable the max_load_factor functions and reserve iff the - * rehash policy is _Prime_rehash_policy. + * rehash policy supports it. */ template + typename _RehashPolicy, typename _Traits, + typename = + __detected_or_t> struct _Rehash_base; - /// Specialization. + /// Specialization when rehash policy doesn't provide load factor management. template + typename _H1, typename _H2, typename _Hash, + typename _RehashPolicy, typename _Traits> struct _Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, - _H1, _H2, _Hash, _Prime_rehash_policy, _Traits> + _H1, _H2, _Hash, _RehashPolicy, _Traits, + std::false_type> + { + }; + + /// Specialization when rehash policy provide load factor management. + template + struct _Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, + _H1, _H2, _Hash, _RehashPolicy, _Traits, + std::true_type> { using __hashtable = _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, - _Prime_rehash_policy, _Traits>; + _RehashPolicy, _Traits>; float max_load_factor() const noexcept @@ -945,7 +1059,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION max_load_factor(float __z) { __hashtable* __this = static_cast<__hashtable*>(this); - __this->__rehash_policy(_Prime_rehash_policy(__z)); + __this->__rehash_policy(_RehashPolicy(__z)); } void diff --git a/libstdc++-v3/src/c++11/hashtable_c++0x.cc b/libstdc++-v3/src/c++11/hashtable_c++0x.cc index 69f999f..dd2afe3 100644 --- a/libstdc++-v3/src/c++11/hashtable_c++0x.cc +++ b/libstdc++-v3/src/c++11/hashtable_c++0x.cc @@ -60,8 +60,16 @@ namespace __detail = sizeof(__prime_list) / sizeof(unsigned long) - 1; const unsigned long* __next_bkt = std::lower_bound(__prime_list + 5, __prime_list + __n_primes, __n); - _M_next_resize = - __builtin_ceil(*__next_bkt * (long double)_M_max_load_factor); + + if (__next_bkt == __prime_list + __n_primes) + // Set next resize to the max value so that we never try to rehash again + // as we already reach the biggest possible bucket number. + // Note that it might result in max_load_factor not being respected. + _M_next_resize = std::size_t(0) - 1; + else + _M_next_resize = + __builtin_ceil(*__next_bkt * (long double)_M_max_load_factor); + return *__next_bkt; } diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/power2_rehash.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/power2_rehash.cc new file mode 100644 index 0000000..502794b --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/power2_rehash.cc @@ -0,0 +1,42 @@ +// Copyright (C) 2015 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . +// +// { dg-options "-std=gnu++11" } + +#include + +#include + +void test01() +{ + bool test __attribute__((unused)) = true; + + std::__detail::_Power2_rehash_policy policy; + VERIFY( policy._M_next_bkt(1) == 1 ); + VERIFY( policy._M_next_bkt(2) == 2 ); + VERIFY( policy._M_next_bkt(3) == 4 ); + VERIFY( policy._M_next_bkt(5) == 8 ); + VERIFY( policy._M_next_bkt(33) == 64 ); + VERIFY( policy._M_next_bkt((std::size_t(1) << (sizeof(std::size_t) * 8 - 2)) + 1) + == (std::size_t(1) << (sizeof(std::size_t) * 8 - 1)) ); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/performance/23_containers/insert/54075.cc b/libstdc++-v3/testsuite/performance/23_containers/insert/54075.cc index ef956a0..a07d552 100644 --- a/libstdc++-v3/testsuite/performance/23_containers/insert/54075.cc +++ b/libstdc++-v3/testsuite/performance/23_containers/insert/54075.cc @@ -127,7 +127,27 @@ template using __umset = std::__umset_hashtable, std::allocator, - std::__uset_traits>; + std::__umset_traits>; + +template + using __uset2 = + std::_Hashtable, + std::__detail::_Identity, + std::equal_to, HashFunction, + std::__detail::_Mask_range_hashing, + std::__detail::_Default_ranged_hash, + std::__detail::_Power2_rehash_policy, + std::__uset_traits>; + +template + using __umset2 = + std::_Hashtable, + std::__detail::_Identity, + std::equal_to, HashFunction, + std::__detail::_Mask_range_hashing, + std::__detail::_Default_ranged_hash, + std::__detail::_Power2_rehash_policy, + std::__umset_traits>; int main() { @@ -181,6 +201,19 @@ int main() stop_counters(time, resource); report_performance(__FILE__, "std benches", time, resource); + start_counters(time, resource); + bench<__uset2>( + "std::unordered_set2 without hash code cached ", foos); + bench<__uset2>( + "std::unordered_set2 with hash code cached ", foos); + bench<__umset2>( + "std::unordered_multiset2 without hash code cached ", foos); + bench<__umset2>( + "std::unordered_multiset2 with hash code cached ", foos); + + stop_counters(time, resource); + report_performance(__FILE__, "std2 benches", time, resource); + bench>( "std::unordered_set default cache ", foos); bench>( diff --git a/libstdc++-v3/testsuite/performance/23_containers/insert_erase/41975.cc b/libstdc++-v3/testsuite/performance/23_containers/insert_erase/41975.cc index 9846104..4b29fde 100644 --- a/libstdc++-v3/testsuite/performance/23_containers/insert_erase/41975.cc +++ b/libstdc++-v3/testsuite/performance/23_containers/insert_erase/41975.cc @@ -177,6 +177,16 @@ template cache>; template + using __uset2 = + std::_Hashtable, + std::__detail::_Identity, + std::equal_to, std::hash, + std::__detail::_Mask_range_hashing, + std::__detail::_Default_ranged_hash, + std::__detail::_Power2_rehash_policy, + std::__uset_traits>; + +template using __str_uset = std::__uset_hashtable, std::equal_to, @@ -190,6 +200,16 @@ template std::allocator, cache>; +template + using __str_uset2 = + std::_Hashtable, + std::__detail::_Identity, + std::equal_to, std::hash, + std::__detail::_Mask_range_hashing, + std::__detail::_Default_ranged_hash, + std::__detail::_Power2_rehash_policy, + std::__uset_traits>; + int main() { bench<__tr1_uset>( @@ -202,6 +222,10 @@ int main() "std::unordered_set with hash code cached"); bench>( "std::unordered_set default cache"); + bench<__uset2>( + "std::unordered_set2 without hash code cached"); + bench<__uset2>( + "std::unordered_set2 with hash code cached"); bench_str<__tr1_str_uset>( "std::tr1::unordered_set without hash code cached"); bench_str<__tr1_str_uset>( @@ -210,7 +234,11 @@ int main() "std::unordered_set without hash code cached"); bench_str<__str_uset>( "std::unordered_set with hash code cached"); - bench_str>( + bench_str>( "std::unordered_set default cache"); + bench_str<__str_uset2>( + "std::unordered_set2 without hash code cached"); + bench_str<__str_uset2>( + "std::unordered_set2 with hash code cached"); return 0; }