diff mbox

New hashtable power 2 rehash policy

Message ID 5744C0EE.9050105@gmail.com
State New
Headers show

Commit Message

François Dumont May 24, 2016, 9 p.m. UTC
Attached patch applied then.

I had to regorganize things a little now that some pieces have been 
integrated in 71181 patch.

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.

François

On 23/05/2016 13:31, Jonathan Wakely wrote:
> On 17/05/16 22:28 +0200, François Dumont wrote:
>> On 14/05/2016 19:06, Daniel Krügler wrote:
>>> 1) The function __clp2 is declared using _GLIBCXX14_CONSTEXPR, which
>>> means that it is an inline function if and *only* if
>>> _GLIBCXX14_CONSTEXPR really expands to constexpr, otherwise it is
>>> *not* inline, which is probably not intended and could easily cause
>>> ODR problems. I suggest to mark it unconditionally as inline,
>>> regardless of _GLIBCXX14_CONSTEXPR.
>>
>> Maybe _GLIBCXX14_CONSTEXPR should take inline value previous to C++14 
>> mode.
>
> That's probably a good idea.
>
>> For the moment I simply added the inline as done in other situations.
>
> OK, thanks.
>
>>>
>>> 2) Furthermore I suggest to declare __clp2 as noexcept - this is
>>> (intentionally) *not* implied by constexpr.
>>>
>>> 3) Is there any reason, why _Power2_rehash_policy::_M_next_bkt
>>> shouldn't be noexcept?
>>>
>>> 4) Similar to (3) for _Power2_rehash_policy's member functions
>>> _M_bkt_for_elements, _M_need_rehash, _M_state, _M_reset
>> For noexcept I throught we were only adding it if necessary. We might 
>> have to go through a lot of code to find all places where noexcept 
>> could be added. Jonathan will give his feedback.
>
> I'm in favour of adding it anywhere that that definitely can't throw.
> We don't *need* to do that everywhere, but it doesn't hurt.
>
>> For the moment I have added it on all those methods.
>
> Great.
>
>> Thanks for feedback, updated and tested patch attached.
>
> OK for trunk - thanks!
>
>

Comments

Andreas Schwab May 25, 2016, 8:15 a.m. UTC | #1
François Dumont <frs.dumont@gmail.com> writes:

>     * include/bits/c++config (_GLIBCXX14_USE_CONSTEXPR): New.

FAIL: ext/profile/mutex_extensions_neg.cc  (test for errors, line 324)
FAIL: ext/profile/mutex_extensions_neg.cc (test for excess errors)
Excess errors:
/usr/local/gcc/gcc-20160525/Build/ia64-suse-linux/libstdc++-v3/include/ia64-suse-linux/bits/c++config.h:326:4: error: #error illegal use of multiple inlined namespaces

Andreas.
diff mbox

Patch

Index: include/bits/c++config
===================================================================
--- include/bits/c++config	(revision 236662)
+++ include/bits/c++config	(working copy)
@@ -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
 
Index: include/bits/hashtable_policy.h
===================================================================
--- include/bits/hashtable_policy.h	(revision 236662)
+++ include/bits/hashtable_policy.h	(working copy)
@@ -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 @@ 
   /// 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 @@ 
     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 @@ 
 	   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,7 +918,7 @@ 
 	   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>
     {
@@ -793,58 +925,23 @@ 
       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 __hashtable_base = _Hashtable_base<_Key, _Value, _ExtractKey,
+					       _Equal, _H1, _H2, _Hash,
+					       _Traits>;
 
-      using __base_type::insert;
-
-      std::pair<iterator, bool>
-      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>
-    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();
@@ -866,9 +963,9 @@ 
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
 	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits, bool _Unique_keys>
+	   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>
     {
@@ -912,28 +1009,46 @@ 
 	}
    };
 
+  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 @@ 
       max_load_factor(float __z)
       {
 	__hashtable* __this = static_cast<__hashtable*>(this);
-	__this->__rehash_policy(_Prime_rehash_policy(__z));
+	__this->__rehash_policy(_RehashPolicy(__z));
       }
 
       void
Index: testsuite/23_containers/unordered_set/hash_policy/26132.cc
===================================================================
--- testsuite/23_containers/unordered_set/hash_policy/26132.cc	(revision 236662)
+++ testsuite/23_containers/unordered_set/hash_policy/26132.cc	(working copy)
@@ -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;
 }
Index: testsuite/23_containers/unordered_set/hash_policy/load_factor.cc
===================================================================
--- testsuite/23_containers/unordered_set/hash_policy/load_factor.cc	(revision 236662)
+++ testsuite/23_containers/unordered_set/hash_policy/load_factor.cc	(working copy)
@@ -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() );
+	}
     }
-  }
-  {
-    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() );
+      _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() );
+	}
     }
-  }
-  {
-    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() );
+      _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() );
+	}
     }
   }
-}
 
+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;
 }
Index: testsuite/23_containers/unordered_set/hash_policy/power2_rehash.cc
===================================================================
--- testsuite/23_containers/unordered_set/hash_policy/power2_rehash.cc	(nonexistent)
+++ testsuite/23_containers/unordered_set/hash_policy/power2_rehash.cc	(working copy)
@@ -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;
+}
Index: testsuite/23_containers/unordered_set/hash_policy/rehash.cc
===================================================================
--- testsuite/23_containers/unordered_set/hash_policy/rehash.cc	(revision 236662)
+++ testsuite/23_containers/unordered_set/hash_policy/rehash.cc	(working copy)
@@ -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 @@ 
   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;
 }
Index: testsuite/23_containers/unordered_set/insert/hash_policy.cc
===================================================================
--- testsuite/23_containers/unordered_set/insert/hash_policy.cc	(revision 236662)
+++ testsuite/23_containers/unordered_set/insert/hash_policy.cc	(working copy)
@@ -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;
 }
Index: testsuite/23_containers/unordered_set/max_load_factor/robustness.cc
===================================================================
--- testsuite/23_containers/unordered_set/max_load_factor/robustness.cc	(revision 236662)
+++ testsuite/23_containers/unordered_set/max_load_factor/robustness.cc	(working copy)
@@ -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;
 }
Index: testsuite/performance/23_containers/insert/54075.cc
===================================================================
--- testsuite/performance/23_containers/insert/54075.cc	(revision 236662)
+++ testsuite/performance/23_containers/insert/54075.cc	(working copy)
@@ -127,8 +127,28 @@ 
   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()
 {
   using namespace __gnu_test;
@@ -181,6 +201,19 @@ 
   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>>(
Index: testsuite/performance/23_containers/insert_erase/41975.cc
===================================================================
--- testsuite/performance/23_containers/insert_erase/41975.cc	(revision 236662)
+++ testsuite/performance/23_containers/insert_erase/41975.cc	(working copy)
@@ -177,6 +177,16 @@ 
 					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>,
 				    std::equal_to<std::string>,
@@ -190,6 +200,16 @@ 
 					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 @@ 
 	"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 @@ 
 	"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;
 }