From 2fed2fbd9e747f70e61e7b5ec41c57b9cbe2a695 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 18 Feb 2012 15:47:59 +0000 Subject: [PATCH] Unordered: Hashing policy for 64 bit computers. [SVN r77066] --- include/boost/unordered/detail/buckets.hpp | 105 ++++++++++++++++-- include/boost/unordered/detail/equivalent.hpp | 39 ++++--- include/boost/unordered/detail/table.hpp | 50 +++++---- include/boost/unordered/detail/unique.hpp | 39 ++++--- include/boost/unordered/unordered_map.hpp | 6 +- include/boost/unordered/unordered_set.hpp | 6 +- 6 files changed, 179 insertions(+), 66 deletions(-) diff --git a/include/boost/unordered/detail/buckets.hpp b/include/boost/unordered/detail/buckets.hpp index 7492220b..c8f5de3b 100644 --- a/include/boost/unordered/detail/buckets.hpp +++ b/include/boost/unordered/detail/buckets.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #if defined(BOOST_MSVC) #pragma warning(push) @@ -29,7 +30,8 @@ namespace boost { namespace unordered { namespace detail { template struct table; template struct bucket; struct ptr_bucket; - template struct buckets; + template + struct buckets; /////////////////////////////////////////////////////////////////// // @@ -179,12 +181,97 @@ namespace boost { namespace unordered { namespace detail { enum { extra_node = false }; }; + /////////////////////////////////////////////////////////////////// + // + // Hash Policy + // + // Don't really want buckets to derive from this, but will for now. + + template + struct prime_policy + { + template + static inline SizeT apply_hash(Hash const& hf, T const& x) { + return hf(x); + } + + static inline SizeT to_bucket(SizeT bucket_count, SizeT hash) { + return hash % bucket_count; + } + + static inline SizeT new_bucket_count(SizeT min) { + return boost::unordered::detail::next_prime(min); + } + + static inline SizeT prev_bucket_count(SizeT max) { + return boost::unordered::detail::prev_prime(max); + } + }; + + template + struct mix64_policy + { + template + static inline SizeT apply_hash(Hash const& hf, T const& x) { + SizeT key = hf(x); + key = (~key) + (key << 21); // key = (key << 21) - key - 1; + key = key ^ (key >> 24); + key = (key + (key << 3)) + (key << 8); // key * 265 + key = key ^ (key >> 14); + key = (key + (key << 2)) + (key << 4); // key * 21 + key = key ^ (key >> 28); + key = key + (key << 31); + return key; + } + + static inline SizeT to_bucket(SizeT bucket_count, SizeT hash) { + return hash & (bucket_count - 1); + } + + static inline SizeT new_bucket_count(SizeT min) { + if (min <= 4) return 4; + --min; + min |= min >> 1; + min |= min >> 2; + min |= min >> 4; + min |= min >> 8; + min |= min >> 16; + min |= min >> 32; + return min + 1; + } + + static inline SizeT prev_bucket_count(SizeT max) { + max |= max >> 1; + max |= max >> 2; + max |= max >> 4; + max |= max >> 8; + max |= max >> 16; + max |= max >> 32; + return (max >> 1) + 1; + } + }; + + template + struct pick_policy_impl { + typedef prime_policy type; + }; + + template <> + struct pick_policy_impl<64, 2> { + typedef mix64_policy type; + }; + + struct pick_policy : + pick_policy_impl< + std::numeric_limits::digits, + std::numeric_limits::radix> {}; + /////////////////////////////////////////////////////////////////// // // Buckets - template - struct buckets + template + struct buckets : Policy { private: buckets(buckets const&); @@ -247,7 +334,7 @@ namespace boost { namespace unordered { namespace detail { std::size_t max_bucket_count() const { // -1 to account for the start bucket. - return boost::unordered::detail::prev_prime( + return this->prev_bucket_count( bucket_allocator_traits::max_size(bucket_alloc()) - 1); } @@ -292,7 +379,7 @@ namespace boost { namespace unordered { namespace detail { if (!ptr) return 0; std::size_t count = 0; - while(ptr && ptr->hash_ % this->bucket_count_ == index) + while(ptr && this->to_bucket(this->bucket_count_, ptr->hash_) == index) { ++count; ptr = static_cast(ptr->next_); @@ -487,7 +574,7 @@ namespace boost { namespace unordered { namespace detail { else { bucket_pointer next_bucket = this->get_bucket( - next->hash_ % this->bucket_count_); + this->to_bucket(this->bucket_count_, next->hash_)); if (next_bucket != bucket) { @@ -513,7 +600,7 @@ namespace boost { namespace unordered { namespace detail { if (n == end) return; std::size_t new_bucket_index = - n->hash_ % this->bucket_count_; + this->to_bucket(this->bucket_count_, n->hash_); if (bucket_index != new_bucket_index) { bucket_index = new_bucket_index; break; @@ -529,7 +616,7 @@ namespace boost { namespace unordered { namespace detail { if (n == end) break; std::size_t new_bucket_index = - n->hash_ % this->bucket_count_; + this->to_bucket(this->bucket_count_, n->hash_); if (bucket_index != new_bucket_index) { bucket_index = new_bucket_index; this->get_bucket(bucket_index)->next_ = previous_pointer(); @@ -538,7 +625,7 @@ namespace boost { namespace unordered { namespace detail { // Finally fix the bucket containing the trailing node. if (n) { - this->get_bucket(n->hash_ % this->bucket_count_)->next_ + this->get_bucket(this->to_bucket(this->bucket_count_, n->hash_))->next_ = prev; } } diff --git a/include/boost/unordered/detail/equivalent.hpp b/include/boost/unordered/detail/equivalent.hpp index 6e7e4190..fb25e39c 100644 --- a/include/boost/unordered/detail/equivalent.hpp +++ b/include/boost/unordered/detail/equivalent.hpp @@ -136,6 +136,8 @@ namespace boost { namespace unordered { namespace detail { typedef boost::unordered::detail::grouped_table_impl table; typedef boost::unordered::detail::set_extractor extractor; + + typedef boost::unordered::detail::pick_policy::type policy; }; template @@ -160,6 +162,8 @@ namespace boost { namespace unordered { namespace detail { typedef boost::unordered::detail::grouped_table_impl table; typedef boost::unordered::detail::map_extractor extractor; + + typedef boost::unordered::detail::pick_policy::type policy; }; template @@ -219,7 +223,7 @@ namespace boost { namespace unordered { namespace detail { Key const& k, Pred const& eq) const { - std::size_t bucket_index = hash % this->bucket_count_; + std::size_t bucket_index = this->to_bucket(this->bucket_count_, hash); node_pointer n = this->get_start(bucket_index); for (;;) @@ -234,7 +238,7 @@ namespace boost { namespace unordered { namespace detail { } else { - if (node_hash % this->bucket_count_ != bucket_index) + if (this->to_bucket(this->bucket_count_, node_hash) != bucket_index) return node_pointer(); } @@ -397,25 +401,25 @@ namespace boost { namespace unordered { namespace detail { if(pos) { this->add_after_node(n, pos); if (n->next_) { - std::size_t next_bucket = - static_cast(n->next_)->hash_ % - this->bucket_count_; - if (next_bucket != hash % this->bucket_count_) { + std::size_t next_bucket = this->to_bucket( + this->bucket_count_, + static_cast(n->next_)->hash_); + if (next_bucket != this->to_bucket(this->bucket_count_, hash)) { this->get_bucket(next_bucket)->next_ = n; } } } else { - bucket_pointer b = this->get_bucket(hash % this->bucket_count_); + bucket_pointer b = this->get_bucket(this->to_bucket(this->bucket_count_, hash)); if (!b->next_) { previous_pointer start_node = this->get_previous_start(); if (start_node->next_) { - this->get_bucket( + this->get_bucket(this->to_bucket(this->bucket_count_, static_cast(start_node->next_)->hash_ - % this->bucket_count_)->next_ = n; + ))->next_ = n; } b->next_ = start_node; @@ -435,7 +439,7 @@ namespace boost { namespace unordered { namespace detail { node_pointer emplace_impl(node_constructor& a) { key_type const& k = this->get_key(a.value()); - std::size_t hash = this->hash_function()(k); + std::size_t hash = this->hash(k); node_pointer position = this->find_node(hash, k); // reserve has basic exception safety if the hash function @@ -447,7 +451,7 @@ namespace boost { namespace unordered { namespace detail { void emplace_impl_no_rehash(node_constructor& a) { key_type const& k = this->get_key(a.value()); - std::size_t hash = this->hash_function()(k); + std::size_t hash = this->hash(k); this->add_node(a, hash, this->find_node(hash, k)); } @@ -523,8 +527,8 @@ namespace boost { namespace unordered { namespace detail { { if(!this->size_) return 0; - std::size_t hash = this->hash_function()(k); - std::size_t bucket_index = hash % this->bucket_count_; + std::size_t hash = this->hash(k); + std::size_t bucket_index = this->to_bucket(this->bucket_count_, hash); bucket_pointer bucket = this->get_bucket(bucket_index); previous_pointer prev = bucket->next_; @@ -535,7 +539,7 @@ namespace boost { namespace unordered { namespace detail { if (!prev->next_) return 0; std::size_t node_hash = static_cast(prev->next_)->hash_; - if (node_hash % this->bucket_count_ != bucket_index) + if (this->to_bucket(this->bucket_count_, node_hash) != bucket_index) return 0; if (node_hash == hash && this->key_eq()(k, this->get_key( @@ -560,7 +564,7 @@ namespace boost { namespace unordered { namespace detail { node_pointer next = static_cast(r->next_); bucket_pointer bucket = this->get_bucket( - r->hash_ % this->bucket_count_); + this->to_bucket(this->bucket_count_, r->hash_)); previous_pointer prev = unlink_node(*bucket, r); this->fix_buckets(bucket, prev, next); @@ -574,7 +578,7 @@ namespace boost { namespace unordered { namespace detail { { if (r1 == r2) return r2; - std::size_t bucket_index = r1->hash_ % this->bucket_count_; + std::size_t bucket_index = this->to_bucket(this->bucket_count_, r1->hash_); previous_pointer prev = unlink_nodes( *this->get_bucket(bucket_index), r1, r2); this->fix_buckets_range(bucket_index, prev, r1, r2); @@ -809,7 +813,8 @@ namespace boost { namespace unordered { namespace detail { static previous_pointer place_in_bucket(buckets& dst, previous_pointer prev, node_pointer end) { - bucket_pointer b = dst.get_bucket(end->hash_ % dst.bucket_count_); + bucket_pointer b = dst.get_bucket(dst.to_bucket( + dst.bucket_count_, end->hash_)); if (!b->next_) { b->next_ = static_cast(prev); diff --git a/include/boost/unordered/detail/table.hpp b/include/boost/unordered/detail/table.hpp index 66c46084..f535f8cc 100644 --- a/include/boost/unordered/detail/table.hpp +++ b/include/boost/unordered/detail/table.hpp @@ -24,15 +24,16 @@ namespace boost { namespace unordered { namespace iterator_detail { template struct iterator; template struct c_iterator; - template struct l_iterator; + template + struct l_iterator; template struct cl_iterator; + typename Value, typename Policy> struct cl_iterator; // Local Iterators // // all no throw - template + template struct l_iterator : public boost::iterator< std::forward_iterator_tag, Value, std::ptrdiff_t, @@ -40,7 +41,7 @@ namespace boost { namespace unordered { namespace iterator_detail { { #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) template + typename Value2, typename Policy2> friend struct boost::unordered::iterator_detail::cl_iterator; private: #endif @@ -66,7 +67,8 @@ namespace boost { namespace unordered { namespace iterator_detail { l_iterator& operator++() { ptr_ = static_cast(ptr_->next_); - if (ptr_ && ptr_->hash_ % bucket_count_ != bucket_) + if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_) + != bucket_) ptr_ = node_pointer(); return *this; } @@ -86,14 +88,15 @@ namespace boost { namespace unordered { namespace iterator_detail { } }; - template + template struct cl_iterator : public boost::iterator< std::forward_iterator_tag, Value, std::ptrdiff_t, ConstNodePointer, Value const&> { friend struct boost::unordered::iterator_detail::l_iterator - ; + ; private: typedef NodePointer node_pointer; @@ -109,7 +112,7 @@ namespace boost { namespace unordered { namespace iterator_detail { ptr_(x), bucket_(b), bucket_count_(c) {} cl_iterator(boost::unordered::iterator_detail::l_iterator< - NodePointer, Value> const& x) : + NodePointer, Value, Policy> const& x) : ptr_(x.ptr_), bucket_(x.bucket_), bucket_count_(x.bucket_count_) {} @@ -124,7 +127,8 @@ namespace boost { namespace unordered { namespace iterator_detail { cl_iterator& operator++() { ptr_ = static_cast(ptr_->next_); - if (ptr_ && ptr_->hash_ % bucket_count_ != bucket_) + if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_) + != bucket_) ptr_ = node_pointer(); return *this; } @@ -302,7 +306,8 @@ namespace boost { namespace unordered { namespace detail { boost::unordered::detail::buckets< typename Types::allocator, typename Types::bucket, - typename Types::node>, + typename Types::node, + typename Types::policy>, boost::unordered::detail::functions< typename Types::hasher, typename Types::key_equal> @@ -318,6 +323,7 @@ namespace boost { namespace unordered { namespace detail { typedef typename Types::value_type value_type; typedef typename Types::table table_impl; typedef typename Types::link_pointer link_pointer; + typedef typename Types::policy policy; typedef boost::unordered::detail::functions< typename Types::hasher, @@ -326,7 +332,8 @@ namespace boost { namespace unordered { namespace detail { typedef boost::unordered::detail::buckets< typename Types::allocator, typename Types::bucket, - typename Types::node> buckets; + typename Types::node, + typename Types::policy> buckets; typedef typename buckets::node_allocator node_allocator; typedef typename buckets::node_allocator_traits node_allocator_traits; @@ -338,9 +345,9 @@ namespace boost { namespace unordered { namespace detail { typedef boost::unordered::iterator_detail:: c_iterator c_iterator; typedef boost::unordered::iterator_detail:: - l_iterator l_iterator; + l_iterator l_iterator; typedef boost::unordered::iterator_detail:: - cl_iterator + cl_iterator cl_iterator; // Members @@ -395,7 +402,7 @@ namespace boost { namespace unordered { namespace detail { // Or from rehash post-condition: // count > size / mlf_ - return boost::unordered::detail::next_prime( + return this->new_bucket_count( boost::unordered::detail::double_to_size(floor( static_cast(size) / static_cast(mlf_))) + 1); @@ -408,7 +415,7 @@ namespace boost { namespace unordered { namespace detail { hasher const& hf, key_equal const& eq, node_allocator const& a) : - buckets(a, boost::unordered::detail::next_prime(num_buckets)), + buckets(a, this->new_bucket_count(num_buckets)), functions(hf, eq), mlf_(1.0f), max_load_(0) @@ -586,6 +593,11 @@ namespace boost { namespace unordered { namespace detail { return extractor::extract(x); } + std::size_t hash(key_type const& k) const + { + return this->apply_hash(this->hash_function(), k); + } + // Find Node template @@ -596,7 +608,7 @@ namespace boost { namespace unordered { namespace detail { { if (!this->size_) return node_pointer(); return static_cast(this)-> - find_node_impl(hash_function(k), k, eq); + find_node_impl(this->apply_hash(hash_function, k), k, eq); } node_pointer find_node( @@ -612,7 +624,7 @@ namespace boost { namespace unordered { namespace detail { { if (!this->size_) return node_pointer(); return static_cast(this)-> - find_node_impl(this->hash_function()(k), k, this->key_eq()); + find_node_impl(this->hash(k), k, this->key_eq()); } node_pointer find_matching_node(node_pointer n) const @@ -666,10 +678,10 @@ namespace boost { namespace unordered { namespace detail { if(!this->size_) { if(this->buckets_) this->delete_buckets(); - this->bucket_count_ = next_prime(min_buckets); + this->bucket_count_ = this->new_bucket_count(min_buckets); } else { - min_buckets = next_prime((std::max)(min_buckets, + min_buckets = this->new_bucket_count((std::max)(min_buckets, boost::unordered::detail::double_to_size(floor( static_cast(this->size_) / static_cast(mlf_))) + 1)); diff --git a/include/boost/unordered/detail/unique.hpp b/include/boost/unordered/detail/unique.hpp index 9c049f78..58ec3e31 100644 --- a/include/boost/unordered/detail/unique.hpp +++ b/include/boost/unordered/detail/unique.hpp @@ -132,6 +132,8 @@ namespace boost { namespace unordered { namespace detail { typedef boost::unordered::detail::table_impl table; typedef boost::unordered::detail::set_extractor extractor; + + typedef boost::unordered::detail::pick_policy::type policy; }; template @@ -156,6 +158,8 @@ namespace boost { namespace unordered { namespace detail { typedef boost::unordered::detail::table_impl table; typedef boost::unordered::detail::map_extractor extractor; + + typedef boost::unordered::detail::pick_policy::type policy; }; template @@ -217,7 +221,7 @@ namespace boost { namespace unordered { namespace detail { Key const& k, Pred const& eq) const { - std::size_t bucket_index = hash % this->bucket_count_; + std::size_t bucket_index = this->to_bucket(this->bucket_count_, hash); node_pointer n = this->get_start(bucket_index); for (;;) @@ -232,7 +236,7 @@ namespace boost { namespace unordered { namespace detail { } else { - if (node_hash % this->bucket_count_ != bucket_index) + if (this->to_bucket(this->bucket_count_, node_hash) != bucket_index) return node_pointer(); } @@ -298,16 +302,16 @@ namespace boost { namespace unordered { namespace detail { node_pointer n = a.release(); n->hash_ = hash; - bucket_pointer b = this->get_bucket(hash % this->bucket_count_); + bucket_pointer b = this->get_bucket(this->to_bucket(this->bucket_count_, hash)); if (!b->next_) { previous_pointer start_node = this->get_previous_start(); if (start_node->next_) { - this->get_bucket( - static_cast(start_node->next_)->hash_ % - this->bucket_count_)->next_ = n; + this->get_bucket(this->to_bucket(this->bucket_count_, + static_cast(start_node->next_)->hash_) + )->next_ = n; } b->next_ = start_node; @@ -328,7 +332,7 @@ namespace boost { namespace unordered { namespace detail { { typedef typename value_type::second_type mapped_type; - std::size_t hash = this->hash_function()(k); + std::size_t hash = this->hash(k); node_pointer pos = this->find_node(hash, k); if (pos) return pos->value(); @@ -389,7 +393,7 @@ namespace boost { namespace unordered { namespace detail { emplace_return emplace_impl(key_type const& k, BOOST_UNORDERED_EMPLACE_ARGS) { - std::size_t hash = this->hash_function()(k); + std::size_t hash = this->hash(k); node_pointer pos = this->find_node(hash, k); if (pos) return emplace_return(iterator(pos), false); @@ -409,7 +413,7 @@ namespace boost { namespace unordered { namespace detail { emplace_return emplace_impl_with_node(node_constructor& a) { key_type const& k = this->get_key(a.value()); - std::size_t hash = this->hash_function()(k); + std::size_t hash = this->hash(k); node_pointer pos = this->find_node(hash, k); if (pos) return emplace_return(iterator(pos), false); @@ -473,7 +477,7 @@ namespace boost { namespace unordered { namespace detail { void insert_range_empty(node_constructor& a, key_type const& k, InputIt i, InputIt j) { - std::size_t hash = this->hash_function()(k); + std::size_t hash = this->hash(k); a.construct_node(); a.construct_value2(*i); this->reserve_for_insert(this->size_ + @@ -486,7 +490,7 @@ namespace boost { namespace unordered { namespace detail { InputIt i, InputIt j) { // No side effects in this initial code - std::size_t hash = this->hash_function()(k); + std::size_t hash = this->hash(k); node_pointer pos = this->find_node(hash, k); if (!pos) { @@ -523,8 +527,8 @@ namespace boost { namespace unordered { namespace detail { { if(!this->size_) return 0; - std::size_t hash = this->hash_function()(k); - std::size_t bucket_index = hash % this->bucket_count_; + std::size_t hash = this->hash(k); + std::size_t bucket_index = this->to_bucket(this->bucket_count_, hash); bucket_pointer bucket = this->get_bucket(bucket_index); previous_pointer prev = bucket->next_; @@ -535,7 +539,7 @@ namespace boost { namespace unordered { namespace detail { if (!prev->next_) return 0; std::size_t node_hash = static_cast(prev->next_)->hash_; - if (node_hash % this->bucket_count_ != bucket_index) + if (this->to_bucket(this->bucket_count_, node_hash) != bucket_index) return 0; if (node_hash == hash && this->key_eq()(k, this->get_key( @@ -557,7 +561,7 @@ namespace boost { namespace unordered { namespace detail { node_pointer next = static_cast(r->next_); bucket_pointer bucket = this->get_bucket( - r->hash_ % this->bucket_count_); + this->to_bucket(this->bucket_count_, r->hash_)); previous_pointer prev = unlink_node(*bucket, r); this->fix_buckets(bucket, prev, next); @@ -571,7 +575,7 @@ namespace boost { namespace unordered { namespace detail { { if (r1 == r2) return r2; - std::size_t bucket_index = r1->hash_ % this->bucket_count_; + std::size_t bucket_index = this->to_bucket(this->bucket_count_, r1->hash_); previous_pointer prev = unlink_nodes( *this->get_bucket(bucket_index), r1, r2); this->fix_buckets_range(bucket_index, prev, r1, r2); @@ -689,7 +693,8 @@ namespace boost { namespace unordered { namespace detail { previous_pointer prev) { node_pointer n = static_cast(prev->next_); - bucket_pointer b = dst.get_bucket(n->hash_ % dst.bucket_count_); + bucket_pointer b = dst.get_bucket(dst.to_bucket(dst.bucket_count_, + n->hash_)); if (!b->next_) { b->next_ = prev; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 1a7ff151..c9d4ac44 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -465,7 +465,8 @@ namespace unordered size_type bucket(const key_type& k) const { - return table_.hash_function()(k) % table_.bucket_count_; + return table_.to_bucket(table_.bucket_count_, + table_.hash(k)); } local_iterator begin(size_type n) @@ -946,7 +947,8 @@ namespace unordered size_type bucket(const key_type& k) const { - return table_.hash_function()(k) % table_.bucket_count_; + return table_.to_bucket(table_.bucket_count_, + table_.hash(k)); } local_iterator begin(size_type n) diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 384769dd..19138b74 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -450,7 +450,8 @@ namespace unordered size_type bucket(const key_type& k) const { - return table_.hash_function()(k) % table_.bucket_count_; + return table_.to_bucket(table_.bucket_count_, + table_.hash(k)); } local_iterator begin(size_type n) @@ -921,7 +922,8 @@ namespace unordered size_type bucket(const key_type& k) const { - return table_.hash_function()(k) % table_.bucket_count_; + return table_.to_bucket(table_.bucket_count_, + table_.hash(k)); } local_iterator begin(size_type n)