storage/nvm/pet_kv/pet_hash.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <emmintrin.h> //NOLINT | ||
| 4 | #include <folly/GLog.h> | ||
| 5 | #include <nmmintrin.h> //NOLINT | ||
| 6 | |||
| 7 | #include <cmath> | ||
| 8 | #include <functional> | ||
| 9 | #include <iostream> | ||
| 10 | #include <thread> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "base/array.h" | ||
| 14 | #include "base/base.h" | ||
| 15 | #include "persistence.h" | ||
| 16 | |||
| 17 | namespace base { | ||
| 18 | |||
| 19 | #pragma pack(push) | ||
| 20 | #pragma pack(2) | ||
| 21 | template <class K, class T> | ||
| 22 | struct EntryT { | ||
| 23 | EntryT() : first(), second() {} | ||
| 24 | 75530 | explicit EntryT(K s, T d) : first(s), second(d) {} | |
| 25 | explicit EntryT(K s) : first(s), second() {} | ||
| 26 | K first; | ||
| 27 | T second; | ||
| 28 | }; | ||
| 29 | #pragma pack(pop) | ||
| 30 | |||
| 31 | class SparseMaskIter { | ||
| 32 | public: | ||
| 33 | 392874 | explicit SparseMaskIter(int mask) : mask_(mask) {} | |
| 34 | 392874 | bool HasNext() const { return mask_ != 0; } | |
| 35 | 239196 | unsigned Next() { | |
| 36 | 239196 | unsigned i = __builtin_ctz(mask_); | |
| 37 | 239196 | mask_ &= (mask_ - 1); | |
| 38 | 239196 | return i; | |
| 39 | } | ||
| 40 | uint32_t Val() const { return mask_; } | ||
| 41 | |||
| 42 | private: | ||
| 43 | uint32_t mask_; | ||
| 44 | }; | ||
| 45 | |||
| 46 | template <typename KeyT, typename ValueT, bool Persistence = false> | ||
| 47 | struct F14Chunk { | ||
| 48 | typedef EntryT<KeyT, ValueT> ItemT; | ||
| 49 | static constexpr int kMaxSize = 14; | ||
| 50 | static constexpr int kFullMask = (1 << kMaxSize) - 1; | ||
| 51 | |||
| 52 | 392874 | SparseMaskIter MatchTag(uint8_t tag) const { | |
| 53 | 392874 | auto tag_vec = _mm_load_si128(static_cast<__m128i const*>( // NOLINT | |
| 54 | 392874 | static_cast<const void*>(&tags_[0]))); // NOLINT | |
| 55 | 785748 | auto cmp_val = _mm_set1_epi8(tag); // NOLINT | |
| 56 | 392874 | auto eq_vec = _mm_cmpeq_epi8(tag_vec, cmp_val); // NOLINT | |
| 57 | 392874 | auto mask = _mm_movemask_epi8(eq_vec) & kFullMask; // NOLINT | |
| 58 | 392874 | return SparseMaskIter{mask}; | |
| 59 | } | ||
| 60 | |||
| 61 | int Find(const KeyT& key, uint8_t tag) { | ||
| 62 | auto it = MatchTag(tag); | ||
| 63 | while (it.HasNext()) { | ||
| 64 | auto id = it.Next(); | ||
| 65 | auto pair = Get(id); | ||
| 66 | if (LIKELY(key == pair->first)) { | ||
| 67 | return id; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | return -1; | ||
| 71 | } | ||
| 72 | |||
| 73 | 75530 | ItemT* Insert(const KeyT& key, const ValueT& value, const uint8_t sign) { | |
| 74 |
2/16base::F14Chunk<unsigned long, base::PetKVData, true>::Insert(unsigned long const&, base::PetKVData const&, unsigned char):
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
base::F14Chunk<unsigned long, unsigned long, false>::Insert(unsigned long const&, unsigned long const&, unsigned char):
✓ Branch 4 taken 75530 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 75530 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
|
75530 | CHECK_LT(Size(), kMaxSize); |
| 75 | 75530 | size_++; | |
| 76 |
1/4base::F14Chunk<unsigned long, base::PetKVData, true>::Insert(unsigned long const&, base::PetKVData const&, unsigned char):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::F14Chunk<unsigned long, unsigned long, false>::Insert(unsigned long const&, unsigned long const&, unsigned char):
✓ Branch 1 taken 75530 times.
✗ Branch 2 not taken.
|
75530 | auto it = MatchTag(0); |
| 77 |
2/24base::F14Chunk<unsigned long, base::PetKVData, true>::Insert(unsigned long const&, base::PetKVData const&, unsigned char):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
base::F14Chunk<unsigned long, unsigned long, false>::Insert(unsigned long const&, unsigned long const&, unsigned char):
✗ Branch 1 not taken.
✓ Branch 2 taken 75530 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 75530 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
|
75530 | CHECK(it.HasNext()); |
| 78 |
1/4base::F14Chunk<unsigned long, base::PetKVData, true>::Insert(unsigned long const&, base::PetKVData const&, unsigned char):
✗ Branch 3 not taken.
✗ Branch 4 not taken.
base::F14Chunk<unsigned long, unsigned long, false>::Insert(unsigned long const&, unsigned long const&, unsigned char):
✓ Branch 3 taken 75530 times.
✗ Branch 4 not taken.
|
75530 | return Set(it.Next(), sign, ItemT{key, value}); |
| 79 | } | ||
| 80 | |||
| 81 | 75530 | ItemT* Set(size_t i, uint8_t tag, const ItemT& val) { | |
| 82 | 75530 | value_[i] = val; | |
| 83 | ✗ | IF_Persistence(clflushopt_range(&value_[i], sizeof(ItemT));); | |
| 84 | 75530 | tags_[i] = tag; | |
| 85 | 75530 | return value_ + i; | |
| 86 | } | ||
| 87 | 290084 | ItemT* Get(size_t i) { return value_ + i; } | |
| 88 | ✗ | void IncOverflow() { | |
| 89 | ✗ | if (overflow_count_ != 255) | |
| 90 | ✗ | ++overflow_count_; | |
| 91 | ✗ | } | |
| 92 | ✗ | void DecOverflow() { | |
| 93 | ✗ | if (overflow_count_ != 255) | |
| 94 | ✗ | --overflow_count_; | |
| 95 | ✗ | } | |
| 96 | ✗ | void Delete(size_t i) { | |
| 97 | ✗ | tags_[i] = 0; | |
| 98 | ✗ | value_[i].first = -1; | |
| 99 | ✗ | IF_Persistence( | |
| 100 | clflushopt_range(&value_[i].first, sizeof(value_[i].first));); | ||
| 101 | ✗ | size_--; | |
| 102 | ✗ | } | |
| 103 | 78148 | inline size_t OverflowCount() { return overflow_count_; } | |
| 104 | |||
| 105 | 151060 | inline size_t Size() const { return size_; } | |
| 106 | ✗ | bool InUse(size_t i) const { return tags_[i] != 0; } | |
| 107 | 45613056 | void Initialize() { | |
| 108 |
2/4base::F14Chunk<unsigned long, base::PetKVData, true>::Initialize():
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::F14Chunk<unsigned long, unsigned long, false>::Initialize():
✓ Branch 0 taken 638582784 times.
✓ Branch 1 taken 45613056 times.
|
684195840 | for (int i = 0; i < kMaxSize; i++) |
| 109 | 638582784 | value_[i].first = -1; | |
| 110 | 45613056 | } | |
| 111 | |||
| 112 | uint8_t tags_[kMaxSize]; | ||
| 113 | uint8_t size_; | ||
| 114 | base::Lock lock_; | ||
| 115 | std::atomic<uint8_t> overflow_count_; | ||
| 116 | std::atomic<bool> is_migrating; | ||
| 117 | ItemT value_[kMaxSize]; | ||
| 118 | } __attribute__((__aligned__(64))); | ||
| 119 | |||
| 120 | // The basic structure is borrowed from F14 hash in Folly | ||
| 121 | template <typename KeyT, | ||
| 122 | typename ValueT, | ||
| 123 | bool Persistence = false, | ||
| 124 | bool MM_PREFETCH = true> | ||
| 125 | class PetHash { | ||
| 126 | public: | ||
| 127 | typedef EntryT<KeyT, ValueT> ItemT; | ||
| 128 | typedef F14Chunk<KeyT, ValueT, Persistence> ChunkT; | ||
| 129 | typedef std::function<bool( | ||
| 130 | const KeyT&, const ValueT&, const bool& force_delete)> | ||
| 131 | CheckFuncT; | ||
| 132 | static constexpr size_t kChunkSize = ChunkT::kMaxSize; | ||
| 133 | static const uint32_t kSignBit = 8; | ||
| 134 | static const uint32_t kSignMask = (1 << kSignBit) - 1; | ||
| 135 | static const uint32_t kMaxLoadFactor = 90; | ||
| 136 | |||
| 137 | 348 | PetHash() = default; | |
| 138 | |||
| 139 | 696 | static int64_t OverFallChunkNum(uint64_t capacity) { | |
| 140 | 696 | return Next2Power((capacity + kChunkSize - 1) / kChunkSize); | |
| 141 | // return 2 * (capacity + kChunkSize - 1) / kChunkSize; | ||
| 142 | } | ||
| 143 | |||
| 144 | 348 | void Initialize(uint64_t capacity, bool ignore_load_factor = false) { | |
| 145 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Initialize(unsigned long, bool):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Initialize(unsigned long, bool):
✓ Branch 0 taken 348 times.
✗ Branch 1 not taken.
|
348 | if (!ignore_load_factor) |
| 146 | 348 | capacity /= kMaxLoadFactor / 100.0; | |
| 147 | 348 | chunk_num_ = OverFallChunkNum(capacity); | |
| 148 | 348 | capacity_ = chunk_num_ * kChunkSize; | |
| 149 | 348 | size_ = 0; | |
| 150 | 348 | memset(chunk_table_, 0, chunk_num_ * sizeof(ChunkT)); | |
| 151 |
2/4base::PetHash<unsigned long, base::PetKVData, true, true>::Initialize(unsigned long, bool):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Initialize(unsigned long, bool):
✓ Branch 0 taken 45613056 times.
✓ Branch 1 taken 348 times.
|
45613404 | for (uint64_t i = 0; i < chunk_num_; i++) { |
| 152 | 45613056 | chunk_table_[i].Initialize(); | |
| 153 | } | ||
| 154 | 348 | } | |
| 155 | |||
| 156 | // Note(xieminhui) that mallocSetValid should be thread safe | ||
| 157 | ✗ | void Reload(uint64_t capacity, | |
| 158 | bool ignore_load_factor, | ||
| 159 | std::function<void(KeyT, ValueT)> mallocSetValid, | ||
| 160 | const int kRecoveryThread = 4) { | ||
| 161 | CHECK(Persistence) << "If not PMEM KV, dont use Reload"; | ||
| 162 | ✗ | if (!ignore_load_factor) | |
| 163 | ✗ | capacity /= kMaxLoadFactor / 100.0; | |
| 164 | // chunk_num_ = Next2Power((capacity + kChunkSize - 1) / kChunkSize); | ||
| 165 | ✗ | chunk_num_ = OverFallChunkNum(capacity); | |
| 166 | ✗ | capacity_ = chunk_num_ * kChunkSize; | |
| 167 | ✗ | uint64_t old_size = size_; | |
| 168 | ✗ | size_ = 0; | |
| 169 | ✗ | std::atomic<uint64_t> count_size(0); | |
| 170 | ✗ | std::vector<std::thread> thread_pool; | |
| 171 | ✗ | uint64_t block_num = chunk_num_ / kRecoveryThread + 1; | |
| 172 | ✗ | for (int i = 0; i < kRecoveryThread; ++i) { | |
| 173 | ✗ | uint64_t left = i * block_num; | |
| 174 | ✗ | uint64_t right = left + block_num; | |
| 175 | ✗ | thread_pool.push_back(std::thread([&, left, right] { | |
| 176 | ✗ | for (int chunk_id = left; chunk_id < right && chunk_id < chunk_num_; | |
| 177 | chunk_id++) { | ||
| 178 | ✗ | ChunkT& chunk = chunk_table_[chunk_id]; | |
| 179 | ✗ | chunk.size_ = 0; | |
| 180 | // reset inconsistent overflow_count_ | ||
| 181 | ✗ | chunk.overflow_count_ = 0; | |
| 182 | ✗ | for (int chunk_offset = 0; chunk_offset < ChunkT::kMaxSize; | |
| 183 | chunk_offset++) { | ||
| 184 | ✗ | if (chunk.value_[chunk_offset].first == -1) | |
| 185 | ✗ | continue; | |
| 186 | ✗ | chunk.size_++; | |
| 187 | ✗ | ItemT& chunkItem = chunk.value_[chunk_offset]; | |
| 188 | // 设置 size_ √ | ||
| 189 | ✗ | count_size++; | |
| 190 | ✗ | uint64 hash_value = Hash(chunkItem.first); | |
| 191 | ✗ | uint8 sign = Sign(hash_value); | |
| 192 | ✗ | if (UNLIKELY(chunk.tags_[chunk_offset] != sign)) { | |
| 193 | // inconsistent tags | ||
| 194 | ✗ | RECSTORE_LOG_EVERY_MS(WARNING, 2000) | |
| 195 | ✗ | << "inconsistent tag, key = " << chunkItem.first | |
| 196 | ✗ | << " , correct it"; | |
| 197 | ✗ | chunk.tags_[chunk_offset] = sign; | |
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 201 | })); | ||
| 202 | } | ||
| 203 | ✗ | for (auto& each : thread_pool) | |
| 204 | ✗ | each.join(); | |
| 205 | ✗ | size_.store(count_size); | |
| 206 | ✗ | if (fabs((double)old_size - (double)size_) >= 1000) { | |
| 207 | ✗ | LOG(ERROR) << "Large Diff after reload."; | |
| 208 | } | ||
| 209 | |||
| 210 | // 设置 overflowbit | ||
| 211 | ✗ | thread_pool.clear(); | |
| 212 | ✗ | for (int i = 0; i < kRecoveryThread; ++i) { | |
| 213 | ✗ | uint64_t left = i * block_num; | |
| 214 | ✗ | uint64_t right = left + block_num; | |
| 215 | ✗ | thread_pool.push_back(std::thread([&, left, right] { | |
| 216 | ✗ | for (int chunk_id = left; chunk_id < right && chunk_id < chunk_num_; | |
| 217 | chunk_id++) { | ||
| 218 | ✗ | ChunkT& chunk = chunk_table_[chunk_id]; | |
| 219 | ✗ | for (int chunk_offset = 0; chunk_offset < ChunkT::kMaxSize; | |
| 220 | chunk_offset++) { | ||
| 221 | ✗ | ItemT& chunkItem = chunk.value_[chunk_offset]; | |
| 222 | ✗ | if (chunk.InUse(chunk_offset)) { | |
| 223 | ✗ | mallocSetValid(chunkItem.first, chunkItem.second); | |
| 224 | ✗ | uint64 hash_value = Hash(chunkItem.first); | |
| 225 | ✗ | size_t step = ProbeDelta(Sign(hash_value)); | |
| 226 | ✗ | uint64 pos = hash_value & (chunk_num_ - 1); | |
| 227 | ✗ | while (pos != chunk_id) { | |
| 228 | /* before | ||
| 229 | * chunk_table_[pos].IncOverflow(); | ||
| 230 | ********************************* | ||
| 231 | * after | ||
| 232 | * overflow_count_ will only incr. Thus we | ||
| 233 | * can use CAS to do lock free while avoid overflow | ||
| 234 | */ | ||
| 235 | ✗ | uint8* prev_overflow_count = nullptr; | |
| 236 | uint8_t temp; | ||
| 237 | do { | ||
| 238 | ✗ | temp = chunk_table_[pos].overflow_count_; | |
| 239 | ✗ | if (temp == 255) | |
| 240 | ✗ | break; | |
| 241 | } while ( | ||
| 242 | ✗ | !chunk_table_[pos].overflow_count_.compare_exchange_strong( | |
| 243 | ✗ | temp, temp + 1)); // NOLINT | |
| 244 | ✗ | pos = (pos + step) & (chunk_num_ - 1); | |
| 245 | } | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | })); | ||
| 250 | } | ||
| 251 | ✗ | for (auto& each : thread_pool) | |
| 252 | ✗ | each.join(); | |
| 253 | ✗ | } | |
| 254 | |||
| 255 | static uint64_t | ||
| 256 | 348 | MemorySize(uint64_t capacity, bool ignore_load_factor = false) { | |
| 257 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::MemorySize(unsigned long, bool):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::MemorySize(unsigned long, bool):
✓ Branch 0 taken 348 times.
✗ Branch 1 not taken.
|
348 | if (!ignore_load_factor) |
| 258 | 348 | capacity /= kMaxLoadFactor / 100.0; | |
| 259 | // auto chunk_table_num = Next2Power((capacity + kChunkSize - 1) / | ||
| 260 | // kChunkSize); | ||
| 261 | 348 | auto chunk_table_num = OverFallChunkNum(capacity); | |
| 262 | 348 | return sizeof(PetHash) + chunk_table_num * sizeof(ChunkT); | |
| 263 | } | ||
| 264 | |||
| 265 | 696 | static uint64_t Next2Power(uint64_t size) { | |
| 266 | 696 | uint64_t new_size = 1; | |
| 267 |
2/4base::PetHash<unsigned long, base::PetKVData, true, true>::Next2Power(unsigned long):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Next2Power(unsigned long):
✓ Branch 0 taken 11832 times.
✓ Branch 1 taken 696 times.
|
12528 | while (new_size < size) |
| 268 | 11832 | new_size <<= 1; | |
| 269 | 696 | return new_size; | |
| 270 | } | ||
| 271 | |||
| 272 | ✗ | bool Valid(int64_t memory_size) { | |
| 273 | ✗ | if (capacity_ == 0 && memory_size > 0) { | |
| 274 | ✗ | LOG(ERROR) << "PetHash invalid. capacity_ == 0"; | |
| 275 | ✗ | return false; | |
| 276 | } | ||
| 277 | ✗ | if (size_ > capacity_) { | |
| 278 | ✗ | LOG(ERROR) << "PetHash invalid. dict size_ > capacity_"; | |
| 279 | ✗ | return false; | |
| 280 | } | ||
| 281 | ✗ | if (chunk_num_ * kChunkSize != capacity_) { | |
| 282 | ✗ | LOG(ERROR) << "chunk_num_ * kChunkSize != capacity_"; | |
| 283 | ✗ | return false; | |
| 284 | } | ||
| 285 | ✗ | if (memory_size != MemorySize(capacity_, true)) { | |
| 286 | ✗ | if (capacity_ != 0) | |
| 287 | ✗ | LOG(ERROR) << "PetHash invalid. memory_size != MemorySize(capacity_, " | |
| 288 | "true), memory_size = " | ||
| 289 | ✗ | << memory_size << ", capacity_ = " << capacity_; | |
| 290 | ✗ | return false; | |
| 291 | } | ||
| 292 | ✗ | uint64_t total_num = 0; | |
| 293 | ✗ | uint64_t chunk_table_total_num = 0; | |
| 294 | KeyT key; | ||
| 295 | ✗ | for (uint64_t i = 0; i < capacity_; ++i) { | |
| 296 | ✗ | if (IndexInUse(i)) { | |
| 297 | ✗ | GetById(i, &key); | |
| 298 | ✗ | auto [value, exists] = Get(key); | |
| 299 | ✗ | if (!exists) { | |
| 300 | ✗ | LOG(ERROR) << "PetHash invalid. GetById has key, but Get(key) " | |
| 301 | "failed; key is " | ||
| 302 | ✗ | << key; | |
| 303 | ✗ | return false; | |
| 304 | } | ||
| 305 | ✗ | total_num++; | |
| 306 | } | ||
| 307 | } | ||
| 308 | ✗ | for (int i = 0; i < chunk_num_; ++i) { | |
| 309 | ✗ | chunk_table_total_num += chunk_table_[i].Size(); | |
| 310 | } | ||
| 311 | ✗ | if (total_num != chunk_table_total_num) | |
| 312 | ✗ | LOG(ERROR) << fmt::format( | |
| 313 | "PetHash invalid {} != {}", total_num, chunk_table_total_num); | ||
| 314 | ✗ | if (total_num != size_) | |
| 315 | ✗ | LOG(ERROR) << fmt::format( | |
| 316 | ✗ | "PetHash invalid {} != {}", total_num, size_.load()); | |
| 317 | ✗ | return total_num == chunk_table_total_num && total_num == size_; | |
| 318 | } | ||
| 319 | |||
| 320 | void Debug() { | ||
| 321 | LOG(INFO) << "capacity_: " << capacity_; | ||
| 322 | LOG(INFO) << "chunk_num_: " << chunk_num_; | ||
| 323 | LOG(INFO) << "Chunk size: " << sizeof(ChunkT); | ||
| 324 | LOG(INFO) << "PetHash size: " << sizeof(PetHash); | ||
| 325 | } | ||
| 326 | |||
| 327 | 103702 | std::pair<const ValueT, bool> Get(const KeyT& key) const { | |
| 328 | 103702 | auto* p = (PetHash<KeyT, ValueT, Persistence, MM_PREFETCH>*)(this); | |
| 329 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Get(unsigned long const&) const:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Get(unsigned long const&) const:
✓ Branch 1 taken 103702 times.
✗ Branch 2 not taken.
|
103702 | auto [value, p_value, exist] = |
| 330 | p->GetInternal(key, nullptr, nullptr, nullptr); | ||
| 331 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Get(unsigned long const&) const:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Get(unsigned long const&) const:
✓ Branch 1 taken 103702 times.
✗ Branch 2 not taken.
|
103702 | return std::make_pair(value, exist); |
| 332 | } | ||
| 333 | |||
| 334 | ✗ | std::pair<ValueT*, bool> GetReturnPtr(const KeyT& key) { | |
| 335 | ✗ | auto [value, p_value, exist] = GetInternal(key, nullptr, nullptr, nullptr); | |
| 336 | ✗ | return std::make_pair(p_value, exist); | |
| 337 | } | ||
| 338 | |||
| 339 | 22716 | inline void HintPrefetch(const KeyT key) { | |
| 340 | 22716 | uint64_t hash_value = Hash(key); | |
| 341 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::HintPrefetch(unsigned long):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::HintPrefetch(unsigned long):
✓ Branch 1 taken 22716 times.
✗ Branch 2 not taken.
|
22716 | auto sign = Sign(hash_value); |
| 342 | 22716 | size_t pos = hash_value & (chunk_num_ - 1); | |
| 343 | 22716 | auto chunk = chunk_table_ + pos; | |
| 344 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::HintPrefetch(unsigned long):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::HintPrefetch(unsigned long):
✓ Branch 1 taken 22716 times.
✗ Branch 2 not taken.
|
22716 | _mm_prefetch((const char*)(chunk), _MM_HINT_T0); |
| 345 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::HintPrefetch(unsigned long):
✗ Branch 2 not taken.
✗ Branch 3 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::HintPrefetch(unsigned long):
✓ Branch 2 taken 22716 times.
✗ Branch 3 not taken.
|
22716 | _mm_prefetch((const char*)(chunk->Get(0)), _MM_HINT_T0); |
| 346 | 22716 | } | |
| 347 | |||
| 348 | std::tuple<const ValueT, ValueT* const, bool> | ||
| 349 | 103702 | GetInternal(const KeyT& key, | |
| 350 | uint64* const item_pos = nullptr, | ||
| 351 | ChunkT** const chunk_pos = nullptr, | ||
| 352 | uint8_t* const in_chunk_id = nullptr) { | ||
| 353 | 103702 | RETRY: | |
| 354 | 103702 | uint64_t hash_value = Hash(key); | |
| 355 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✓ Branch 1 taken 103702 times.
✗ Branch 2 not taken.
|
103702 | auto sign = Sign(hash_value); |
| 356 | 103702 | size_t step = ProbeDelta(sign); | |
| 357 | 103702 | size_t pos = hash_value & (chunk_num_ - 1); | |
| 358 | size_t id; | ||
| 359 | 103702 | bool touch_migrating_chunk = false; | |
| 360 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✓ Branch 0 taken 103702 times.
✗ Branch 1 not taken.
|
103702 | for (size_t i = 0; i < chunk_num_; ++i) { |
| 361 | 103702 | auto chunk = chunk_table_ + pos; | |
| 362 | if (MM_PREFETCH) { | ||
| 363 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 2 not taken.
✗ Branch 3 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✓ Branch 2 taken 103702 times.
✗ Branch 3 not taken.
|
103702 | _mm_prefetch( |
| 364 | static_cast<char const*>(static_cast<void const*>( // NOLINT | ||
| 365 | chunk->Get(3))), | ||
| 366 | _MM_HINT_T0); // NOLINT | ||
| 367 | } | ||
| 368 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✗ Branch 1 not taken.
✓ Branch 2 taken 103702 times.
|
103702 | if (UNLIKELY(chunk->is_migrating)) |
| 369 | ✗ | touch_migrating_chunk = true; | |
| 370 | |||
| 371 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✓ Branch 1 taken 103702 times.
✗ Branch 2 not taken.
|
103702 | auto it = chunk->MatchTag(sign); |
| 372 |
2/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✓ Branch 1 taken 101088 times.
✓ Branch 2 taken 2614 times.
|
103702 | while (it.HasNext()) { |
| 373 | 101088 | id = it.Next(); | |
| 374 | 101088 | auto val = chunk->Get(id); | |
| 375 | // re-read the key to ensure no-one modify it | ||
| 376 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✓ Branch 0 taken 101088 times.
✗ Branch 1 not taken.
|
101088 | if (LIKELY(key == val->first)) { |
| 377 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✗ Branch 0 not taken.
✓ Branch 1 taken 101088 times.
|
101088 | if (item_pos != nullptr) |
| 378 | ✗ | *item_pos = pos * kChunkSize + id; | |
| 379 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✗ Branch 0 not taken.
✓ Branch 1 taken 101088 times.
|
101088 | if (chunk_pos != nullptr) |
| 380 | ✗ | *chunk_pos = chunk; | |
| 381 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✗ Branch 0 not taken.
✓ Branch 1 taken 101088 times.
|
101088 | if (in_chunk_id != nullptr) |
| 382 | ✗ | *in_chunk_id = id; | |
| 383 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✓ Branch 1 taken 101088 times.
✗ Branch 2 not taken.
|
101088 | return std::make_tuple(val->second, &val->second, true); |
| 384 | } | ||
| 385 | } | ||
| 386 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✓ Branch 1 taken 2614 times.
✗ Branch 2 not taken.
|
2614 | if (LIKELY(chunk->OverflowCount() == 0)) |
| 387 | 2614 | break; | |
| 388 | ✗ | pos = (pos + step) & (chunk_num_ - 1); | |
| 389 | } | ||
| 390 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✗ Branch 0 not taken.
✓ Branch 1 taken 2614 times.
|
2614 | if (UNLIKELY(touch_migrating_chunk)) |
| 391 | ✗ | goto RETRY; | |
| 392 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, base::PetKVData, true>**, unsigned char*):
✗ Branch 2 not taken.
✗ Branch 3 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::GetInternal(unsigned long const&, unsigned long*, base::F14Chunk<unsigned long, unsigned long, false>**, unsigned char*):
✓ Branch 1 taken 2614 times.
✗ Branch 2 not taken.
|
2614 | return std::make_tuple(ValueT(), nullptr, false); |
| 393 | } | ||
| 394 | |||
| 395 | ChunkT* const NextChunk(ChunkT* const chunk, const int step) { | ||
| 396 | auto p = chunk - chunk_table_; | ||
| 397 | p = (p + step + chunk_num_) % chunk_num_; | ||
| 398 | return chunk_table_ + p; | ||
| 399 | } | ||
| 400 | ChunkT* const PrevChunk(ChunkT* const chunk, const int step) { | ||
| 401 | auto p = chunk - chunk_table_; | ||
| 402 | p = (p - step + chunk_num_) % chunk_num_; | ||
| 403 | return chunk_table_ + p; | ||
| 404 | } | ||
| 405 | |||
| 406 | // start -> mid ... -> mid -> end , [start, end] | ||
| 407 | void ChainTraverseReverse(ChunkT* const start, | ||
| 408 | ChunkT* const end, | ||
| 409 | int step, | ||
| 410 | std::function<void(ChunkT* const)> func) { | ||
| 411 | ChunkT* current_chunk = end; | ||
| 412 | while (current_chunk != start) { | ||
| 413 | func(current_chunk); | ||
| 414 | current_chunk = PrevChunk(current_chunk, step); | ||
| 415 | } | ||
| 416 | func(current_chunk); | ||
| 417 | } | ||
| 418 | |||
| 419 | void ChainTraverse(ChunkT* const start, | ||
| 420 | ChunkT* const end, | ||
| 421 | int step, | ||
| 422 | std::function<void(ChunkT* const)> func) { | ||
| 423 | ChunkT* current_chunk = end; | ||
| 424 | while (current_chunk != start) { | ||
| 425 | func(current_chunk); | ||
| 426 | current_chunk = NextChunk(current_chunk, step); | ||
| 427 | } | ||
| 428 | func(current_chunk); | ||
| 429 | } | ||
| 430 | |||
| 431 | void HotnessAwareMigration(const base::ConstArray<KeyT> hot_keys) { | ||
| 432 | for (auto key : hot_keys) { | ||
| 433 | auto [home_chunk, home_chunk_id] = HomeChunk(key); | ||
| 434 | auto sign = Sign(Hash(key)); | ||
| 435 | size_t hotkey_step = ProbeDelta(sign); | ||
| 436 | int id_in_chunk = home_chunk->Find(key, sign); | ||
| 437 | if (id_in_chunk != -1) { | ||
| 438 | // hot KV is already in its home chunk, continue | ||
| 439 | continue; | ||
| 440 | } | ||
| 441 | ChunkT* current_chunk; | ||
| 442 | uint8_t current_in_chunk_id; | ||
| 443 | auto [value, p_value_no_use, exists] = | ||
| 444 | GetInternal(key, nullptr, ¤t_chunk, ¤t_in_chunk_id); | ||
| 445 | CHECK(exists); | ||
| 446 | |||
| 447 | ChainTraverseReverse( | ||
| 448 | home_chunk, current_chunk, hotkey_step, [](ChunkT* const each) { | ||
| 449 | each->is_migrating = true; | ||
| 450 | }); | ||
| 451 | |||
| 452 | // case 1: home_chunk has empty slot | ||
| 453 | if (LIKELY(home_chunk->Size() != kChunkSize)) { | ||
| 454 | // insert it to its home chunk | ||
| 455 | home_chunk->Insert(key, value, sign); | ||
| 456 | // remove it from its current chunk | ||
| 457 | current_chunk->Delete(current_in_chunk_id); | ||
| 458 | current_chunk->DecOverflow(); | ||
| 459 | ChainTraverseReverse( | ||
| 460 | home_chunk, | ||
| 461 | current_chunk, | ||
| 462 | hotkey_step, | ||
| 463 | [current_chunk](ChunkT* const each) { | ||
| 464 | each->is_migrating = false; | ||
| 465 | }); | ||
| 466 | continue; | ||
| 467 | } | ||
| 468 | // case 2: migration | ||
| 469 | // Step 2.1: home_chunk select a victim | ||
| 470 | KeyT victim_key = -1; | ||
| 471 | ValueT victim_value = -1; | ||
| 472 | int victim_id = 0; | ||
| 473 | CHECK(home_chunk->InUse(victim_id)); | ||
| 474 | { | ||
| 475 | auto item = home_chunk->Get(victim_id); | ||
| 476 | victim_key = item->first; | ||
| 477 | victim_value = item->second; | ||
| 478 | } | ||
| 479 | // Step 2.2: insert a victim to another chunk | ||
| 480 | auto victim_sign = Sign(Hash(victim_key)); | ||
| 481 | size_t victim_step = ProbeDelta(victim_sign); | ||
| 482 | size_t victim_pos = (home_chunk_id + victim_step) & (chunk_num_ - 1); | ||
| 483 | bool victim_migrated = false; | ||
| 484 | auto chunk = chunk_table_ + victim_pos; | ||
| 485 | for (size_t i = 0; i < chunk_num_; ++i) { | ||
| 486 | base::AutoLock lock(chunk->lock_); | ||
| 487 | if (LIKELY(chunk->Size() != kChunkSize)) { | ||
| 488 | chunk->Insert(victim_key, victim_value, victim_sign); | ||
| 489 | // 不能把victim又插回他原来的home_chunk | ||
| 490 | CHECK_NE(home_chunk, chunk); | ||
| 491 | victim_migrated = true; | ||
| 492 | break; | ||
| 493 | } | ||
| 494 | // 达到负载上限后需要删除才能插入 | ||
| 495 | if (UNLIKELY(Full())) { | ||
| 496 | LOG(FATAL) << "todo: pet hash dict is full"; | ||
| 497 | // let the insert thread first eviction | ||
| 498 | auto it = chunk->Get((sign + i) % kChunkSize); | ||
| 499 | Delete(it->first); | ||
| 500 | } else { | ||
| 501 | chunk->IncOverflow(); | ||
| 502 | victim_pos = (victim_pos + victim_step) & (chunk_num_ - 1); | ||
| 503 | chunk = chunk_table_ + victim_pos; | ||
| 504 | } | ||
| 505 | } | ||
| 506 | CHECK(victim_migrated); | ||
| 507 | // Step 2.3: substitute victim with <key> | ||
| 508 | home_chunk->IncOverflow(); | ||
| 509 | home_chunk->Set(victim_id, sign, ItemT{key, value}); | ||
| 510 | current_chunk->Delete(current_in_chunk_id); | ||
| 511 | // all chunks along this chain should be reduced by 1 | ||
| 512 | // (current_chunk, home_chunk] | ||
| 513 | ChainTraverseReverse( | ||
| 514 | home_chunk, | ||
| 515 | current_chunk, | ||
| 516 | hotkey_step, | ||
| 517 | [current_chunk](ChunkT* const each) { | ||
| 518 | // unlock | ||
| 519 | each->is_migrating = false; | ||
| 520 | // unlock done | ||
| 521 | if (each == current_chunk) | ||
| 522 | return; | ||
| 523 | each->DecOverflow(); | ||
| 524 | }); | ||
| 525 | } | ||
| 526 | } | ||
| 527 | |||
| 528 | 138108 | ValueT* Set(const KeyT& key, | |
| 529 | const ValueT& value, | ||
| 530 | CheckFuncT check_func = nullptr, | ||
| 531 | bool is_force = false, | ||
| 532 | ValueT* old_value_out = nullptr) { | ||
| 533 | static_assert(sizeof(ValueT) <= sizeof(uint64_t), | ||
| 534 | "PetHash::Set CAS update expects a 64-bit-or-smaller value"); | ||
| 535 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 138108 times.
|
138108 | if (old_value_out != nullptr) { |
| 536 | 138108 | *old_value_out = ValueT(); | |
| 537 | } | ||
| 538 | |||
| 539 | 138108 | RETRY: | |
| 540 | 138108 | uint64_t hash_value = Hash(key); | |
| 541 |
1/2✓ Branch 1 taken 138108 times.
✗ Branch 2 not taken.
|
138108 | auto sign = Sign(hash_value); |
| 542 | 138108 | size_t step = ProbeDelta(sign); | |
| 543 | 138108 | size_t pos = hash_value & (chunk_num_ - 1); | |
| 544 | 138108 | bool touch_migrating_chunk = false; | |
| 545 | |||
| 546 |
1/2✓ Branch 0 taken 138108 times.
✗ Branch 1 not taken.
|
138108 | for (size_t i = 0; i < chunk_num_; ++i) { |
| 547 | 138108 | auto chunk = chunk_table_ + pos; | |
| 548 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 138108 times.
|
138108 | if (UNLIKELY(chunk->is_migrating)) { |
| 549 | ✗ | touch_migrating_chunk = true; | |
| 550 | } | ||
| 551 | |||
| 552 | { | ||
| 553 |
1/2✓ Branch 1 taken 138108 times.
✗ Branch 2 not taken.
|
138108 | auto it = chunk->MatchTag(sign); |
| 554 |
2/2✓ Branch 1 taken 62574 times.
✓ Branch 2 taken 75534 times.
|
138108 | while (it.HasNext()) { |
| 555 | 62574 | auto id = it.Next(); | |
| 556 | 62574 | auto val = chunk->Get(id); | |
| 557 |
1/2✓ Branch 0 taken 62574 times.
✗ Branch 1 not taken.
|
62574 | if (LIKELY(key == val->first)) { |
| 558 |
1/2✓ Branch 0 taken 62574 times.
✗ Branch 1 not taken.
|
62574 | while (LIKELY(key == val->first)) { |
| 559 | 62574 | ValueT expected = val->second; | |
| 560 | 62574 | ValueT desired = value; | |
| 561 |
1/2✓ Branch 0 taken 62574 times.
✗ Branch 1 not taken.
|
62574 | if (__atomic_compare_exchange_n(&val->second, |
| 562 | &expected, | ||
| 563 | desired, | ||
| 564 | false, | ||
| 565 | __ATOMIC_ACQ_REL, | ||
| 566 | __ATOMIC_ACQUIRE)) { | ||
| 567 |
1/2✓ Branch 0 taken 62574 times.
✗ Branch 1 not taken.
|
62574 | if (old_value_out != nullptr) { |
| 568 | 62574 | *old_value_out = expected; | |
| 569 | } | ||
| 570 | IF_Persistence(clflushopt_range(&val->second, sizeof(uint64));); | ||
| 571 | 62574 | return &val->second; | |
| 572 | } | ||
| 573 | ✗ | __asm__ volatile("pause" ::: "memory"); | |
| 574 | } | ||
| 575 | ✗ | goto RETRY; | |
| 576 | } | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 |
1/2✓ Branch 1 taken 75534 times.
✗ Branch 2 not taken.
|
75534 | if (LIKELY(chunk->OverflowCount() == 0)) { |
| 581 | 75534 | break; | |
| 582 | } | ||
| 583 | ✗ | pos = (pos + step) & (chunk_num_ - 1); | |
| 584 | } | ||
| 585 | |||
| 586 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75534 times.
|
75534 | if (UNLIKELY(touch_migrating_chunk)) { |
| 587 | ✗ | goto RETRY; | |
| 588 | } | ||
| 589 |
2/4✓ Branch 1 taken 75534 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 75534 times.
✗ Branch 5 not taken.
|
75534 | return Insert(key, value, check_func, is_force); |
| 590 | } | ||
| 591 | |||
| 592 | 75534 | std::pair<ChunkT*, size_t> HomeChunk(const KeyT& key) { | |
| 593 | 75534 | uint64_t hash_value = Hash(key); | |
| 594 | 75534 | size_t pos = hash_value & (chunk_num_ - 1); | |
| 595 | 75534 | auto chunk = chunk_table_ + pos; | |
| 596 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::HomeChunk(unsigned long const&):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::HomeChunk(unsigned long const&):
✓ Branch 1 taken 75534 times.
✗ Branch 2 not taken.
|
151068 | return std::make_pair(chunk, pos); |
| 597 | } | ||
| 598 | |||
| 599 | 75534 | ValueT* Insert(const KeyT& key, | |
| 600 | const ValueT& value, | ||
| 601 | CheckFuncT check_func = nullptr, | ||
| 602 | bool force_insert = false) { | ||
| 603 | 75534 | uint64_t hash_value = Hash(key); | |
| 604 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✓ Branch 1 taken 75534 times.
✗ Branch 2 not taken.
|
75534 | auto sign = Sign(hash_value); |
| 605 | 75534 | size_t step = ProbeDelta(sign); | |
| 606 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✓ Branch 1 taken 75534 times.
✗ Branch 2 not taken.
|
75534 | auto [chunk, pos] = HomeChunk(key); |
| 607 | |||
| 608 |
2/8base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✗ Branch 1 not taken.
✓ Branch 2 taken 75534 times.
✓ Branch 3 taken 75534 times.
✗ Branch 4 not taken.
|
151068 | for (size_t i = 0; i < chunk_num_; ++i) { |
| 609 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✓ Branch 1 taken 75534 times.
✗ Branch 2 not taken.
|
75534 | base::AutoLock lock(chunk->lock_); |
| 610 | // check whether KV expire | ||
| 611 |
2/12base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✗ Branch 1 not taken.
✓ Branch 2 taken 75534 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 75534 times.
|
75534 | if (UNLIKELY(check_func && chunk->Size() >= kChunkSize - 2)) { |
| 612 | ✗ | for (size_t id = 0; id < kChunkSize; ++id) | |
| 613 | ✗ | if (chunk->InUse(id)) { | |
| 614 | ✗ | auto it = chunk->Get(id); | |
| 615 | ✗ | if (!check_func(it->first, it->second, false)) | |
| 616 | ✗ | Delete(it->first); | |
| 617 | } | ||
| 618 | } | ||
| 619 | |||
| 620 | // re-search chunk | ||
| 621 | { | ||
| 622 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✓ Branch 1 taken 75534 times.
✗ Branch 2 not taken.
|
75534 | auto it = chunk->MatchTag(sign); |
| 623 |
2/4base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 75530 times.
|
75534 | while (it.HasNext()) { |
| 624 | 4 | auto id = it.Next(); | |
| 625 | 4 | auto val = chunk->Get(id); | |
| 626 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (LIKELY(key == val->first)) { |
| 627 | 4 | val->second = value; | |
| 628 | 4 | return &val->second; | |
| 629 | } | ||
| 630 | } | ||
| 631 | } | ||
| 632 | |||
| 633 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✓ Branch 1 taken 75530 times.
✗ Branch 2 not taken.
|
75530 | if (LIKELY(chunk->Size() < kChunkSize)) { |
| 634 |
1/4base::PetHash<unsigned long, base::PetKVData, true, true>::Insert(unsigned long const&, base::PetKVData const&, std::function<bool (unsigned long const&, base::PetKVData const&, bool const&)>, bool):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
base::PetHash<unsigned long, unsigned long, false, true>::Insert(unsigned long const&, unsigned long const&, std::function<bool (unsigned long const&, unsigned long const&, bool const&)>, bool):
✓ Branch 1 taken 75530 times.
✗ Branch 2 not taken.
|
75530 | auto val = chunk->Insert(key, value, sign); |
| 635 | 75530 | size_++; | |
| 636 | 75530 | return &val->second; | |
| 637 | } | ||
| 638 | ✗ | if (UNLIKELY(Full())) { | |
| 639 | ✗ | RECSTORE_LOG_EVERY_MS(ERROR, 1000) << fmt::format( | |
| 640 | "Warning pethash dict full, {}/{}={}", | ||
| 641 | ✗ | size_.load(), | |
| 642 | ✗ | capacity_, | |
| 643 | ✗ | size_ / (double)capacity_); | |
| 644 | ✗ | if (!force_insert) { | |
| 645 | ✗ | LOG(FATAL) << "dict OOM"; | |
| 646 | return nullptr; | ||
| 647 | } | ||
| 648 | ✗ | auto it = chunk->Get((sign + i) % kChunkSize); | |
| 649 | ✗ | if (check_func != nullptr) | |
| 650 | ✗ | check_func(it->first, it->second, true); | |
| 651 | ✗ | Delete(it->first); | |
| 652 | } else { | ||
| 653 | ✗ | chunk->IncOverflow(); | |
| 654 | ✗ | pos = (pos + step) & (chunk_num_ - 1); | |
| 655 | ✗ | chunk = chunk_table_ + pos; | |
| 656 | } | ||
| 657 | } | ||
| 658 | ✗ | LOG(FATAL) << "dict OOM"; | |
| 659 | return nullptr; | ||
| 660 | } | ||
| 661 | ✗ | bool Delete(const KeyT& key) { | |
| 662 | uint64_t pos; | ||
| 663 | ✗ | auto [nouse0, nouse1, exists] = GetInternal(key, &pos); | |
| 664 | ✗ | if (!exists) | |
| 665 | ✗ | return true; | |
| 666 | |||
| 667 | ✗ | uint64_t hash_value = Hash(key); | |
| 668 | ✗ | size_t step = ProbeDelta(Sign(hash_value)); | |
| 669 | ✗ | auto chunk_id = pos / kChunkSize; | |
| 670 | ✗ | auto chunk_offset = pos % kChunkSize; | |
| 671 | ✗ | pos = hash_value & (chunk_num_ - 1); | |
| 672 | ✗ | while (pos != chunk_id) { | |
| 673 | ✗ | chunk_table_[pos].DecOverflow(); | |
| 674 | ✗ | pos = (pos + step) & (chunk_num_ - 1); | |
| 675 | } | ||
| 676 | ✗ | chunk_table_[chunk_id].Delete(chunk_offset); | |
| 677 | ✗ | size_--; | |
| 678 | ✗ | return true; | |
| 679 | } | ||
| 680 | |||
| 681 | ✗ | ValueT* GetById(uint64_t pos, KeyT* key = nullptr) { | |
| 682 | ✗ | auto it = chunk_table_[pos / kChunkSize].Get(pos % kChunkSize); | |
| 683 | ✗ | if (key != nullptr) | |
| 684 | ✗ | *key = it->first; | |
| 685 | ✗ | return &it->second; | |
| 686 | } | ||
| 687 | |||
| 688 | ✗ | bool IndexInUse(uint64_t pos) { | |
| 689 | ✗ | if (pos >= capacity_) | |
| 690 | ✗ | return false; | |
| 691 | ✗ | return chunk_table_[pos / kChunkSize].InUse(pos % kChunkSize); | |
| 692 | } | ||
| 693 | |||
| 694 | int64_t Lookup(const KeyT& key) { | ||
| 695 | uint64_t pos; | ||
| 696 | auto [nouse0, nouse1, exists] = GetInternal(key, &pos); | ||
| 697 | if (exists) | ||
| 698 | return pos; | ||
| 699 | return -1; | ||
| 700 | } | ||
| 701 | |||
| 702 | ✗ | uint32_t Size() { return size_; } | |
| 703 | |||
| 704 | ✗ | uint32_t Capacity() { return capacity_; } | |
| 705 | |||
| 706 | ✗ | inline bool Full() { return size_ * 100 >= capacity_ * kMaxLoadFactor; } | |
| 707 | |||
| 708 | private: | ||
| 709 | 317344 | inline size_t ProbeDelta(size_t key) const { return key * 2 | 1; } | |
| 710 | |||
| 711 | 340060 | static inline uint8_t Sign(const uint64_t& key) { // NOLINT | |
| 712 | 340060 | uint64_t c = _mm_crc32_u64(0, key); // NOLINT | |
| 713 | // ?????? | ||
| 714 | // WARNING(xieminhui): diff from kuaishou | ||
| 715 | // key += c; | ||
| 716 | 340060 | return ((c >> 24) | 0x80) & kSignMask; | |
| 717 | } | ||
| 718 | |||
| 719 | 415594 | static inline uint64_t Hash(uint64_t key) { return key * 0xc6a4a7935bd1e995; } | |
| 720 | |||
| 721 | uint64_t capacity_; | ||
| 722 | uint64_t chunk_num_; | ||
| 723 | std::atomic<uint64_t> size_; | ||
| 724 | uint8_t not_use_[64 - 3 * sizeof(uint64_t)]; | ||
| 725 | ChunkT chunk_table_[0]; | ||
| 726 | }; | ||
| 727 | |||
| 728 | } // namespace base | ||
| 729 |