+2019-05-04 François Dumont <fdumont@gcc.gnu.org>
+
+ * include/bits/hashtable.h (_Hashtable<>::rehash): Review comment.
+ * include/bits/hashtable_policy.h
+ (_Prime_rehash_policy::_M_bkt_for_elements): Use __builtin_ceill.
+ (_Power2_rehash_policy::_M_bkt_for_elements): Likewise.
+ (_Power2_rehash_policy::_M_next_bkt): Enforce returning a result not
+ smaller than input value rather than always greater. Preserve
+ _M_next_resize if called with 0 input. Use __builtin_floorl.
+ (_Power2_rehash_policy::_M_need_rehash): Rehash only if number of
+ elements + number of insertions is greater than _M_next_resize. Start
+ with 11 buckets if not told otherwise. Use __builtin_floorl.
+ (_Rehash_base<>::reserve): Use rehash policy _M_bkt_for_elements.
+ * src/c++11/hashtable_c++0x.cc (_Prime_rehash_policy::_M_next_bkt):
+ Preserve _M_next_resize if called with 0 input. Use __builtin_floorl.
+ (_Prime_rehash_policy::_M_need_rehash): Start with 11 buckets if not
+ told otherwise. Use __builtin_floorl.
+ * testsuite/23_containers/unordered_set/hash_policy/71181.cc: Adapt test
+ to also validate _Power2_rehash_policy.
+ * testsuite/23_containers/unordered_set/hash_policy/power2_rehash.cc:
+ Adapt.
+
2019-05-03 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/61761
if (__buckets != _M_bucket_count)
_M_rehash(__buckets, __saved_state);
else
- // No rehash, restore previous state to keep a consistent state.
+ // No rehash, restore previous state to keep it consistent with
+ // container state.
_M_rehash_policy._M_reset(__saved_state);
}
// 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); }
+ { return __builtin_ceill(__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
std::size_t
_M_next_bkt(std::size_t __n) noexcept
{
+ if (__n == 0)
+ // Special case on container 1st initialization with 0 bucket count
+ // hint. We keep _M_next_resize to 0 to make sure that next time we
+ // want to add an element allocation will take place.
+ return 1;
+
const auto __max_width = std::min<size_t>(sizeof(size_t), 8);
const auto __max_bkt = size_t(1) << (__max_width * __CHAR_BIT__ - 1);
std::size_t __res = __clp2(__n);
- if (__res == __n)
- __res <<= 1;
-
if (__res == 0)
__res = __max_bkt;
+ else if (__res == 1)
+ // If __res is 1 we force it to 2 to make sure there will be an
+ // allocation so that nothing need to be stored in the initial
+ // single bucket
+ __res = 2;
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(-1);
+ _M_next_resize = numeric_limits<size_t>::max();
else
_M_next_resize
- = __builtin_ceil(__res * (long double)_M_max_load_factor);
+ = __builtin_floorl(__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 noexcept
- { return __builtin_ceil(__n / (long double)_M_max_load_factor); }
+ { return __builtin_ceill(__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
_M_need_rehash(std::size_t __n_bkt, std::size_t __n_elt,
std::size_t __n_ins) noexcept
{
- if (__n_elt + __n_ins >= _M_next_resize)
+ if (__n_elt + __n_ins > _M_next_resize)
{
- long double __min_bkts = (__n_elt + __n_ins)
- / (long double)_M_max_load_factor;
+ // If _M_next_resize is 0 it means that we have nothing allocated so
+ // far and that we start inserting elements. In this case we start
+ // with an initial bucket size of 11.
+ long double __min_bkts
+ = std::max<std::size_t>(__n_elt + __n_ins, _M_next_resize ? 0 : 11)
+ / (long double)_M_max_load_factor;
if (__min_bkts >= __n_bkt)
- return std::make_pair(true,
- _M_next_bkt(std::max<std::size_t>(__builtin_floor(__min_bkts) + 1,
- __n_bkt * _S_growth_factor)));
+ return { true,
+ _M_next_bkt(std::max<std::size_t>(__builtin_floorl(__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);
+ = __builtin_floorl(__n_bkt * (long double)_M_max_load_factor);
+ return { false, 0 };
}
else
- return std::make_pair(false, 0);
+ return { false, 0 };
}
typedef std::size_t _State;
reserve(std::size_t __n)
{
__hashtable* __this = static_cast<__hashtable*>(this);
- __this->rehash(__builtin_ceil(__n / max_load_factor()));
+ __this->rehash(__this->__rehash_policy()._M_bkt_for_elements(__n));
}
};
if (__n < sizeof(__fast_bkt))
{
+ if (__n == 0)
+ // Special case on container 1st initialization with 0 bucket count
+ // hint. We keep _M_next_resize to 0 to make sure that next time we
+ // want to add an element allocation will take place.
+ return 1;
+
_M_next_resize =
- __builtin_floor(__fast_bkt[__n] * (long double)_M_max_load_factor);
+ __builtin_floorl(__fast_bkt[__n] * (long double)_M_max_load_factor);
return __fast_bkt[__n];
}
// 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(-1);
+ _M_next_resize = numeric_limits<size_t>::max();
else
_M_next_resize =
- __builtin_floor(*__next_bkt * (long double)_M_max_load_factor);
+ __builtin_floorl(*__next_bkt * (long double)_M_max_load_factor);
return *__next_bkt;
}
{
if (__n_elt + __n_ins > _M_next_resize)
{
- long double __min_bkts = (__n_elt + __n_ins)
- / (long double)_M_max_load_factor;
+ // If _M_next_resize is 0 it means that we have nothing allocated so
+ // far and that we start inserting elements. In this case we start
+ // with an initial bucket size of 11.
+ long double __min_bkts
+ = std::max<std::size_t>(__n_elt + __n_ins, _M_next_resize ? 0 : 11)
+ / (long double)_M_max_load_factor;
if (__min_bkts >= __n_bkt)
- return std::make_pair(true,
- _M_next_bkt(std::max<std::size_t>(__builtin_floor(__min_bkts) + 1,
- __n_bkt * _S_growth_factor)));
+ return { true,
+ _M_next_bkt(std::max<std::size_t>(__builtin_floorl(__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);
+ = __builtin_floorl(__n_bkt * (long double)_M_max_load_factor);
+ return { false, 0 };
}
else
- return std::make_pair(false, 0);
+ return { false, 0 };
}
} // namespace __detail
#include <testsuite_hooks.h>
template<typename _USet>
- void test(int threshold)
+ void
+ test(_USet& us, int threshold)
{
- _USet us;
auto nb_reserved = us.bucket_count();
us.reserve(nb_reserved);
auto bkts = us.bucket_count();
- for (int i = 0; i != threshold; ++i)
+ for (int nb_insert = 1; nb_insert <= threshold; ++nb_insert)
{
- if (i >= nb_reserved)
+ if (nb_insert > nb_reserved)
{
nb_reserved = bkts;
us.reserve(nb_reserved);
bkts = us.bucket_count();
}
- us.insert(i);
+ us.insert(nb_insert);
VERIFY( us.bucket_count() == bkts );
}
std::__detail::_Power2_rehash_policy,
std::__detail::_Hashtable_traits<false, true, true>>;
+template<typename _USet>
+ void
+ test_cont()
+ {
+ _USet us;
+ test(us, 150);
+
+ us.clear();
+ us.rehash(0);
+
+ test(us, 150);
+ }
+
int main()
{
- test<std::unordered_set<int>>(150);
- test<unordered_set_power2_rehash<int>>(150);
+ test_cont<std::unordered_set<int>>();
+ test_cont<unordered_set_power2_rehash<int>>();
return 0;
}
{
std::__detail::_Power2_rehash_policy policy;
VERIFY( policy._M_next_bkt(1) == 2 );
- VERIFY( policy._M_next_bkt(2) == 4 );
+ 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(16) == 16 );
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)) );
{
std::__detail::_Power2_rehash_policy policy;
- for (std::size_t i = 1;;)
+ for (std::size_t i = 3;;)
{
auto nxt = policy._M_next_bkt(i);
- if (nxt == i)
+ if (nxt <= i)
{
- // Equals only when reaching max.
+ // Lower or equal only when reaching max.
constexpr auto mx = std::numeric_limits<std::size_t>::max();
VERIFY( nxt == policy._M_next_bkt(mx) );
break;
}
- VERIFY( nxt > i );
- i = nxt;
+ VERIFY( nxt >= i );
+ i = nxt + 1;
}
}