diff --git a/libcxx/benchmarks/ContainerBenchmarks.h b/libcxx/benchmarks/ContainerBenchmarks.h --- a/libcxx/benchmarks/ContainerBenchmarks.h +++ b/libcxx/benchmarks/ContainerBenchmarks.h @@ -135,6 +135,20 @@ } } +template +static void BM_Rehash(benchmark::State& st, Container c, GenInputs gen) { + auto in = gen(st.range(0)); + c.max_load_factor(3.0); + c.insert(in.begin(), in.end()); + benchmark::DoNotOptimize(c); + const auto bucket_count = c.bucket_count(); + while (st.KeepRunning()) { + c.rehash(bucket_count + 1); + c.rehash(bucket_count); + benchmark::ClobberMemory(); + } +} + } // end namespace ContainerBenchmarks #endif // BENCHMARK_CONTAINER_BENCHMARKS_H diff --git a/libcxx/benchmarks/unordered_set_operations.bench.cpp b/libcxx/benchmarks/unordered_set_operations.bench.cpp --- a/libcxx/benchmarks/unordered_set_operations.bench.cpp +++ b/libcxx/benchmarks/unordered_set_operations.bench.cpp @@ -104,6 +104,27 @@ } }; +// The sole purpose of this comparator is to be used in BM_Rehash, where +// we need something slow enough to be easily noticable in benchmark results. +// The default implementation of operator== for strings seems to be a little +// too fast for that specific benchmark to reliably show a noticeable +// improvement, but unoptimized bytewise comparison fits just right. +// Early return is there just for convenience, since we only compare strings +// of equal length in BM_Rehash. +struct SlowStringEq { + SlowStringEq() = default; + inline TEST_ALWAYS_INLINE + bool operator()(const std::string& lhs, const std::string& rhs) const { + if (lhs.size() != rhs.size()) return false; + + bool eq = true; + for (size_t i = 0; i < lhs.size(); ++i) { + eq &= lhs[i] == rhs[i]; + } + return eq; + } +}; + //----------------------------------------------------------------------------// // BM_Hash // ---------------------------------------------------------------------------// @@ -266,6 +287,20 @@ std::unordered_set{}, getRandomStringInputs)->Arg(TestNumInputs); +//----------------------------------------------------------------------------// +// BM_Rehash +// ---------------------------------------------------------------------------// + +BENCHMARK_CAPTURE(BM_Rehash, + unordered_set_string_arg, + std::unordered_set, SlowStringEq>{}, + getRandomStringInputs)->Arg(TestNumInputs); + +BENCHMARK_CAPTURE(BM_Rehash, + unordered_set_int_arg, + std::unordered_set{}, + getRandomIntegerInputs)->Arg(TestNumInputs); + /////////////////////////////////////////////////////////////////////////////// BENCHMARK_CAPTURE(BM_InsertDuplicate, unordered_set_int, diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table --- a/libcxx/include/__hash_table +++ b/libcxx/include/__hash_table @@ -1152,9 +1152,16 @@ #endif void clear() _NOEXCEPT; - void rehash(size_type __n); - _LIBCPP_INLINE_VISIBILITY void reserve(size_type __n) - {rehash(static_cast(ceil(__n / max_load_factor())));} + _LIBCPP_INLINE_VISIBILITY void __rehash_unique(size_type __n) { __rehash(__n); } + _LIBCPP_INLINE_VISIBILITY void __rehash_multi(size_type __n) { __rehash(__n); } + _LIBCPP_INLINE_VISIBILITY void __reserve_unique(size_type __n) + { + __rehash_unique(static_cast(ceil(__n / max_load_factor()))); + } + _LIBCPP_INLINE_VISIBILITY void __reserve_multi(size_type __n) + { + __rehash_multi(static_cast(ceil(__n / max_load_factor()))); + } _LIBCPP_INLINE_VISIBILITY size_type bucket_count() const _NOEXCEPT @@ -1291,7 +1298,8 @@ #endif // _LIBCPP_ENABLE_DEBUG_MODE private: - void __rehash(size_type __n); + template void __rehash(size_type __n); + template void __do_rehash(size_type __n); template __node_holder __construct_node(_Args&& ...__args); @@ -1796,7 +1804,7 @@ } if (size()+1 > __bc * max_load_factor() || __bc == 0) { - rehash(_VSTD::max(2 * __bc + !__is_hash_power2(__bc), + __rehash_unique(_VSTD::max(2 * __bc + !__is_hash_power2(__bc), size_type(ceil(float(size() + 1) / max_load_factor())))); } return nullptr; @@ -1868,7 +1876,7 @@ size_type __bc = bucket_count(); if (size()+1 > __bc * max_load_factor() || __bc == 0) { - rehash(_VSTD::max(2 * __bc + !__is_hash_power2(__bc), + __rehash_multi(_VSTD::max(2 * __bc + !__is_hash_power2(__bc), size_type(ceil(float(size() + 1) / max_load_factor())))); __bc = bucket_count(); } @@ -1962,7 +1970,7 @@ size_type __bc = bucket_count(); if (size()+1 > __bc * max_load_factor() || __bc == 0) { - rehash(_VSTD::max(2 * __bc + !__is_hash_power2(__bc), + __rehash_multi(_VSTD::max(2 * __bc + !__is_hash_power2(__bc), size_type(ceil(float(size() + 1) / max_load_factor())))); __bc = bucket_count(); } @@ -2010,7 +2018,7 @@ __node_holder __h = __construct_node_hash(__hash, _VSTD::forward<_Args>(__args)...); if (size()+1 > __bc * max_load_factor() || __bc == 0) { - rehash(_VSTD::max(2 * __bc + !__is_hash_power2(__bc), + __rehash_unique(_VSTD::max(2 * __bc + !__is_hash_power2(__bc), size_type(ceil(float(size() + 1) / max_load_factor())))); __bc = bucket_count(); __chash = __constrain_hash(__hash, __bc); @@ -2213,8 +2221,9 @@ #endif // _LIBCPP_STD_VER > 14 template +template void -__hash_table<_Tp, _Hash, _Equal, _Alloc>::rehash(size_type __n) +__hash_table<_Tp, _Hash, _Equal, _Alloc>::__rehash(size_type __n) _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK { if (__n == 1) @@ -2223,7 +2232,7 @@ __n = __next_prime(__n); size_type __bc = bucket_count(); if (__n > __bc) - __rehash(__n); + __do_rehash<_UniqueKeys>(__n); else if (__n < __bc) { __n = _VSTD::max @@ -2233,13 +2242,14 @@ __next_prime(size_t(ceil(float(size()) / max_load_factor()))) ); if (__n < __bc) - __rehash(__n); + __do_rehash<_UniqueKeys>(__n); } } template +template void -__hash_table<_Tp, _Hash, _Equal, _Alloc>::__rehash(size_type __nbc) +__hash_table<_Tp, _Hash, _Equal, _Alloc>::__do_rehash(size_type __nbc) { std::__debug_db_invalidate_all(this); __pointer_allocator& __npa = __bucket_list_.get_deleter().__alloc(); @@ -2274,11 +2284,14 @@ else { __next_pointer __np = __cp; - for (; __np->__next_ != nullptr && - key_eq()(__cp->__upcast()->__value_, - __np->__next_->__upcast()->__value_); - __np = __np->__next_) - ; + if _LIBCPP_CONSTEXPR_AFTER_CXX14 (!_UniqueKeys) + { + for (; __np->__next_ != nullptr && + key_eq()(__cp->__upcast()->__value_, + __np->__next_->__upcast()->__value_); + __np = __np->__next_) + ; + } __pp->__next_ = __np->__next_; __np->__next_ = __bucket_list_[__chash]->__next_; __bucket_list_[__chash]->__next_ = __cp; diff --git a/libcxx/include/ext/hash_map b/libcxx/include/ext/hash_map --- a/libcxx/include/ext/hash_map +++ b/libcxx/include/ext/hash_map @@ -605,7 +605,7 @@ {return __table_.bucket_size(__n);} _LIBCPP_INLINE_VISIBILITY - void resize(size_type __n) {__table_.rehash(__n);} + void resize(size_type __n) {__table_.__rehash_unique(__n);} private: __node_holder __construct_node(const key_type& __k); @@ -616,7 +616,7 @@ size_type __n, const hasher& __hf, const key_equal& __eql) : __table_(__hf, __eql) { - __table_.rehash(__n); + __table_.__rehash_unique(__n); } template @@ -625,7 +625,7 @@ const allocator_type& __a) : __table_(__hf, __eql, __a) { - __table_.rehash(__n); + __table_.__rehash_unique(__n); } template @@ -643,7 +643,7 @@ const hasher& __hf, const key_equal& __eql) : __table_(__hf, __eql) { - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__first, __last); } @@ -654,7 +654,7 @@ const hasher& __hf, const key_equal& __eql, const allocator_type& __a) : __table_(__hf, __eql, __a) { - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__first, __last); } @@ -663,7 +663,7 @@ const hash_map& __u) : __table_(__u.__table_) { - __table_.rehash(__u.bucket_count()); + __table_.__rehash_unique(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -874,7 +874,7 @@ {return __table_.bucket_size(__n);} _LIBCPP_INLINE_VISIBILITY - void resize(size_type __n) {__table_.rehash(__n);} + void resize(size_type __n) {__table_.__rehash_multi(__n);} }; template @@ -882,7 +882,7 @@ size_type __n, const hasher& __hf, const key_equal& __eql) : __table_(__hf, __eql) { - __table_.rehash(__n); + __table_.__rehash_multi(__n); } template @@ -891,7 +891,7 @@ const allocator_type& __a) : __table_(__hf, __eql, __a) { - __table_.rehash(__n); + __table_.__rehash_multi(__n); } template @@ -909,7 +909,7 @@ const hasher& __hf, const key_equal& __eql) : __table_(__hf, __eql) { - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__first, __last); } @@ -920,7 +920,7 @@ const hasher& __hf, const key_equal& __eql, const allocator_type& __a) : __table_(__hf, __eql, __a) { - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__first, __last); } @@ -929,7 +929,7 @@ const hash_multimap& __u) : __table_(__u.__table_) { - __table_.rehash(__u.bucket_count()); + __table_.__rehash_multi(__u.bucket_count()); insert(__u.begin(), __u.end()); } diff --git a/libcxx/include/ext/hash_set b/libcxx/include/ext/hash_set --- a/libcxx/include/ext/hash_set +++ b/libcxx/include/ext/hash_set @@ -333,7 +333,7 @@ size_type elems_in_bucket(size_type __n) const {return __table_.bucket_size(__n);} _LIBCPP_INLINE_VISIBILITY - void resize(size_type __n) {__table_.rehash(__n);} + void resize(size_type __n) {__table_.__rehash_unique(__n);} }; template @@ -341,7 +341,7 @@ const hasher& __hf, const key_equal& __eql) : __table_(__hf, __eql) { - __table_.rehash(__n); + __table_.__rehash_unique(__n); } template @@ -349,7 +349,7 @@ const hasher& __hf, const key_equal& __eql, const allocator_type& __a) : __table_(__hf, __eql, __a) { - __table_.rehash(__n); + __table_.__rehash_unique(__n); } template @@ -367,7 +367,7 @@ const hasher& __hf, const key_equal& __eql) : __table_(__hf, __eql) { - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__first, __last); } @@ -378,7 +378,7 @@ const hasher& __hf, const key_equal& __eql, const allocator_type& __a) : __table_(__hf, __eql, __a) { - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__first, __last); } @@ -387,7 +387,7 @@ const hash_set& __u) : __table_(__u.__table_) { - __table_.rehash(__u.bucket_count()); + __table_.__rehash_unique(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -553,7 +553,7 @@ size_type elems_in_bucket(size_type __n) const {return __table_.bucket_size(__n);} _LIBCPP_INLINE_VISIBILITY - void resize(size_type __n) {__table_.rehash(__n);} + void resize(size_type __n) {__table_.__rehash_multi(__n);} }; template @@ -561,7 +561,7 @@ size_type __n, const hasher& __hf, const key_equal& __eql) : __table_(__hf, __eql) { - __table_.rehash(__n); + __table_.__rehash_multi(__n); } template @@ -570,7 +570,7 @@ const allocator_type& __a) : __table_(__hf, __eql, __a) { - __table_.rehash(__n); + __table_.__rehash_multi(__n); } template @@ -588,7 +588,7 @@ const hasher& __hf, const key_equal& __eql) : __table_(__hf, __eql) { - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__first, __last); } @@ -599,7 +599,7 @@ const hasher& __hf, const key_equal& __eql, const allocator_type& __a) : __table_(__hf, __eql, __a) { - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__first, __last); } @@ -608,7 +608,7 @@ const hash_multiset& __u) : __table_(__u.__table_) { - __table_.rehash(__u.bucket_count()); + __table_.__rehash_multi(__u.bucket_count()); insert(__u.begin(), __u.end()); } diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map --- a/libcxx/include/unordered_map +++ b/libcxx/include/unordered_map @@ -1527,9 +1527,9 @@ _LIBCPP_INLINE_VISIBILITY void max_load_factor(float __mlf) {__table_.max_load_factor(__mlf);} _LIBCPP_INLINE_VISIBILITY - void rehash(size_type __n) {__table_.rehash(__n);} + void rehash(size_type __n) {__table_.__rehash_unique(__n);} _LIBCPP_INLINE_VISIBILITY - void reserve(size_type __n) {__table_.reserve(__n);} + void reserve(size_type __n) {__table_.__reserve_unique(__n);} #ifdef _LIBCPP_ENABLE_DEBUG_MODE @@ -1628,7 +1628,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); } template @@ -1638,7 +1638,7 @@ : __table_(__hf, __eql, typename __table::allocator_type(__a)) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); } template @@ -1667,7 +1667,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__first, __last); } @@ -1679,7 +1679,7 @@ : __table_(__hf, __eql, typename __table::allocator_type(__a)) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__first, __last); } @@ -1689,7 +1689,7 @@ : __table_(__u.__table_) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__u.bucket_count()); + __table_.__rehash_unique(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -1699,7 +1699,7 @@ : __table_(__u.__table_, typename __table::allocator_type(__a)) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__u.bucket_count()); + __table_.__rehash_unique(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -1749,7 +1749,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__il.begin(), __il.end()); } @@ -1760,7 +1760,7 @@ : __table_(__hf, __eql, typename __table::allocator_type(__a)) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__il.begin(), __il.end()); } @@ -2303,9 +2303,9 @@ _LIBCPP_INLINE_VISIBILITY void max_load_factor(float __mlf) {__table_.max_load_factor(__mlf);} _LIBCPP_INLINE_VISIBILITY - void rehash(size_type __n) {__table_.rehash(__n);} + void rehash(size_type __n) {__table_.__rehash_multi(__n);} _LIBCPP_INLINE_VISIBILITY - void reserve(size_type __n) {__table_.reserve(__n);} + void reserve(size_type __n) {__table_.__reserve_multi(__n);} #ifdef _LIBCPP_ENABLE_DEBUG_MODE @@ -2400,7 +2400,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); } template @@ -2410,7 +2410,7 @@ : __table_(__hf, __eql, typename __table::allocator_type(__a)) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); } template @@ -2430,7 +2430,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__first, __last); } @@ -2442,7 +2442,7 @@ : __table_(__hf, __eql, typename __table::allocator_type(__a)) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__first, __last); } @@ -2461,7 +2461,7 @@ : __table_(__u.__table_) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__u.bucket_count()); + __table_.__rehash_multi(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -2471,7 +2471,7 @@ : __table_(__u.__table_, typename __table::allocator_type(__a)) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__u.bucket_count()); + __table_.__rehash_multi(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -2522,7 +2522,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__il.begin(), __il.end()); } @@ -2533,7 +2533,7 @@ : __table_(__hf, __eql, typename __table::allocator_type(__a)) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__il.begin(), __il.end()); } diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set --- a/libcxx/include/unordered_set +++ b/libcxx/include/unordered_set @@ -858,9 +858,9 @@ _LIBCPP_INLINE_VISIBILITY void max_load_factor(float __mlf) {__table_.max_load_factor(__mlf);} _LIBCPP_INLINE_VISIBILITY - void rehash(size_type __n) {__table_.rehash(__n);} + void rehash(size_type __n) {__table_.__rehash_unique(__n);} _LIBCPP_INLINE_VISIBILITY - void reserve(size_type __n) {__table_.reserve(__n);} + void reserve(size_type __n) {__table_.__reserve_unique(__n);} #ifdef _LIBCPP_ENABLE_DEBUG_MODE @@ -942,7 +942,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); } template @@ -951,7 +951,7 @@ : __table_(__hf, __eql, __a) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); } template @@ -971,7 +971,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__first, __last); } @@ -983,7 +983,7 @@ : __table_(__hf, __eql, __a) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__first, __last); } @@ -1002,7 +1002,7 @@ : __table_(__u.__table_) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__u.bucket_count()); + __table_.__rehash_unique(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -1012,7 +1012,7 @@ : __table_(__u.__table_, __a) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__u.bucket_count()); + __table_.__rehash_unique(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -1060,7 +1060,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__il.begin(), __il.end()); } @@ -1071,7 +1071,7 @@ : __table_(__hf, __eql, __a) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_unique(__n); insert(__il.begin(), __il.end()); } @@ -1496,9 +1496,9 @@ _LIBCPP_INLINE_VISIBILITY void max_load_factor(float __mlf) {__table_.max_load_factor(__mlf);} _LIBCPP_INLINE_VISIBILITY - void rehash(size_type __n) {__table_.rehash(__n);} + void rehash(size_type __n) {__table_.__rehash_multi(__n);} _LIBCPP_INLINE_VISIBILITY - void reserve(size_type __n) {__table_.reserve(__n);} + void reserve(size_type __n) {__table_.__reserve_multi(__n);} #ifdef _LIBCPP_ENABLE_DEBUG_MODE @@ -1578,7 +1578,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); } template @@ -1588,7 +1588,7 @@ : __table_(__hf, __eql, __a) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); } template @@ -1608,7 +1608,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__first, __last); } @@ -1620,7 +1620,7 @@ : __table_(__hf, __eql, __a) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__first, __last); } @@ -1639,7 +1639,7 @@ : __table_(__u.__table_) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__u.bucket_count()); + __table_.__rehash_multi(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -1649,7 +1649,7 @@ : __table_(__u.__table_, __a) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__u.bucket_count()); + __table_.__rehash_multi(__u.bucket_count()); insert(__u.begin(), __u.end()); } @@ -1697,7 +1697,7 @@ : __table_(__hf, __eql) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__il.begin(), __il.end()); } @@ -1708,7 +1708,7 @@ : __table_(__hf, __eql, __a) { _VSTD::__debug_db_insert_c(this); - __table_.rehash(__n); + __table_.__rehash_multi(__n); insert(__il.begin(), __il.end()); }