c++config (_GLIBCXX14_USE_CONSTEXPR): New.

2016-05-24  François Dumont  <fdumont@gcc.gnu.org>

	* include/bits/c++config (_GLIBCXX14_USE_CONSTEXPR): New.
	* include/bits/hashtable_policy.h
	(_Prime_rehash_policy::__has_load_factor): New. Mark rehash policy
	having load factor management.
	(_Mask_range_hashing): New.
	(__clp2): New.
	(_Power2_rehash_policy): New.
	(_Inserts<>): Remove last template parameter, _Unique_keys, so that
	partial specializations only depend on whether iterators are constant
	or not.
	* testsuite/23_containers/unordered_set/hash_policy/26132.cc: Adapt to
	test new hash policy.
	* testsuite/23_containers/unordered_set/hash_policy/load_factor.cc:
	Likewise.
	* testsuite/23_containers/unordered_set/hash_policy/rehash.cc:
	Likewise.
	* testsuite/23_containers/unordered_set/insert/hash_policy.cc:
	Likewise.
	* testsuite/23_containers/unordered_set/max_load_factor/robustness.cc:
	Likewise.
	* testsuite/23_containers/unordered_set/hash_policy/power2_rehash.cc:
	New.
	* testsuite/performance/23_containers/insert/54075.cc: Add benchmark
	using the new hash policy.
	* testsuite/performance/23_containers/insert_erase/41975.cc: Likewise.

From-SVN: r236669
This commit is contained in:
François Dumont 2016-05-24 20:55:57 +00:00
parent f65e97fd3d
commit 732eb07625
11 changed files with 566 additions and 233 deletions

View File

@ -1,3 +1,31 @@
2016-05-24 François Dumont <fdumont@gcc.gnu.org>
* include/bits/c++config (_GLIBCXX14_USE_CONSTEXPR): New.
* include/bits/hashtable_policy.h
(_Prime_rehash_policy::__has_load_factor): New. Mark rehash policy
having load factor management.
(_Mask_range_hashing): New.
(__clp2): New.
(_Power2_rehash_policy): New.
(_Inserts<>): Remove last template parameter, _Unique_keys, so that
partial specializations only depend on whether iterators are constant
or not.
* testsuite/23_containers/unordered_set/hash_policy/26132.cc: Adapt to
test new hash policy.
* testsuite/23_containers/unordered_set/hash_policy/load_factor.cc:
Likewise.
* testsuite/23_containers/unordered_set/hash_policy/rehash.cc:
Likewise.
* testsuite/23_containers/unordered_set/insert/hash_policy.cc:
Likewise.
* testsuite/23_containers/unordered_set/max_load_factor/robustness.cc:
Likewise.
* testsuite/23_containers/unordered_set/hash_policy/power2_rehash.cc:
New.
* testsuite/performance/23_containers/insert/54075.cc: Add benchmark
using the new hash policy.
* testsuite/performance/23_containers/insert_erase/41975.cc: Likewise.
2016-05-24 Jonathan Wakely <jwakely@redhat.com>
* include/bits/stl_queue.h (priority_queue::value_compare): Define.

View File

@ -106,8 +106,10 @@
#ifndef _GLIBCXX14_CONSTEXPR
# if __cplusplus >= 201402L
# define _GLIBCXX14_CONSTEXPR constexpr
# define _GLIBCXX14_USE_CONSTEXPR constexpr
# else
# define _GLIBCXX14_CONSTEXPR
# define _GLIBCXX14_USE_CONSTEXPR const
# endif
#endif

View File

