From 262c9cd10c5b3fbd6d9914c06e13be282fcae204 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 22 May 2024 16:04:01 -0700 Subject: [PATCH] Scan and remove old entries in addMutations --- Bench.cpp | 5 ++- VersionedMap.cpp | 98 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 77 insertions(+), 26 deletions(-) diff --git a/Bench.cpp b/Bench.cpp index 6191aba..49b11c0 100644 --- a/Bench.cpp +++ b/Bench.cpp @@ -7,6 +7,8 @@ void monotonicallyIncreasing() { ankerl::nanobench::Bench bench; Facade facade{0}; + bench.minEpochIterations(1000); + bench.warmup(kWindow).run("monotonically increasing", [&] { const int64_t remove = __builtin_bswap64(facade.getVersion() - kWindow); const int64_t next = __builtin_bswap64(facade.getVersion()); @@ -18,6 +20,8 @@ void monotonicallyIncreasing() { }; facade.addMutations(mutations, sizeof(mutations) / sizeof(mutations[0]), facade.getVersion() + 1); + facade.setOldestVersion( + std::max(0, facade.getVersion() - kWindow)); }); const auto v = facade.getVersion() - kWindow / 2; @@ -25,7 +29,6 @@ void monotonicallyIncreasing() { const auto end = facade.versioned.end(v); auto iter = begin; - // Slow because all the clears from old versions are still around. bench.run("scan", [&] { for (iter = begin; iter != end; ++iter) { } diff --git a/VersionedMap.cpp b/VersionedMap.cpp index fb2132f..6e350f3 100644 --- a/VersionedMap.cpp +++ b/VersionedMap.cpp @@ -703,21 +703,10 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { } } - // Removes `finger` from the tree, and leaves `finger` pointing to the next - // entry. + // Removes `finger` from the tree, and leaves `finger` pointing to insertion + // point of its former entry. void remove(Finger &finger) { -#ifndef NDEBUG - Entry *expected; - { - Finger copy; - finger.copyTo(copy); - move(copy, latestVersion, true); - expected = - copy.searchPathSize() > 0 ? mm.base[copy.backNode()].entry : nullptr; - } -#endif - // True if finger is pointing to an entry > than the entry we're removing // after we rotate it down @@ -762,17 +751,6 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { node = parent; } finger.setSearchPathSizeUnsafe(oldSize); - - // finger now points to the insertion point of the node we're removing - move(finger, latestVersion, true); - -#ifndef NDEBUG - if (finger.searchPathSize() > 0) { - assert(mm.base[finger.backNode()].entry == expected); - } else { - assert(expected == nullptr); - } -#endif } uint32_t newNode(int64_t version, int64_t rangeVersion, const uint8_t *key, @@ -803,13 +781,78 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { void printInOrderHelper(int64_t version, uint32_t node, int depth); + void scanAndRemoveOldEntries(int fuel) { + // Get a finger to the first entry > continueKey, or the last entry if + // continueKey is the empty string + if (latestRoot == 0) { + // Tree is empty + return; + } + Finger finger; + if (continueKey.len == 0) { + // Set finger to last entry in tree + bool ignored; + finger.push(latestRoot, ignored); + uint32_t c; + while ((c = child(finger.backNode(), true, + latestVersion)) != 0) { + finger.push(c, true); + } + } else { + search(continueKey, latestRoot, latestVersion, + finger); + move(finger, latestVersion, true); + if (finger.searchPathSize() == 0) { + continueKey = {nullptr, 0}; + return; + } + } + assert(finger.backNode() != 0); + int64_t rangeVersion = mm.base[finger.backNode()].entry->rangeVersion; + + move(finger, latestVersion, false); + if (finger.searchPathSize() == 0) { + continueKey = {nullptr, 0}; + return; + } + + // Phew. Ok now we have a finger to the next entry to consider removing, and + // the range version terminated at this entry. + for (; fuel > 0; --fuel) { + const auto &n = mm.base[finger.backNode()]; + if (rangeVersion < 0 && std::max(n.entry->pointVersion, + n.entry->rangeVersion) < oldestVersion) { + remove(finger); + if (latestRoot == 0) { + return; + } + } else { + rangeVersion = n.entry->rangeVersion; + } + move(finger, latestVersion, false); + if (finger.searchPathSize() == 0) { + continueKey = {nullptr, 0}; + return; + } + } + + continueArena = Arena(); + const auto &n = mm.base[finger.backNode()]; + uint8_t *data = new (continueArena) uint8_t[n.entry->keyLen]; + memcpy(data, n.entry->getKey(), n.entry->keyLen); + continueKey = {data, n.entry->keyLen}; + } + void addMutations(const Mutation *mutations, int numMutations, int64_t version) { mallocBytesDelta = 0; - // TODO scan to remove mutations older than oldestVersion assert(latestVersion < version); latestVersion = version; latestRoot = roots.roots()[roots.rootCount() - 1]; + + // TODO tune? + scanAndRemoveOldEntries(2 * numMutations + 10); + // TODO Improve ILP? for (int i = 0; i < numMutations; ++i) { const auto &m = mutations[i]; @@ -827,6 +870,7 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { while (iter.searchPathSize() > 0 && mm.base[iter.backNode()] < Key{m.param2, m.param2Len}) { remove(iter); + move(iter, latestVersion, true); } insert({m.param2, m.param2Len}, {}); } @@ -843,6 +887,10 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { void firstGeq(const Key *key, const int64_t *version, Iterator *iterator, int count) const; + // State used to resume scanning and removing old entries in `addMutations` + Key continueKey; + Arena continueArena; + MemManager mm; RootSet roots; // Only meaningful within the callstack of `addMutations`