Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2016 Jeremy Rubin2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#ifndef BITCOIN_CUCKOOCACHE_H6#define BITCOIN_CUCKOOCACHE_H8#include <util/fastrange.h>9#include <util/overflow.h>11#include <algorithm>12#include <array>13#include <atomic>14#include <cmath>15#include <cstring>16#include <limits>17#include <memory>18#include <utility>19#include <vector>22/** High-performance cache primitives.23*24* Summary:25*26* 1. @ref bit_packed_atomic_flags is bit-packed atomic flags for garbage collection27*28* 2. @ref cache is a cache which is performant in memory usage and lookup speed. It29* is lockfree for erase operations. Elements are lazily erased on the next insert.30*/31namespace CuckooCache32{33/** @ref bit_packed_atomic_flags implements a container for garbage collection flags34* that is only thread unsafe on calls to setup. This class bit-packs collection35* flags for memory efficiency.36*37* All operations are `std::memory_order_relaxed` so external mechanisms must38* ensure that writes and reads are properly synchronized.39*40* On setup(n), all bits up to `n` are marked as collected.41*42* Under the hood, because it is an 8-bit type, it makes sense to use a multiple43* of 8 for setup, but it will be safe if that is not the case as well.44*/45class bit_packed_atomic_flags46{47std::unique_ptr<std::atomic<uint8_t>[]> mem;49public:50/** No default constructor, as there must be some size. */51bit_packed_atomic_flags() = delete;53/**54* bit_packed_atomic_flags constructor creates memory to sufficiently55* keep track of garbage collection information for `size` entries.56*57* @param size the number of elements to allocate space for58*59* @post bit_set, bit_unset, and bit_is_set function properly forall x. x <60* size61* @post All calls to bit_is_set (without subsequent bit_unset) will return62* true.63*/64explicit bit_packed_atomic_flags(uint32_t size)65{66// pad out the size if needed67size = CeilDiv(size, 8u);68mem.reset(new std::atomic<uint8_t>[size]);69for (uint32_t i = 0; i < size; ++i)70mem[i].store(0xFF);71};73/** setup marks all entries and ensures that bit_packed_atomic_flags can store74* at least `b` entries.75*76* @param b the number of elements to allocate space for77* @post bit_set, bit_unset, and bit_is_set function properly forall x. x <78* b79* @post All calls to bit_is_set (without subsequent bit_unset) will return80* true.81*/82inline void setup(uint32_t b)83{84bit_packed_atomic_flags d(b);85std::swap(mem, d.mem);86}88/** bit_set sets an entry as discardable.89*90* @param s the index of the entry to bit_set91* @post immediately subsequent call (assuming proper external memory92* ordering) to bit_is_set(s) == true.93*/94inline void bit_set(uint32_t s)95{96mem[s >> 3].fetch_or(uint8_t(1 << (s & 7)), std::memory_order_relaxed);97}99/** bit_unset marks an entry as something that should not be overwritten.100*101* @param s the index of the entry to bit_unset102* @post immediately subsequent call (assuming proper external memory103* ordering) to bit_is_set(s) == false.104*/105inline void bit_unset(uint32_t s)106{107mem[s >> 3].fetch_and(uint8_t(~(1 << (s & 7))), std::memory_order_relaxed);108}110/** bit_is_set queries the table for discardability at `s`.111*112* @param s the index of the entry to read113* @returns true if the bit at index `s` was set, false otherwise114* */115inline bool bit_is_set(uint32_t s) const116{117return (1 << (s & 7)) & mem[s >> 3].load(std::memory_order_relaxed);118}119};121/** @ref cache implements a cache with properties similar to a cuckoo-set.122*123* The cache is able to hold up to `(~(uint32_t)0) - 1` elements.124*125* Read Operations:126* - contains() for `erase=false`127*128* Read+Erase Operations:129* - contains() for `erase=true`130*131* Erase Operations:132* - allow_erase()133*134* Write Operations:135* - setup()136* - setup_bytes()137* - insert()138* - please_keep()139*140* Synchronization Free Operations:141* - invalid()142* - compute_hashes()143*144* User Must Guarantee:145*146* 1. Write requires synchronized access (e.g. a lock)147* 2. Read requires no concurrent Write, synchronized with last insert.148* 3. Erase requires no concurrent Write, synchronized with last insert.149* 4. An Erase caller must release all memory before allowing a new Writer.150*151*152* Note on function names:153* - The name "allow_erase" is used because the real discard happens later.154* - The name "please_keep" is used because elements may be erased anyways on insert.155*156* @tparam Element should be a movable and copyable type157* @tparam Hash should be a function/callable which takes a template parameter158* hash_select and an Element and extracts a hash from it. Should return159* high-entropy uint32_t hashes for `Hash h; h<0>(e) ... h<7>(e)`.160*/161template <typename Element, typename Hash>162class cache163{164private:165/** table stores all the elements */166std::vector<Element> table;168/** size stores the total available slots in the hash table */169uint32_t size{0};171/** The bit_packed_atomic_flags array is marked mutable because we want172* garbage collection to be allowed to occur from const methods */173mutable bit_packed_atomic_flags collection_flags;175/** epoch_flags tracks how recently an element was inserted into176* the cache. true denotes recent, false denotes not-recent. See insert()177* method for full semantics.178*/179mutable std::vector<bool> epoch_flags;181/** epoch_heuristic_counter is used to determine when an epoch might be aged182* & an expensive scan should be done. epoch_heuristic_counter is183* decremented on insert and reset to the new number of inserts which would184* cause the epoch to reach epoch_size when it reaches zero.185*/186uint32_t epoch_heuristic_counter{0};188/** epoch_size is set to be the number of elements supposed to be in a189* epoch. When the number of non-erased elements in an epoch190* exceeds epoch_size, a new epoch should be started and all191* current entries demoted. epoch_size is set to be 45% of size because192* we want to keep load around 90%, and we support 3 epochs at once --193* one "dead" which has been erased, one "dying" which has been marked to be194* erased next, and one "living" which new inserts add to.195*/196uint32_t epoch_size{0};198/** depth_limit determines how many elements insert should try to replace.199* Should be set to log2(n).200*/201uint8_t depth_limit{0};203/** hash_function is a const instance of the hash function. It cannot be204* static or initialized at call time as it may have internal state (such as205* a nonce).206*/207const Hash hash_function;209/** compute_hashes is convenience for not having to write out this210* expression everywhere we use the hash values of an Element.211*212* We need to map the 32-bit input hash onto a hash bucket in a range [0, size) in a213* manner which preserves as much of the hash's uniformity as possible. Ideally214* this would be done by bitmasking but the size is usually not a power of two.215*216* The naive approach would be to use a mod -- which isn't perfectly uniform but so217* long as the hash is much larger than size it is not that bad. Unfortunately,218* mod/division is fairly slow on ordinary microprocessors (e.g. 90-ish cycles on219* haswell, ARM doesn't even have an instruction for it.); when the divisor is a220* constant the compiler will do clever tricks to turn it into a multiply+add+shift,221* but size is a run-time value so the compiler can't do that here.222*223* One option would be to implement the same trick the compiler uses and compute the224* constants for exact division based on the size, as described in "{N}-bit Unsigned225* Division via {N}-bit Multiply-Add" by Arch D. Robison in 2005. But that code is226* somewhat complicated and the result is still slower than an even simpler option:227* see the FastRange32 function in util/fastrange.h.228*229* The resulting non-uniformity is also more equally distributed which would be230* advantageous for something like linear probing, though it shouldn't matter231* one way or the other for a cuckoo table.232*233* The primary disadvantage of this approach is increased intermediate precision is234* required but for a 32-bit random number we only need the high 32 bits of a235* 32*32->64 multiply, which means the operation is reasonably fast even on a236* typical 32-bit processor.237*238* @param e The element whose hashes will be returned239* @returns Deterministic hashes derived from `e` uniformly mapped onto the range [0, size)240*/241inline std::array<uint32_t, 8> compute_hashes(const Element& e) const242{243return {{FastRange32(hash_function.template operator()<0>(e), size),244FastRange32(hash_function.template operator()<1>(e), size),245FastRange32(hash_function.template operator()<2>(e), size),246FastRange32(hash_function.template operator()<3>(e), size),247FastRange32(hash_function.template operator()<4>(e), size),248FastRange32(hash_function.template operator()<5>(e), size),249FastRange32(hash_function.template operator()<6>(e), size),250FastRange32(hash_function.template operator()<7>(e), size)}};
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.