@ -31,6 +31,8 @@
#ifndef _HASHTABLE_POLICY_H
#define _HASHTABLE_POLICY_H 1
#include <bits/stl_algobase.h> // for std::min.
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@ -457,6 +459,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 +505,135 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
mutable std::size_t _M_next_resize;
};
/// Range hashing function assuming that second arg 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); }
};
/// Compute closest power of 2.
_GLIBCXX14_CONSTEXPR
inline std::size_t
__clp2(std::size_t n) noexcept
{
#if __SIZEOF_SIZE_T__ >= 8
std::uint_fast64_t x = n;
#else
std::uint_fast32_t x = n;
#endif
// Algorithm from Hacker's Delight, Figure 3-3.
x = x - 1;
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >>16);
#if __SIZEOF_SIZE_T__ >= 8
x = x | (x >>32);
#endif
return x + 1;
}
/// Rehash policy providing power of 2 bucket numbers. Avoids 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 noexcept
{
_GLIBCXX14_USE_CONSTEXPR size_t __max_width
= std::min<size_t>(sizeof(size_t), 8);
_GLIBCXX14_USE_CONSTEXPR auto __max_bkt
= std::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;
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);
else
_M_next_resize
= __builtin_ceil(__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); }
// __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<bool, std::size_t>
_M_need_rehash(std::size_t __n_bkt, std::size_t __n_elt,
std::size_t __n_ins) const noexcept
{
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<std::size_t>(__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 noexcept
{ return _M_next_resize; }
void
_M_reset() noexcept
{ _M_next_resize = 0; }
void
_M_reset(_State __state) noexcept
{ _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
@ -776,8 +909,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.
@ -786,24 +918,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 __hashtable_base = _Hashtable_base<_Key, _Value, _ExtractKey,
_Equal, _H1, _H2, _Hash,
_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;
std::pair<iterator, bool>
__ireturn_type
insert(value_type&& __v)
{
__hashtable& __h = this->_M_conjure_hashtable();
@ -827,48 +965,7 @@ _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, 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 __hashtable = typename __base_type::__hashtable;
using __node_gen_type = typename __base_type::__node_gen_type;
using __base_type::insert;
iterator
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());
}
};
/// Specialization.
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _H1, typename _H2, typename _Hash,
typename _RehashPolicy, typename _Traits, bool _Unique_keys>
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>
{
@ -912,28 +1009,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
};
template<typename _Policy>
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 _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _H1, typename _H2, typename _Hash,
typename _RehashPolicy, typename _Traits>
typename _RehashPolicy, typename _Traits,
typename =
__detected_or_t<std::false_type, __has_load_factor, _RehashPolicy>>
struct _Rehash_base;
/// Specialization.
/// Specialization when rehash policy doesn't provide load factor management.
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _H1, typename _H2, typename _Hash, typename _Traits>
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<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _H1, typename _H2, typename _Hash,
typename _RehashPolicy, typename _Traits>
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
@ -946,7 +1061,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

View File

@ -23,35 +23,48 @@
#include <testsuite_hooks.h>
// libstdc++/26132
void test01()
{
bool test __attribute__((unused)) = true;
template<typename _USet>
void test()
{
bool test __attribute__((unused)) = true;
for (float lf = 1.0; lf < 101.0; lf *= 10.0)
for (int size = 1; size <= 6561; size *= 3)
{
std::unordered_set<int> us1;
typedef std::unordered_set<int>::size_type size_type;
us1.max_load_factor(10.0);
for (float lf = 1.0; lf < 101.0; lf *= 10.0)
for (int size = 1; size <= 6561; size *= 3)
{
_USet us1;
typedef typename _USet::size_type size_type;
for (int i = 0; i < size; ++i)
us1.insert(i);
us1.max_load_factor(10.0);
us1.max_load_factor(lf);
for (int i = 0; i < size; ++i)
us1.insert(i);
for (int i = 1; i <= 6561; i *= 81)
{
const size_type n = size * 81 / i;
us1.rehash(n);
VERIFY( us1.bucket_count() > us1.size() / us1.max_load_factor() );
VERIFY( us1.bucket_count() >= n );
}
}
}
us1.max_load_factor(lf);
for (int i = 1; i <= 6561; i *= 81)
{
const size_type n = size * 81 / i;
us1.rehash(n);
VERIFY( us1.bucket_count() > us1.size() / us1.max_load_factor() );
VERIFY( us1.bucket_count() >= n );
}
}
}
template<typename _Value>
using unordered_set_power2_rehash =
std::_Hashtable<_Value, _Value, std::allocator<_Value>,
std::__detail::_Identity,
std::equal_to<_Value>,
std::hash<_Value>,
std::__detail::_Mask_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Power2_rehash_policy,
std::__detail::_Hashtable_traits<false, true, true>>;
int main()
{
test01();
test<std::unordered_set<int>>();
test<unordered_set_power2_rehash<int>>();
return 0;
}

View File

@ -18,41 +18,55 @@
// { dg-options "-std=gnu++11" }
#include <unordered_set>
#include <testsuite_hooks.h>
void test01()
{
bool test __attribute__((unused)) = true;
template<typename _USet>
void test()
{
std::unordered_set<int> us;
for (int i = 0; i != 100000; ++i)
bool test __attribute__((unused)) = true;
{
us.insert(i);
VERIFY( us.load_factor() <= us.max_load_factor() );
_USet us;
for (int i = 0; i != 100000; ++i)
{
us.insert(i);
VERIFY( us.load_factor() <= us.max_load_factor() );
}
}
{
_USet us;
us.max_load_factor(3.f);
for (int i = 0; i != 100000; ++i)
{
us.insert(i);
VERIFY( us.load_factor() <= us.max_load_factor() );
}
}
{
_USet us;
us.max_load_factor(.3f);
for (int i = 0; i != 100000; ++i)
{
us.insert(i);
VERIFY( us.load_factor() <= us.max_load_factor() );
}
}
}
{
std::unordered_set<int> us;
us.max_load_factor(3.f);
for (int i = 0; i != 100000; ++i)
{
us.insert(i);
VERIFY( us.load_factor() <= us.max_load_factor() );
}
}
{
std::unordered_set<int> us;
us.max_load_factor(.3f);
for (int i = 0; i != 100000; ++i)
{
us.insert(i);
VERIFY( us.load_factor() <= us.max_load_factor() );
}
}
}
template<typename _Value>
using unordered_set_power2_rehash =
std::_Hashtable<_Value, _Value, std::allocator<_Value>,
std::__detail::_Identity,
std::equal_to<_Value>,
std::hash<_Value>,
std::__detail::_Mask_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Power2_rehash_policy,
std::__detail::_Hashtable_traits<false, true, true>>;
int main()
{
test01();
test<std::unordered_set<int>>();
test<unordered_set_power2_rehash<int>>();
return 0;
}

View File

@ -0,0 +1,42 @@
// Copyright (C) 2016 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
// <http://www.gnu.org/licenses/>.
//
// { dg-options "-std=gnu++11" }
#include <unordered_set>
#include <testsuite_hooks.h>
void test01()
{
bool test __attribute__((unused)) = true;
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(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;
}

View File

@ -18,13 +18,15 @@
// { dg-options "-std=gnu++11" }
#include <unordered_set>
#include <testsuite_hooks.h>
void test01()
template<typename _USet>
void test()
{
bool test __attribute__((unused)) = true;
std::unordered_set<int> us;
typedef typename std::unordered_set<int>::size_type size_type;
_USet us;
typedef typename _USet::size_type size_type;
bool rehashed = false;
for (int i = 0; i != 100000; ++i)
{
@ -55,8 +57,20 @@ void test01()
VERIFY( rehashed );
}
template<typename _Value>
using unordered_set_power2_rehash =
std::_Hashtable<_Value, _Value, std::allocator<_Value>,
std::__detail::_Identity,
std::equal_to<_Value>,
std::hash<_Value>,
std::__detail::_Mask_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Power2_rehash_policy,
std::__detail::_Hashtable_traits<false, true, true>>;
int main()
{
test01();
test<std::unordered_set<int>>();
test<unordered_set_power2_rehash<int>>();
return 0;
}

View File

@ -20,94 +20,122 @@
#include <unordered_set>
#include <vector>
#include <limits>
#include <ext/throw_allocator.h>
#include <testsuite_hooks.h>
void test01()
{
bool test __attribute__((unused)) = true;
template<template<typename _Value, typename _Hash,
typename _Pred, typename _Alloc>
typename _USet>
void test01()
{
bool test __attribute__((unused)) = true;
typedef std::numeric_limits<std::size_t> nl_size_t;
std::unordered_set<int, std::hash<int>, std::equal_to<int>,
__gnu_cxx::throw_allocator_limit<int> > us;
const int nb = 100;
int scheduled_throw_counter = 0;
std::size_t thrown_exceptions = 0;
for (int i = 0; i != nb; ++i)
{
if ((float)(us.size() + 1)
/ (float)us.bucket_count() >= us.max_load_factor())
{
// We are going to need a rehash, lets introduce allocation issues:
__gnu_cxx::limit_condition::set_limit(scheduled_throw_counter++);
}
try
{
VERIFY(us.insert(i).second);
scheduled_throw_counter = 0;
}
catch (const __gnu_cxx::forced_error&)
{
++thrown_exceptions;
--i;
}
VERIFY( us.load_factor() <= us.max_load_factor() );
__gnu_cxx::limit_condition::set_limit(nl_size_t::max());
}
// Make sure whatever happen we restore throw allocator limit at exit.
__gnu_cxx::limit_condition::adjustor_base adj;
VERIFY( thrown_exceptions != 0 );
// Check that all values have been inserted:
for (int i = 0; i != nb; ++i)
{
VERIFY( us.count(i) == 1 );
}
}
typedef std::numeric_limits<std::size_t> nl_size_t;
_USet<int, std::hash<int>, std::equal_to<int>,
__gnu_cxx::throw_allocator_limit<int> > us;
const int nb = 100;
int scheduled_throw_counter = 0;
std::size_t thrown_exceptions = 0;
for (int i = 0; i != nb; ++i)
{
if ((float)(us.size() + 1)
/ (float)us.bucket_count() >= us.max_load_factor())
{
// We are going to need a rehash, lets introduce allocation issues:
__gnu_cxx::limit_condition::set_limit(scheduled_throw_counter++);
}
try
{
VERIFY(us.insert(i).second);
scheduled_throw_counter = 0;
}
catch (const __gnu_cxx::forced_error&)
{
++thrown_exceptions;
--i;
}
VERIFY( us.load_factor() <= us.max_load_factor() );
__gnu_cxx::limit_condition::set_limit(nl_size_t::max());
}
void test02()
{
bool test __attribute__((unused)) = true;
VERIFY( thrown_exceptions != 0 );
// Check that all values have been inserted:
for (int i = 0; i != nb; ++i)
{
VERIFY( us.count(i) == 1 );
}
}
typedef std::numeric_limits<std::size_t> nl_size_t;
std::unordered_set<int, std::hash<int>, std::equal_to<int>,
__gnu_cxx::throw_allocator_limit<int> > us;
const int nb = 100;
int scheduled_throw_counter = 0;
std::size_t thrown_exceptions = 0;
for (int i = 0; i != nb; ++i)
{
if ((float)(us.size() + 2)
/ (float)us.bucket_count() >= us.max_load_factor())
{
// We are going to need a rehash, lets introduce allocation issues:
__gnu_cxx::limit_condition::set_limit(scheduled_throw_counter++);
}
try
{
std::vector<int> v = { i, i };
// Check the insert range robustness
us.insert(v.begin(), v.end());
scheduled_throw_counter = 0;
}
catch (const __gnu_cxx::forced_error&)
{
++thrown_exceptions;
--i;
}
VERIFY( us.load_factor() <= us.max_load_factor() );
__gnu_cxx::limit_condition::set_limit(nl_size_t::max());
}
template<template<typename _Value, typename _Hash,
typename _Pred, typename _Alloc>
typename _USet>
void test02()
{
bool test __attribute__((unused)) = true;
VERIFY( thrown_exceptions != 0 );
// Check that all values have been inserted:
for (int i = 0; i != nb; ++i)
{
VERIFY( us.count(i) == 1 );
}
}
// Make sure whatever happen we restore throw allocator limit at exit.
__gnu_cxx::limit_condition::adjustor_base adj;
typedef std::numeric_limits<std::size_t> nl_size_t;
_USet<int, std::hash<int>, std::equal_to<int>,
__gnu_cxx::throw_allocator_limit<int> > us;
const int nb = 100;
int scheduled_throw_counter = 0;
std::size_t thrown_exceptions = 0;
for (int i = 0; i != nb; ++i)
{
if ((float)(us.size() + 2)
/ (float)us.bucket_count() >= us.max_load_factor())
{
// We are going to need a rehash, lets introduce allocation issues:
__gnu_cxx::limit_condition::set_limit(scheduled_throw_counter++);
}
try
{
std::vector<int> v = { i, i };
// Check the insert range robustness
us.insert(v.begin(), v.end());
scheduled_throw_counter = 0;
}
catch (const __gnu_cxx::forced_error&)
{
++thrown_exceptions;
--i;
}
VERIFY( us.load_factor() <= us.max_load_factor() );
__gnu_cxx::limit_condition::set_limit(nl_size_t::max());
}
VERIFY( thrown_exceptions != 0 );
// Check that all values have been inserted:
for (int i = 0; i != nb; ++i)
{
VERIFY( us.count(i) == 1 );
}
}
template<typename _Value, typename _Hash,
typename _Pred, typename _Alloc>
using unordered_set_power2_rehash =
std::_Hashtable<_Value, _Value, _Alloc,
std::__detail::_Identity,
_Pred,
_Hash,
std::__detail::_Mask_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Power2_rehash_policy,
std::__detail::_Hashtable_traits<false, true, true>>;
int main()
{
test01();
test02();
test01<std::unordered_set>();
test01<unordered_set_power2_rehash>();
test02<std::unordered_set>();
test02<unordered_set_power2_rehash>();
return 0;
}

View File

@ -22,62 +22,78 @@
#include <ext/throw_allocator.h>
#include <testsuite_hooks.h>
void test01()
{
bool test __attribute__((unused)) = true;
template<template<typename _Value, typename _Hash,
typename _Pred, typename _Alloc>
typename _USet>
void test()
{
bool test __attribute__((unused)) = true;
typedef std::numeric_limits<std::size_t> nl_size_t;
std::unordered_set<int, std::hash<int>, std::equal_to<int>,
__gnu_cxx::throw_allocator_limit<int> > us;
int val = 0;
for (; val != 100; ++val)
{
VERIFY( us.insert(val).second );
VERIFY( us.load_factor() <= us.max_load_factor() );
}
typedef std::numeric_limits<std::size_t> nl_size_t;
_USet<int, std::hash<int>, std::equal_to<int>,
__gnu_cxx::throw_allocator_limit<int> > us;
int val = 0;
for (; val != 100; ++val)
{
VERIFY( us.insert(val).second );
VERIFY( us.load_factor() <= us.max_load_factor() );
}
float cur_max_load_factor = us.max_load_factor();
int counter = 0;
std::size_t thrown_exceptions = 0;
float cur_max_load_factor = us.max_load_factor();
int counter = 0;
std::size_t thrown_exceptions = 0;
// Reduce max load factor.
us.max_load_factor(us.max_load_factor() / 2);
// Reduce max load factor.
us.max_load_factor(us.max_load_factor() / 4);
// At this point load factor is higher than max_load_factor because we can't
// rehash in max_load_factor call.
VERIFY( us.load_factor() > us.max_load_factor() );
// At this point load factor is higher than max_load_factor because we can't
// rehash in max_load_factor call.
VERIFY( us.load_factor() > us.max_load_factor() );
while (true)
{
__gnu_cxx::limit_condition::set_limit(counter++);
bool do_break = false;
try
{
size_t nbkts = us.bucket_count();
// Check that unordered_set will still be correctly resized when
// needed.
VERIFY( us.insert(val++).second );
while (true)
{
__gnu_cxx::limit_condition::limit_adjustor adjustor(counter++);
bool do_break = false;
try
{
size_t nbkts = us.bucket_count();
// Check that unordered_set will still be correctly resized when
// needed.
VERIFY( us.insert(val++).second );
VERIFY( us.bucket_count() != nbkts );
VERIFY( us.load_factor() <= us.max_load_factor() );
do_break = true;
}
catch (const __gnu_cxx::forced_error&)
{
// max load factor doesn't change.
VERIFY( us.max_load_factor() == .25f );
++thrown_exceptions;
}
VERIFY( us.bucket_count() != nbkts );
VERIFY( us.load_factor() <= us.max_load_factor() );
do_break = true;
}
catch (const __gnu_cxx::forced_error&)
{
// max load factor doesn't change.
VERIFY( us.max_load_factor() == .5f );
++thrown_exceptions;
}
if (do_break)
break;
}
if (do_break)
break;
}
VERIFY( thrown_exceptions > 0 );
}
VERIFY( thrown_exceptions > 0 );
}
template<typename _Value, typename _Hash,
typename _Pred, typename _Alloc>
using unordered_set_power2_rehash =
std::_Hashtable<_Value, _Value, _Alloc,
std::__detail::_Identity,
_Pred,
_Hash,
std::__detail::_Mask_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Power2_rehash_policy,
std::__detail::_Hashtable_traits<false, true, true>>;
int main()
{
test01();
test<std::unordered_set>();
test<unordered_set_power2_rehash>();
return 0;
}

View File

@ -127,7 +127,27 @@ template<bool cache>
using __umset = std::__umset_hashtable<Foo, HashFunction,
std::equal_to<Foo>,
std::allocator<Foo>,
std::__uset_traits<cache>>;
std::__umset_traits<cache>>;
template<bool cache>
using __uset2 =
std::_Hashtable<Foo, Foo, std::allocator<Foo>,
std::__detail::_Identity,
std::equal_to<Foo>, HashFunction,
std::__detail::_Mask_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Power2_rehash_policy,
std::__uset_traits<cache>>;
template<bool cache>
using __umset2 =
std::_Hashtable<Foo, Foo, std::allocator<Foo>,
std::__detail::_Identity,
std::equal_to<Foo>, HashFunction,
std::__detail::_Mask_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Power2_rehash_policy,
std::__umset_traits<cache>>;
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<false>>(
"std::unordered_set2 without hash code cached ", foos);
bench<__uset2<true>>(
"std::unordered_set2 with hash code cached ", foos);
bench<__umset2<false>>(
"std::unordered_multiset2 without hash code cached ", foos);
bench<__umset2<true>>(
"std::unordered_multiset2 with hash code cached ", foos);
stop_counters(time, resource);
report_performance(__FILE__, "std2 benches", time, resource);
bench<std::unordered_set<Foo, HashFunction>>(
"std::unordered_set default cache ", foos);
bench<std::unordered_multiset<Foo, HashFunction>>(

View File

@ -176,6 +176,16 @@ template<bool cache>
std::allocator<int>,
cache>;
template<bool cache>
using __uset2 =
std::_Hashtable<int, int, std::allocator<int>,
std::__detail::_Identity,
std::equal_to<int>, std::hash<int>,
std::__detail::_Mask_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Power2_rehash_policy,
std::__uset_traits<cache>>;
template<bool cache>
using __str_uset =
std::__uset_hashtable<std::string, std::hash<std::string>,
@ -190,6 +200,16 @@ template<bool cache>
std::allocator<std::string>,
cache>;
template<bool cache>
using __str_uset2 =
std::_Hashtable<std::string, std::string, std::allocator<std::string>,
std::__detail::_Identity,
std::equal_to<std::string>, std::hash<std::string>,
std::__detail::_Mask_range_hashing,
std::__detail::_Default_ranged_hash,
std::__detail::_Power2_rehash_policy,
std::__uset_traits<cache>>;
int main()
{
bench<__tr1_uset<false>>(
@ -202,6 +222,10 @@ int main()
"std::unordered_set<int> with hash code cached");
bench<std::unordered_set<int>>(
"std::unordered_set<int> default cache");
bench<__uset2<false>>(
"std::unordered_set2<int> without hash code cached");
bench<__uset2<true>>(
"std::unordered_set2<int> with hash code cached");
bench_str<__tr1_str_uset<false>>(
"std::tr1::unordered_set<string> without hash code cached");
bench_str<__tr1_str_uset<true>>(
@ -210,7 +234,11 @@ int main()
"std::unordered_set<string> without hash code cached");
bench_str<__str_uset<true>>(
"std::unordered_set<string> with hash code cached");
bench_str<std::unordered_set<std::string>>(
bench_str<std::unordered_set<std::string>>(
"std::unordered_set<string> default cache");
bench_str<__str_uset2<false>>(
"std::unordered_set2<string> without hash code cached");
bench_str<__str_uset2<true>>(
"std::unordered_set2<string> with hash code cached");
return 0;
}