@@ -27,6 +27,8 @@ constexpr size_t kMinSizeShift = 2;
2727constexpr size_t kMinSize = 1 << kMinSizeShift ;
2828constexpr bool kAllowDisplacements = true ;
2929
30+ thread_local absl::InsecureBitGen tl_bit_gen;
31+
3032#define PREFETCH_READ (x ) __builtin_prefetch(x, 0 , 1 )
3133
3234DenseSet::IteratorBase::IteratorBase (const DenseSet* owner, bool is_end)
@@ -673,13 +675,28 @@ void DenseSet::Delete(DensePtr* prev, DensePtr* ptr) {
673675}
674676
675677DenseSet::ChainVectorIterator DenseSet::GetRandomChain () {
676- size_t offset = absl::Uniform (absl::BitGen{}, 0u , entries_.size ());
677- for (size_t i = offset; i < entries_.size () + offset; i++) {
678- auto it = entries_.begin () + (i % entries_.size ());
679- ExpireIfNeeded (nullptr , &*it);
680- if (!it->IsEmpty ())
681- return it;
678+ if (entries_.empty () || size_ == 0 ) {
679+ return entries_.end ();
682680 }
681+
682+ size_t offset = absl::Uniform<size_t >(tl_bit_gen, 0u , entries_.size ());
683+
684+ // Start at random position and scan linearly with wrap-around
685+ auto it = entries_.begin () + offset;
686+ for (size_t n = 0 ; n < entries_.size (); n++) {
687+ // Check IsEmpty first to avoid ExpireIfNeeded overhead on empty buckets
688+ if (!it->IsEmpty ()) {
689+ ExpireIfNeeded (nullptr , &*it);
690+ if (!it->IsEmpty ()) {
691+ return it;
692+ }
693+ }
694+
695+ if (++it == entries_.end ()) {
696+ it = entries_.begin ();
697+ }
698+ }
699+
683700 return entries_.end ();
684701}
685702
@@ -689,8 +706,7 @@ DenseSet::IteratorBase DenseSet::GetRandomIterator() {
689706 return IteratorBase{};
690707
691708 DensePtr* ptr = &*chain_it;
692- absl::BitGen bg{};
693- while (ptr->IsLink () && absl::Bernoulli (bg, 0.5 )) {
709+ while (ptr->IsLink () && absl::Bernoulli (tl_bit_gen, 0.5 )) {
694710 DensePtr* next = ptr->Next ();
695711 if (ExpireIfNeeded (ptr, next)) // stop if we break the chain with expiration
696712 break ;
0 commit comments