Skip to content

Commit 654bc27

Browse files
committed
1 parent 7301b8e commit 654bc27

File tree

2 files changed

+50
-25
lines changed

2 files changed

+50
-25
lines changed

include/gtl/phmap.hpp

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -819,11 +819,15 @@ static_assert(kDeleted == -2,
819819
// A single block of empty control bytes for tables without any slots allocated.
820820
// This enables removing a branch in the hot path of find().
821821
// --------------------------------------------------------------------------
822+
template <class std_alloc_t>
822823
inline ctrl_t* EmptyGroup() {
823-
alignas(16) static constexpr ctrl_t empty_group[] = { kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,
824-
kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,
825-
kEmpty, kEmpty, kEmpty, kEmpty };
826-
return const_cast<ctrl_t*>(empty_group);
824+
if constexpr (std_alloc_t::value) {
825+
alignas(16) static constexpr ctrl_t empty_group[] = { kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,
826+
kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,
827+
kEmpty, kEmpty, kEmpty, kEmpty };
828+
return const_cast<ctrl_t*>(empty_group);
829+
}
830+
return nullptr;
827831
}
828832

829833
// --------------------------------------------------------------------------
@@ -1529,6 +1533,8 @@ class raw_hash_set {
15291533
template<class K>
15301534
using key_arg = typename KeyArgImpl::template type<K, key_type>;
15311535

1536+
using std_alloc_t = std::is_same<typename std::decay<Alloc>::type, gtl::priv::Allocator<value_type>>;
1537+
15321538
private:
15331539
// Give an early error when key_type is not hashable/eq.
15341540
auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k));
@@ -1635,6 +1641,11 @@ class raw_hash_set {
16351641
, slot_(slot) {}
16361642

16371643
void skip_empty_or_deleted() {
1644+
if constexpr (!std_alloc_t::value) {
1645+
// ctrl_ could be nullptr
1646+
if (!ctrl_)
1647+
return;
1648+
}
16381649
while (IsEmptyOrDeleted(*ctrl_)) {
16391650
// ctrl is not necessarily aligned to Group::kWidth. It is also likely
16401651
// to read past the space for ctrl bytes and into slots. This is ok
@@ -1701,7 +1712,7 @@ class raw_hash_set {
17011712
const hasher& hashfn = hasher(),
17021713
const key_equal& eq = key_equal(),
17031714
const allocator_type& alloc = allocator_type())
1704-
: ctrl_(EmptyGroup())
1715+
: ctrl_(EmptyGroup<std_alloc_t>())
17051716
, settings_(0, hashfn, eq, alloc) {
17061717
if (bucket_cnt) {
17071718
size_t new_capacity = NormalizeCapacity(bucket_cnt);
@@ -1824,7 +1835,7 @@ class raw_hash_set {
18241835
raw_hash_set(raw_hash_set&& that) noexcept(std::is_nothrow_copy_constructible_v<hasher> &&
18251836
std::is_nothrow_copy_constructible_v<key_equal> &&
18261837
std::is_nothrow_copy_constructible_v<allocator_type>)
1827-
: ctrl_(std::exchange(that.ctrl_, EmptyGroup()))
1838+
: ctrl_(std::exchange(that.ctrl_, EmptyGroup<std_alloc_t>()))
18281839
, slots_(std::exchange(that.slots_, nullptr))
18291840
, size_(std::exchange(that.size_, 0))
18301841
, capacity_(std::exchange(that.capacity_, 0))
@@ -1839,7 +1850,7 @@ class raw_hash_set {
18391850
}
18401851

18411852
raw_hash_set(raw_hash_set&& that, const allocator_type& a)
1842-
: ctrl_(EmptyGroup())
1853+
: ctrl_(EmptyGroup<std_alloc_t>())
18431854
, slots_(nullptr)
18441855
, size_(0)
18451856
, capacity_(0)
@@ -2366,22 +2377,13 @@ class raw_hash_set {
23662377
// NOTE: This is a very low level operation and should not be used without
23672378
// specific benchmarks indicating its importance.
23682379
// -----------------------------------------------------------------------
2369-
void prefetch_hash(size_t hashval) const {
2370-
(void)hashval;
2371-
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
2372-
auto seq = probe(hashval);
2373-
_mm_prefetch((const char*)(ctrl_ + seq.offset()), _MM_HINT_NTA);
2374-
_mm_prefetch((const char*)(slots_ + seq.offset()), _MM_HINT_NTA);
2375-
#elif defined(__GNUC__)
2376-
auto seq = probe(hashval);
2377-
__builtin_prefetch(static_cast<const void*>(ctrl_ + seq.offset()));
2378-
__builtin_prefetch(static_cast<const void*>(slots_ + seq.offset()));
2379-
#endif // __GNUC__
2380+
void prefetch_hash(size_t) const {
23802381
}
23812382

23822383
template<class K = key_type>
23832384
void prefetch(const key_arg<K>& key) const {
2384-
prefetch_hash(this->hash(key));
2385+
if constexpr (std_alloc_t::value)
2386+
prefetch_hash(this->hash(key));
23852387
}
23862388

23872389
// The API of find() has two extensions.
@@ -2490,6 +2492,11 @@ class raw_hash_set {
24902492

24912493
template<class K = key_type>
24922494
bool find_impl(const key_arg<K>& key, size_t hashval, size_t& offset) {
2495+
if constexpr (!std_alloc_t::value) {
2496+
// ctrl_ could be nullptr
2497+
if (!ctrl_)
2498+
return false;
2499+
}
24932500
auto seq = probe(hashval);
24942501
while (true) {
24952502
Group g{ ctrl_ + seq.offset() };
@@ -2652,7 +2659,7 @@ class raw_hash_set {
26522659
// Unpoison before returning the memory to the allocator.
26532660
SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
26542661
Deallocate<Layout::Alignment()>(&alloc_ref(), ctrl_, layout.AllocSize());
2655-
ctrl_ = EmptyGroup();
2662+
ctrl_ = EmptyGroup<std_alloc_t>();
26562663
slots_ = nullptr;
26572664
size_ = 0;
26582665
capacity_ = 0;
@@ -2761,6 +2768,11 @@ class raw_hash_set {
27612768
}
27622769

27632770
bool has_element(const value_type& elem, size_t hashval) const {
2771+
if constexpr (!std_alloc_t::value) {
2772+
// ctrl_ could be nullptr
2773+
if (!ctrl_)
2774+
return false;
2775+
}
27642776
auto seq = probe(hashval);
27652777
while (true) {
27662778
Group g{ ctrl_ + seq.offset() };
@@ -2823,6 +2835,11 @@ class raw_hash_set {
28232835
protected:
28242836
template<class K>
28252837
size_t _find_key(const K& key, size_t hashval) {
2838+
if constexpr (!std_alloc_t::value) {
2839+
// ctrl_ could be nullptr
2840+
if (!ctrl_)
2841+
return (size_t)-1;
2842+
}
28262843
auto seq = probe(hashval);
28272844
while (true) {
28282845
Group g{ ctrl_ + seq.offset() };
@@ -2847,6 +2864,11 @@ class raw_hash_set {
28472864
}
28482865

28492866
size_t prepare_insert(size_t hashval) GTL_ATTRIBUTE_NOINLINE {
2867+
if constexpr (!std_alloc_t::value) {
2868+
// ctrl_ could be nullptr
2869+
if (!ctrl_)
2870+
rehash_and_grow_if_necessary();
2871+
}
28502872
auto target = find_first_non_full(hashval);
28512873
if (GTL_PREDICT_FALSE(growth_left() == 0 && !IsDeleted(ctrl_[target.offset]))) {
28522874
rehash_and_grow_if_necessary();
@@ -2966,10 +2988,10 @@ class raw_hash_set {
29662988
// - ctrl/slots can be derived from each other
29672989
// - size can be moved into the slot array
29682990
// -------------------------------------------------------------------------
2969-
ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t]
2970-
slot_type* slots_ = nullptr; // [capacity * slot_type]
2971-
size_t size_ = 0; // number of full slots
2972-
size_t capacity_ = 0; // total number of slots
2991+
ctrl_t* ctrl_ = EmptyGroup<std_alloc_t>(); // [(capacity + 1) * ctrl_t]
2992+
slot_type* slots_ = nullptr; // [capacity * slot_type]
2993+
size_t size_ = 0; // number of full slots
2994+
size_t capacity_ = 0; // total number of slots
29732995
std::tuple<size_t /* growth_left */, hasher, key_equal, allocator_type> settings_{ 0,
29742996
hasher{},
29752997
key_equal{},
@@ -4059,6 +4081,7 @@ class parallel_hash_set {
40594081
// Do not use erase APIs taking iterators when accessing the map concurrently
40604082
// --------------------------------------------------------------------
40614083
iterator erase(iterator it) {
4084+
assert(it != end());
40624085
_erase(it++);
40634086
return it;
40644087
}
@@ -4991,6 +5014,8 @@ struct HashtableDebugAccess<Set, std::enable_if_t<has_member_type_raw_hash_set<S
49915014
using Slot = typename Traits::slot_type;
49925015

49935016
static size_t GetNumProbes(const Set& set, const typename Set::key_type& key) {
5017+
if (!set.ctrl_)
5018+
return 0;
49945019
size_t num_probes = 0;
49955020
size_t hashval = set.hash(key);
49965021
auto seq = set.probe(hashval);

tests/phmap/raw_hash_set_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ TEST(BitMask, LeadingTrailing) {
160160

161161
TEST(Group, EmptyGroup) {
162162
for (h2_t h = 0; h != 128; ++h)
163-
EXPECT_FALSE(Group{ EmptyGroup() }.Match(h));
163+
EXPECT_FALSE(Group{ EmptyGroup<std::true_type>() }.Match(h));
164164
}
165165

166166
TEST(Group, Match) {

0 commit comments

Comments
 (0)