From 65429ebd0af689be2d72b9b3b4510dc10f43234f Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Thu, 2 May 2024 15:34:19 -0700 Subject: [PATCH] Implement `insert` --- VersionedMap.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 9 deletions(-) diff --git a/VersionedMap.cpp b/VersionedMap.cpp index fc3059b..1ef3a5a 100644 --- a/VersionedMap.cpp +++ b/VersionedMap.cpp @@ -59,6 +59,11 @@ void munmapSafe(void *ptr, size_t size) { namespace weaselab { +// 96 is enough for an entire search path in a tree with a size that +// overflows int. See +// https://en.wikipedia.org/wiki/Random_binary_tree#The_longest_path +constexpr int kPathLengthUpperBound = 96; + struct Entry { int64_t insertVersion; int keyLen; @@ -216,7 +221,9 @@ struct MemManager { void gc(const uint32_t *roots, int numRoots, int64_t oldestVersion) { // Calculate reachable set BitSet reachable{next}; - uint32_t stack[1000]; // Much more than bound imposed by max height of tree + // Each node has at most 3 children and nodes along the search path aren't + // in the stack, so we need 2 * kPathLengthUpperBound + uint32_t stack[2 * kPathLengthUpperBound]; int stackIndex = 0; auto tryPush = [&](uint32_t p) { if (!reachable.set(p)) { @@ -391,11 +398,37 @@ private: uint32_t end; }; +auto operator<=>(const VersionedMap::Mutation &lhs, const Node &rhs) { + int cl = std::min(lhs.param1Length, rhs.entry->keyLen); + if (cl > 0) { + int c = memcmp(lhs.param1, rhs.entry->getKey(), cl); + if (c != 0) { + return c <=> 0; + } + } + return lhs.param1Length <=> rhs.entry->keyLen; +} + +struct Finger { + void push(uint32_t node) { searchPath[searchPathSize_++] = node; } + void pop() { --searchPathSize_; } + uint32_t back() const { + assert(searchPathSize_ > 0); + return searchPath[searchPathSize_ - 1]; + } + uint32_t searchPathSize() const { return searchPathSize_; } + +private: + uint32_t searchPath[kPathLengthUpperBound]; + int searchPathSize_ = 0; +}; + struct VersionedMap::Impl { template uint32_t child(uint32_t node, bool which, int64_t at) { - static_assert(kOrder == std::memory_order_acquire || kOrder == std::memory_order_relaxed); + static_assert(kOrder == std::memory_order_acquire || + kOrder == std::memory_order_relaxed); auto &n = mm.base[node]; if (n.updated.load(kOrder) && n.updateVersion <= at && which == n.replacedPointer) { @@ -469,6 +502,60 @@ struct VersionedMap::Impl { at); } + void insert(const Mutation &m) { + Finger finger; + finger.push(latestRoot); + bool directionStack[kPathLengthUpperBound]; + int directionStackSize = 0; + + // Initialize finger to the search path of `m` + for (;;) { + auto n = finger.back(); + if (n == 0) { + break; + } + auto c = m <=> mm.base[n]; + if (c == 0) { + // No duplicates + break; + } + finger.push(child(n, c > 0, latestVersion)); + directionStack[directionStackSize++] = c > 0; + } + + // Prepare new node + uint32_t node = newNode(latestVersion, m.param1, m.param1Length, m.param2, + m.param2Length, false /* TODO set correctly */); + if (finger.back() != 0) { + auto &n = mm.base[node]; + n.pointer[0] = + child(finger.back(), false, latestVersion); + n.pointer[1] = + child(finger.back(), true, latestVersion); + } + + // Rotate and propagate up the search path + for (;;) { + if (finger.searchPathSize() == 1) { + // Made it to the root + latestRoot = node; + break; + } + finger.pop(); + auto parent = finger.back(); + const bool direction = directionStack[--directionStackSize]; + parent = update(parent, latestVersion, direction, node); + if (mm.base[node].entry->priority > mm.base[parent].entry->priority) { + rotate(parent, latestVersion, direction); + } else { + if (parent == finger.back()) { + break; + } + } + node = parent; + } + } + uint32_t newNode(int64_t version, const uint8_t *key, int keyLen, const uint8_t *val, int valLen, bool clearTo) { auto result = mm.allocate(); @@ -513,6 +600,9 @@ struct VersionedMap::Impl { MemManager mm; RootSet roots; + // Only meaningful within the callstack of `addMutations` + uint32_t latestRoot; + int64_t latestVersion = 0; }; } // namespace weaselab @@ -523,19 +613,21 @@ int main() { { weaselab::VersionedMap::Impl impl; - impl.roots.add(impl.newNode(1, (const uint8_t *)"a", 1, nullptr, 0, true), - 1); - impl.roots.add(impl.newNode(2, (const uint8_t *)"b", 1, nullptr, -1, false), - 2); - impl.roots.add(impl.newNode(3, (const uint8_t *)"c", 1, nullptr, -1, false), - 3); + impl.latestRoot = impl.roots.roots()[impl.roots.rootCount() - 1]; + impl.latestVersion = 1; + impl.insert(weaselab::VersionedMap::Mutation{ + (const uint8_t *)"c", nullptr, 1, 0, weaselab::VersionedMap::Set}); + impl.insert(weaselab::VersionedMap::Mutation{ + (const uint8_t *)"b", nullptr, 1, 0, weaselab::VersionedMap::Set}); + impl.insert(weaselab::VersionedMap::Mutation{ + (const uint8_t *)"a", nullptr, 1, 0, weaselab::VersionedMap::Set}); + impl.roots.add(impl.latestRoot, impl.latestVersion); impl.printInOrder(0); impl.printInOrder(1); impl.printInOrder(2); impl.printInOrder(3); impl.setOldestVersion(3); } - return 0; ankerl::nanobench::Bench bench; bench.minEpochIterations(5000);