Dogecoin Core  1.14.2
P2P Digital Currency
cuckoocache.h
Go to the documentation of this file.
1 // Copyright (c) 2016 Jeremy Rubin
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #ifndef _BITCOIN_CUCKOOCACHE_H_
6 #define _BITCOIN_CUCKOOCACHE_H_
7 
8 #include <array>
9 #include <algorithm>
10 #include <atomic>
11 #include <cstring>
12 #include <cmath>
13 #include <memory>
14 #include <vector>
15 
16 
27 namespace CuckooCache
28 {
43 {
44  std::unique_ptr<std::atomic<uint8_t>[]> mem;
45 
46 public:
49 
61  bit_packed_atomic_flags(uint32_t size)
62  {
63  // pad out the size if needed
64  size = (size + 7) / 8;
65  mem.reset(new std::atomic<uint8_t>[size]);
66  for (uint32_t i = 0; i < size; ++i)
67  mem[i].store(0xFF);
68  };
69 
79  inline void setup(uint32_t b)
80  {
82  std::swap(mem, d.mem);
83  }
84 
92  inline void bit_set(uint32_t s)
93  {
94  mem[s >> 3].fetch_or(1 << (s & 7), std::memory_order_relaxed);
95  }
96 
103  inline void bit_unset(uint32_t s)
104  {
105  mem[s >> 3].fetch_and(~(1 << (s & 7)), std::memory_order_relaxed);
106  }
107 
113  inline bool bit_is_set(uint32_t s) const
114  {
115  return (1 << (s & 7)) & mem[s >> 3].load(std::memory_order_relaxed);
116  }
117 };
118 
159 template <typename Element, typename Hash>
160 class cache
161 {
162 private:
164  std::vector<Element> table;
165 
167  uint32_t size;
168 
172 
177  mutable std::vector<bool> epoch_flags;
178 
185 
194  uint32_t epoch_size;
195 
200  uint32_t hash_mask;
201 
204  uint8_t depth_limit;
205 
211 
218  inline std::array<uint32_t, 8> compute_hashes(const Element& e) const
219  {
220  return {{hash_function.template operator()<0>(e) & hash_mask,
221  hash_function.template operator()<1>(e) & hash_mask,
222  hash_function.template operator()<2>(e) & hash_mask,
223  hash_function.template operator()<3>(e) & hash_mask,
224  hash_function.template operator()<4>(e) & hash_mask,
225  hash_function.template operator()<5>(e) & hash_mask,
226  hash_function.template operator()<6>(e) & hash_mask,
227  hash_function.template operator()<7>(e) & hash_mask}};
228  }
229 
230  /* end
231  * @returns a constexpr index that can never be inserted to */
232  constexpr uint32_t invalid() const
233  {
234  return ~(uint32_t)0;
235  }
236 
241  inline void allow_erase(uint32_t n) const
242  {
244  }
245 
250  inline void please_keep(uint32_t n) const
251  {
253  }
254 
264  void epoch_check()
265  {
266  if (epoch_heuristic_counter != 0) {
268  return;
269  }
270  // count the number of elements from the latest epoch which
271  // have not been erased.
272  uint32_t epoch_unused_count = 0;
273  for (uint32_t i = 0; i < size; ++i)
274  epoch_unused_count += epoch_flags[i] &&
276  // If there are more non-deleted entries in the current epoch than the
277  // epoch size, then allow_erase on all elements in the old epoch (marked
278  // false) and move all elements in the current epoch to the old epoch
279  // but do not call allow_erase on their indices.
280  if (epoch_unused_count >= epoch_size) {
281  for (uint32_t i = 0; i < size; ++i)
282  if (epoch_flags[i])
283  epoch_flags[i] = false;
284  else
285  allow_erase(i);
287  } else
288  // reset the epoch_heuristic_counter to next do a scan when worst
289  // case behavior (no intermittent erases) would exceed epoch size,
290  // with a reasonable minimum scan size.
291  // Ordinarily, we would have to sanity check std::min(epoch_size,
292  // epoch_unused_count), but we already know that `epoch_unused_count
293  // < epoch_size` in this branch
294  epoch_heuristic_counter = std::max(1u, std::max(epoch_size / 16,
295  epoch_size - epoch_unused_count));
296  }
297 
298 public:
304  {
305  }
306 
315  uint32_t setup(uint32_t new_size)
316  {
317  // depth_limit must be at least one otherwise errors can occur.
318  depth_limit = static_cast<uint8_t>(std::log2(static_cast<float>(std::max((uint32_t)2, new_size))));
319  size = 1 << depth_limit;
320  hash_mask = size-1;
321  table.resize(size);
323  epoch_flags.resize(size);
324  // Set to 45% as described above
325  epoch_size = std::max((uint32_t)1, (45 * size) / 100);
326  // Initially set to wait for a whole epoch
328  return size;
329  }
330 
343  uint32_t setup_bytes(size_t bytes)
344  {
345  return setup(bytes/sizeof(Element));
346  }
347 
368  inline void insert(Element e)
369  {
370  epoch_check();
371  uint32_t last_loc = invalid();
372  bool last_epoch = true;
373  std::array<uint32_t, 8> locs = compute_hashes(e);
374  // Make sure we have not already inserted this element
375  // If we have, make sure that it does not get deleted
376  for (uint32_t loc : locs)
377  if (table[loc] == e) {
378  please_keep(loc);
379  epoch_flags[loc] = last_epoch;
380  return;
381  }
382  for (uint8_t depth = 0; depth < depth_limit; ++depth) {
383  // First try to insert to an empty slot, if one exists
384  for (uint32_t loc : locs) {
385  if (!collection_flags.bit_is_set(loc))
386  continue;
387  table[loc] = std::move(e);
388  please_keep(loc);
389  epoch_flags[loc] = last_epoch;
390  return;
391  }
406  last_loc = locs[(1 + (std::find(locs.begin(), locs.end(), last_loc) - locs.begin())) & 7];
407  std::swap(table[last_loc], e);
408  // Can't std::swap a std::vector<bool>::reference and a bool&.
409  bool epoch = last_epoch;
410  last_epoch = epoch_flags[last_loc];
411  epoch_flags[last_loc] = epoch;
412 
413  // Recompute the locs -- unfortunately happens one too many times!
414  locs = compute_hashes(e);
415  }
416  }
417 
418  /* contains iterates through the hash locations for a given element
419  * and checks to see if it is present.
420  *
421  * contains does not check garbage collected state (in other words,
422  * garbage is only collected when the space is needed), so:
423  *
424  * insert(x);
425  * if (contains(x, true))
426  * return contains(x, false);
427  * else
428  * return true;
429  *
430  * executed on a single thread will always return true!
431  *
432  * This is a great property for re-org performance for example.
433  *
434  * contains returns a bool set true if the element was found.
435  *
436  * @param e the element to check
437  * @param erase
438  *
439  * @post if erase is true and the element is found, then the garbage collect
440  * flag is set
441  * @returns true if the element is found, false otherwise
442  */
443  inline bool contains(const Element& e, const bool erase) const
444  {
445  std::array<uint32_t, 8> locs = compute_hashes(e);
446  for (uint32_t loc : locs)
447  if (table[loc] == e) {
448  if (erase)
449  allow_erase(loc);
450  return true;
451  }
452  return false;
453  }
454 };
455 } // namespace CuckooCache
456 
457 #endif
bit_packed_atomic_flags implements a container for garbage collection flags that is only thread unsaf...
Definition: cuckoocache.h:43
void bit_set(uint32_t s)
bit_set sets an entry as discardable.
Definition: cuckoocache.h:92
void setup(uint32_t b)
setup marks all entries and ensures that bit_packed_atomic_flags can store at least size entries
Definition: cuckoocache.h:79
bit_packed_atomic_flags()=delete
No default constructor as there must be some size.
bool bit_is_set(uint32_t s) const
bit_is_set queries the table for discardability at s
Definition: cuckoocache.h:113
void bit_unset(uint32_t s)
bit_unset marks an entry as something that should not be overwritten
Definition: cuckoocache.h:103
bit_packed_atomic_flags(uint32_t size)
bit_packed_atomic_flags constructor creates memory to sufficiently keep track of garbage collection i...
Definition: cuckoocache.h:61
std::unique_ptr< std::atomic< uint8_t >[]> mem
Definition: cuckoocache.h:44
cache implements a cache with properties similar to a cuckoo-set
Definition: cuckoocache.h:161
uint32_t size
size stores the total available slots in the hash table
Definition: cuckoocache.h:167
uint8_t depth_limit
depth_limit determines how many elements insert should try to replace.
Definition: cuckoocache.h:204
std::vector< Element > table
table stores all the elements
Definition: cuckoocache.h:164
uint32_t epoch_heuristic_counter
epoch_heuristic_counter is used to determine when a epoch might be aged & an expensive scan should be...
Definition: cuckoocache.h:184
uint32_t setup(uint32_t new_size)
setup initializes the container to store no more than new_size elements.
Definition: cuckoocache.h:315
uint32_t hash_mask
hash_mask should be set to appropriately mask out a hash such that every masked hash is [0,...
Definition: cuckoocache.h:200
void epoch_check()
epoch_check handles the changing of epochs for elements stored in the cache.
Definition: cuckoocache.h:264
uint32_t epoch_size
epoch_size is set to be the number of elements supposed to be in a epoch.
Definition: cuckoocache.h:194
std::vector< bool > epoch_flags
epoch_flags tracks how recently an element was inserted into the cache.
Definition: cuckoocache.h:177
void insert(Element e)
insert loops at most depth_limit times trying to insert a hash at various locations in the table via ...
Definition: cuckoocache.h:368
bit_packed_atomic_flags collection_flags
The bit_packed_atomic_flags array is marked mutable because we want garbage collection to be allowed ...
Definition: cuckoocache.h:171
std::array< uint32_t, 8 > compute_hashes(const Element &e) const
compute_hashes is convenience for not having to write out this expression everywhere we use the hash ...
Definition: cuckoocache.h:218
uint32_t setup_bytes(size_t bytes)
setup_bytes is a convenience function which accounts for internal memory usage when deciding how many...
Definition: cuckoocache.h:343
constexpr uint32_t invalid() const
Definition: cuckoocache.h:232
const Hash hash_function
hash_function is a const instance of the hash function.
Definition: cuckoocache.h:210
void allow_erase(uint32_t n) const
allow_erase marks the element at index n as discardable.
Definition: cuckoocache.h:241
bool contains(const Element &e, const bool erase) const
Definition: cuckoocache.h:443
void please_keep(uint32_t n) const
please_keep marks the element at index n as an entry that should be kept.
Definition: cuckoocache.h:250
cache()
You must always construct a cache with some elements via a subsequent call to setup or setup_bytes,...
Definition: cuckoocache.h:302
uint256 Hash(const T1 pbegin, const T1 pend)
Compute the 256-bit hash of an object.
Definition: hash.h:70
namespace CuckooCache provides high performance cache primitives
Definition: cuckoocache.h:28