raw_hash_set.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright 2018 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "absl/container/internal/raw_hash_set.h"
  15. #include <atomic>
  16. #include <cstddef>
  17. #include <cstring>
  18. #include "absl/base/config.h"
  19. namespace absl {
  20. ABSL_NAMESPACE_BEGIN
  21. namespace container_internal {
  22. // A single block of empty control bytes for tables without any slots allocated.
  23. // This enables removing a branch in the hot path of find().
  24. // We have 17 bytes because there may be a generation counter. Any constant is
  25. // fine for the generation counter.
  26. alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[17] = {
  27. ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
  28. ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
  29. ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
  30. ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
  31. static_cast<ctrl_t>(0)};
  32. #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
  33. constexpr size_t Group::kWidth;
  34. #endif
  35. // Returns "random" seed.
  36. inline size_t RandomSeed() {
  37. #ifdef ABSL_HAVE_THREAD_LOCAL
  38. static thread_local size_t counter = 0;
  39. size_t value = ++counter;
  40. #else // ABSL_HAVE_THREAD_LOCAL
  41. static std::atomic<size_t> counter(0);
  42. size_t value = counter.fetch_add(1, std::memory_order_relaxed);
  43. #endif // ABSL_HAVE_THREAD_LOCAL
  44. return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
  45. }
  46. bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl) {
  47. // To avoid problems with weak hashes and single bit tests, we use % 13.
  48. // TODO(kfm,sbenza): revisit after we do unconditional mixing
  49. return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6;
  50. }
  51. void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) {
  52. assert(ctrl[capacity] == ctrl_t::kSentinel);
  53. assert(IsValidCapacity(capacity));
  54. for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) {
  55. Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
  56. }
  57. // Copy the cloned ctrl bytes.
  58. std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes());
  59. ctrl[capacity] = ctrl_t::kSentinel;
  60. }
  61. // Extern template instantiation for inline function.
  62. template FindInfo find_first_non_full(const CommonFields&, size_t);
  63. FindInfo find_first_non_full_outofline(const CommonFields& common,
  64. size_t hash) {
  65. return find_first_non_full(common, hash);
  66. }
  67. // Return address of the ith slot in slots where each slot occupies slot_size.
  68. static inline void* SlotAddress(void* slot_array, size_t slot,
  69. size_t slot_size) {
  70. return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot_array) +
  71. (slot * slot_size));
  72. }
  73. // Return the address of the slot just after slot assuming each slot
  74. // has the specified size.
  75. static inline void* NextSlot(void* slot, size_t slot_size) {
  76. return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) + slot_size);
  77. }
  78. // Return the address of the slot just before slot assuming each slot
  79. // has the specified size.
  80. static inline void* PrevSlot(void* slot, size_t slot_size) {
  81. return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) - slot_size);
  82. }
  83. void DropDeletesWithoutResize(CommonFields& common,
  84. const PolicyFunctions& policy, void* tmp_space) {
  85. void* set = &common;
  86. void* slot_array = common.slots_;
  87. const size_t capacity = common.capacity_;
  88. assert(IsValidCapacity(capacity));
  89. assert(!is_small(capacity));
  90. // Algorithm:
  91. // - mark all DELETED slots as EMPTY
  92. // - mark all FULL slots as DELETED
  93. // - for each slot marked as DELETED
  94. // hash = Hash(element)
  95. // target = find_first_non_full(hash)
  96. // if target is in the same group
  97. // mark slot as FULL
  98. // else if target is EMPTY
  99. // transfer element to target
  100. // mark slot as EMPTY
  101. // mark target as FULL
  102. // else if target is DELETED
  103. // swap current element with target element
  104. // mark target as FULL
  105. // repeat procedure for current slot with moved from element (target)
  106. ctrl_t* ctrl = common.control_;
  107. ConvertDeletedToEmptyAndFullToDeleted(ctrl, capacity);
  108. auto hasher = policy.hash_slot;
  109. auto transfer = policy.transfer;
  110. const size_t slot_size = policy.slot_size;
  111. size_t total_probe_length = 0;
  112. void* slot_ptr = SlotAddress(slot_array, 0, slot_size);
  113. for (size_t i = 0; i != capacity;
  114. ++i, slot_ptr = NextSlot(slot_ptr, slot_size)) {
  115. assert(slot_ptr == SlotAddress(slot_array, i, slot_size));
  116. if (!IsDeleted(ctrl[i])) continue;
  117. const size_t hash = (*hasher)(set, slot_ptr);
  118. const FindInfo target = find_first_non_full(common, hash);
  119. const size_t new_i = target.offset;
  120. total_probe_length += target.probe_length;
  121. // Verify if the old and new i fall within the same group wrt the hash.
  122. // If they do, we don't need to move the object as it falls already in the
  123. // best probe we can.
  124. const size_t probe_offset = probe(common, hash).offset();
  125. const auto probe_index = [probe_offset, capacity](size_t pos) {
  126. return ((pos - probe_offset) & capacity) / Group::kWidth;
  127. };
  128. // Element doesn't move.
  129. if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
  130. SetCtrl(common, i, H2(hash), slot_size);
  131. continue;
  132. }
  133. void* new_slot_ptr = SlotAddress(slot_array, new_i, slot_size);
  134. if (IsEmpty(ctrl[new_i])) {
  135. // Transfer element to the empty spot.
  136. // SetCtrl poisons/unpoisons the slots so we have to call it at the
  137. // right time.
  138. SetCtrl(common, new_i, H2(hash), slot_size);
  139. (*transfer)(set, new_slot_ptr, slot_ptr);
  140. SetCtrl(common, i, ctrl_t::kEmpty, slot_size);
  141. } else {
  142. assert(IsDeleted(ctrl[new_i]));
  143. SetCtrl(common, new_i, H2(hash), slot_size);
  144. // Until we are done rehashing, DELETED marks previously FULL slots.
  145. // Swap i and new_i elements.
  146. (*transfer)(set, tmp_space, new_slot_ptr);
  147. (*transfer)(set, new_slot_ptr, slot_ptr);
  148. (*transfer)(set, slot_ptr, tmp_space);
  149. // repeat the processing of the ith slot
  150. --i;
  151. slot_ptr = PrevSlot(slot_ptr, slot_size);
  152. }
  153. }
  154. ResetGrowthLeft(common);
  155. common.infoz().RecordRehash(total_probe_length);
  156. }
  157. void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size) {
  158. assert(IsFull(*it) && "erasing a dangling iterator");
  159. --c.size_;
  160. const auto index = static_cast<size_t>(it - c.control_);
  161. const size_t index_before = (index - Group::kWidth) & c.capacity_;
  162. const auto empty_after = Group(it).MaskEmpty();
  163. const auto empty_before = Group(c.control_ + index_before).MaskEmpty();
  164. // We count how many consecutive non empties we have to the right and to the
  165. // left of `it`. If the sum is >= kWidth then there is at least one probe
  166. // window that might have seen a full group.
  167. bool was_never_full = empty_before && empty_after &&
  168. static_cast<size_t>(empty_after.TrailingZeros()) +
  169. empty_before.LeadingZeros() <
  170. Group::kWidth;
  171. SetCtrl(c, index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted,
  172. slot_size);
  173. c.growth_left() += (was_never_full ? 1 : 0);
  174. c.infoz().RecordErase();
  175. }
  176. void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
  177. bool reuse) {
  178. c.size_ = 0;
  179. if (reuse) {
  180. ResetCtrl(c, policy.slot_size);
  181. c.infoz().RecordStorageChanged(0, c.capacity_);
  182. } else {
  183. void* set = &c;
  184. (*policy.dealloc)(set, policy, c.control_, c.slots_, c.capacity_);
  185. c.control_ = EmptyGroup();
  186. c.set_generation_ptr(EmptyGeneration());
  187. c.slots_ = nullptr;
  188. c.capacity_ = 0;
  189. c.growth_left() = 0;
  190. c.infoz().RecordClearedReservation();
  191. assert(c.size_ == 0);
  192. c.infoz().RecordStorageChanged(0, 0);
  193. }
  194. }
  195. } // namespace container_internal
  196. ABSL_NAMESPACE_END
  197. } // namespace absl