Add experimental fastmod_buckets option

This commit is contained in:
Ion Gaztañaga
2022-06-13 00:42:58 +02:00
parent 466ff07d15
commit 05bb58091e
6 changed files with 702 additions and 371 deletions

View File

@ -201,16 +201,12 @@ class hashtable_iterator
BOOST_INTRUSIVE_FORCEINLINE const value_traits &priv_value_traits() const
{ return traitsptr_->priv_value_traits(); }
BOOST_INTRUSIVE_FORCEINLINE const bucket_traits &priv_bucket_traits() const
{ return traitsptr_->priv_bucket_traits(); }
private:
void increment()
{
const bucket_traits &rbuck_traits = this->priv_bucket_traits();
bucket_type* const buckets = boost::movelib::to_raw_pointer(rbuck_traits.bucket_begin());
const std::size_t buckets_len = rbuck_traits.bucket_count();
bucket_type* const buckets = boost::movelib::to_raw_pointer(traitsptr_->priv_bucket_traits().bucket_begin());
const std::size_t buckets_len = traitsptr_->priv_bucket_traits().bucket_count();
++slist_it_;
const slist_node_ptr n = slist_it_.pointed_node();
@ -334,7 +330,7 @@ class hashtable_iterator<BucketValueTraits, true, IsConst>
{ return i.slist_it_ == i2.slist_it_; }
BOOST_INTRUSIVE_FORCEINLINE friend bool operator!= (const hashtable_iterator& i, const hashtable_iterator& i2)
{ return !(i == i2); }
{ return i.slist_it_ != i2.slist_it_; }
BOOST_INTRUSIVE_FORCEINLINE reference operator*() const
{ return *this->operator ->(); }

File diff suppressed because it is too large Load Diff

View File

@ -212,13 +212,22 @@ BOOST_INTRUSIVE_OPTION_CONSTANT(store_hash, bool, Enabled, store_hash)
//!with the same key.
BOOST_INTRUSIVE_OPTION_CONSTANT(optimize_multikey, bool, Enabled, optimize_multikey)
//!This option setter specifies if the bucket array will be always power of two.
//!This option setter specifies if the length of the bucket array provided by
//!the user will always be power of two.
//!This allows using masks instead of the default modulo operation to determine
//!the bucket number from the hash value, leading to better performance.
//!In debug mode, if power of two buckets mode is activated, the bucket length
//!will be checked with assertions.
//!In debug mode, the provided bucket array length will be checked with assertions.
BOOST_INTRUSIVE_OPTION_CONSTANT(power_2_buckets, bool, Enabled, power_2_buckets)
//!WARNING: this option is EXPERIMENTAL, don't use it in production code
//!This option setter specifies if the length of the bucket array provided by
//!the user will always be a value specified by the
//!suggested_upper|lower_bucket_count call. This allows the use of some
//!precomputed values and speeds hash to bucket index operations, leading
//!to better performance.
//!In debug mode, the provided bucket array length will be checked with assertions.
BOOST_INTRUSIVE_OPTION_CONSTANT(fastmod_buckets, bool, Enabled, fastmod_buckets)
//!This option setter specifies if the container will cache a pointer to the first
//!non-empty bucket so that begin() is always constant-time.
//!This is specially helpful when we can have containers with a few elements

View File

@ -456,6 +456,7 @@ struct make_unordered_set
| (std::size_t(packed_options::compare_hash)*hash_bool_flags::compare_hash_pos)
| (std::size_t(packed_options::incremental)*hash_bool_flags::incremental_pos)
| (std::size_t(packed_options::linear_buckets)*hash_bool_flags::linear_buckets_pos)
| (std::size_t(packed_options::fastmod_buckets)*hash_bool_flags::fastmod_buckets_pos)
> implementation_defined;
/// @endcond
@ -910,6 +911,7 @@ struct make_unordered_multiset
| (std::size_t(packed_options::compare_hash)*hash_bool_flags::compare_hash_pos)
| (std::size_t(packed_options::incremental)*hash_bool_flags::incremental_pos)
| (std::size_t(packed_options::linear_buckets)*hash_bool_flags::linear_buckets_pos)
| (std::size_t(packed_options::fastmod_buckets)*hash_bool_flags::fastmod_buckets_pos)
> implementation_defined;
/// @endcond

View File

@ -243,24 +243,27 @@ void test_common_unordered_and_associative_container(Container & c, Data & d, bo
//If size_type is big enough the upper bound is returned
BOOST_IF_CONSTEXPR(sizeof(size_type) < sizeof(std::size_t)){
sz = Container::suggested_upper_bucket_count(size_type(-1)/2);
BOOST_TEST( sz >= size_type(-1)/2 );
BOOST_TEST( sz > size_type(-1)/2 );
}
sz = Container::suggested_upper_bucket_count(size_type(-1)/4);
BOOST_TEST( sz >= size_type(-1)/4 );
BOOST_TEST( sz > size_type(-1)/4 );
sz = Container::suggested_upper_bucket_count(size_type(-1) / 8);
BOOST_TEST(sz >= size_type(-1) / 8);
BOOST_TEST(sz > size_type(-1) / 8);
sz = Container::suggested_upper_bucket_count(0);
BOOST_TEST( sz > 0 );
//
//suggested_lower_bucket_count
//
sz = Container::suggested_lower_bucket_count(size_type(-1));
BOOST_TEST( sz <= size_type(-1) );
//If size_type is big enough the lower bound is returned
BOOST_IF_CONSTEXPR(sizeof(size_type) < sizeof(std::size_t)) {
sz = Container::suggested_lower_bucket_count(size_type(-1) / 2);
BOOST_TEST(sz >= size_type(-1) / 2);
}
//In the rest of cases the lower bound is returned
sz = Container::suggested_lower_bucket_count(size_type(-1)/2);
BOOST_TEST( sz <= size_type(-1)/2 );
sz = Container::suggested_lower_bucket_count(size_type(-1)/4);
BOOST_TEST( sz <= size_type(-1)/4 );
BOOST_TEST( sz >= size_type(-1)/4 );
sz = Container::suggested_lower_bucket_count(size_type(-1) / 8);
BOOST_TEST(sz >= size_type(-1) / 8);
//Minimum fallbacks to the lowest possible value
sz = Container::suggested_upper_bucket_count(0);
BOOST_TEST( sz > 0 );

View File

@ -28,7 +28,7 @@
using namespace boost::intrusive;
template < class ValueTraits, bool ConstantTimeSize, bool CacheBegin, bool CompareHash
, bool Incremental, bool Map, bool DefaultHolder, bool LinearBuckets>
, bool Incremental, bool Map, bool DefaultHolder, bool LinearBuckets, bool FastMod>
struct rebinder
{
typedef unordered_rebinder_common<ValueTraits, DefaultHolder, Map> common_t;
@ -47,6 +47,7 @@ struct rebinder
, compare_hash<CompareHash>
, incremental<Incremental>
, linear_buckets<LinearBuckets>
// , fastmod_buckets<FastMod>
, typename common_t::holder_opt
, typename common_t::key_of_value_opt
, Option1
@ -90,7 +91,8 @@ class test_main_template<VoidPointer, ConstantTimeSize, DefaultHolder, Map, Base
test::test_unordered
< //cache_begin, compare_hash, incremental
rebinder< base_hook_t, ConstantTimeSize, ConstantTimeSize
, !ConstantTimeSize, !!ConstantTimeSize, Map, DefaultHolder, LinearBuckets>
, !ConstantTimeSize, !!ConstantTimeSize, Map, DefaultHolder
, LinearBuckets, LinearBuckets && !ConstantTimeSize>
>::test_all(data);
}
};
@ -118,7 +120,8 @@ class test_main_template<VoidPointer, ConstantTimeSize, DefaultHolder, Map, Memb
test::test_unordered
< //cache_begin, compare_hash, incremental
rebinder <member_hook_t, ConstantTimeSize, false
, !ConstantTimeSize, false, !ConstantTimeSize, DefaultHolder, LinearBuckets>
, !ConstantTimeSize, false, !ConstantTimeSize, DefaultHolder
, LinearBuckets, LinearBuckets && !ConstantTimeSize>
>::test_all(data);
}
};
@ -142,7 +145,8 @@ class test_main_template<VoidPointer, ConstantTimeSize, DefaultHolder, Map, NonM
test::test_unordered
< //cache_begin, compare_hash, incremental
rebinder< typename testval_traits_t::nonhook_value_traits
, ConstantTimeSize, false, false, false, Map, DefaultHolder, LinearBuckets>
, ConstantTimeSize, false, false, false, Map, DefaultHolder
, LinearBuckets, LinearBuckets && !ConstantTimeSize>
>::test_all(data);
}
};
@ -160,6 +164,7 @@ int main()
test_main_template<void*, false, true, false, Member, true>::execute();
test_main_template<void*, true, false, false, NonMember, true>::execute();
test_main_template<void*, true, true, false, Base, true>::execute();
test_main_template<void*, false, true, false, Base, true>::execute();
//smart_ptr
test_main_template<smart_ptr<void>, false, false, false, Member, false>::execute();