From 887f400a479aca7220b712e6eee90eb93d2a3251 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 29 May 2024 17:45:23 -0700 Subject: [PATCH] Expand clears --- VersionedMap.cpp | 115 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 25 deletions(-) diff --git a/VersionedMap.cpp b/VersionedMap.cpp index d1b3e30..fc16961 100644 --- a/VersionedMap.cpp +++ b/VersionedMap.cpp @@ -75,11 +75,12 @@ namespace weaselab { constexpr int kPathLengthUpperBound = 96; struct Entry { - // If there is a point mutation at key, then pointVersion is its version. - // Otherwise it's negative. + // If there is a point mutation at key, then pointVersion is >= 0 and key has + // not been modified since pointVersion. Otherwise it's negative. int64_t pointVersion; - // If there is a range mutation ending at key, then rangeVersion is its - // version. Otherwise it's negative. + // If there is a range mutation ending at key, then rangeVersion is >= 0 and + // the range has not been modified since rangeVersion. Otherwise it's + // negative. int64_t rangeVersion; int keyLen; // Negative if this key is cleared. Only meaningful if this is a point @@ -91,6 +92,7 @@ struct Entry { // True if the entry is a point mutation. If false, this entry's key should be // read through to the underlying data structure. bool pointMutation() const { return pointVersion >= 0; } + bool pointSet() const { return pointVersion >= 0 && valLen >= 0; } bool pointClear() const { return pointVersion >= 0 && valLen < 0; } // True if mutations in (pred, this) are cleared. If false, (pred, this) @@ -388,6 +390,18 @@ auto operator<=>(const VersionedMap::Key &lhs, const Node &rhs) { return lhs.len <=> rhs.entry->keyLen; } +auto operator<=>(const weaselab::VersionedMap::Key &lhs, + const weaselab::VersionedMap::Key &rhs) { + int cl = std::min(lhs.len, rhs.len); + if (cl > 0) { + int c = memcmp(lhs.p, rhs.p, cl); + if (c != 0) { + return c <=> 0; + } + } + return lhs.len <=> rhs.len; +} + constexpr int orderToInt(std::strong_ordering o) { return o == std::strong_ordering::less ? -1 : o == std::strong_ordering::equal ? 0 @@ -883,10 +897,38 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { insert({m.param1, m.param1Len}, {{m.param2, m.param2Len}}, iter); } break; case Clear: { - search({m.param1, m.param1Len}, latestRoot, - latestVersion, iter); - insert({m.param1, m.param1Len}, {{nullptr, -1}}, iter); - if (m.param2Len > 0) { + // TODO we can avoid some insertions here. Complexity is getting out of + // hand though. + + if (m.param2Len == 0) { + search({m.param1, m.param1Len}, latestRoot, + latestVersion, iter); + insert({m.param1, m.param1Len}, {{nullptr, -1}}, iter); + + const bool engulfLeft = mm.base[iter.backNode()].entry->clearTo(); + move(iter, latestVersion); + const auto *next = iter.searchPathSize() > 0 + ? mm.base[iter.backNode()].entry + : nullptr; + if (engulfLeft && next && next->clearTo()) { + insert({next->getKey(), next->keyLen}, {}, iter); + move(iter, latestVersion); + remove(iter); + } + + } else { + search({m.param1, m.param1Len}, latestRoot, + latestVersion, iter); + insert({m.param1, m.param1Len}, {{nullptr, -1}}, iter); + + // Check if we can engulf on the left + { + const auto *entry = mm.base[iter.backNode()].entry; + if (entry->clearTo()) { + remove(iter); + } + } + move(iter, latestVersion); while (iter.searchPathSize() > 0 && mm.base[iter.backNode()] < Key{m.param2, m.param2Len}) { @@ -898,6 +940,20 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { search({m.param2, m.param2Len}, latestRoot, latestVersion, iter); insert({m.param2, m.param2Len}, {}, iter); + + // Check if we can engulf on the right + { + const auto *entry = mm.base[iter.backNode()].entry; + move(iter, latestVersion); + const auto *next = iter.searchPathSize() > 0 + ? mm.base[iter.backNode()].entry + : nullptr; + if (entry->pointClear() && next && next->clearTo()) { + insert({next->getKey(), next->keyLen}, {}, iter); + move(iter, latestVersion); + remove(iter); + } + } } } break; default: // GCOVR_EXCL_LINE @@ -1066,17 +1122,14 @@ void materializeMutations(VersionedMap::Iterator::Impl *impl, const Entry *prev, impl->mutations[impl->mutationCount++] = { prev->getKey(), entry.getKey(), - prev->pointClear() && prev->pointVersion == entry.rangeVersion - ? prev->keyLen - : prev->keyLen + 1, + prev->pointClear() ? prev->keyLen : prev->keyLen + 1, entry.keyLen, VersionedMap::Clear, entry.rangeVersion}; } if (entry.pointMutation()) { if (entry.valLen < 0 /* pointClear */) { - if (next == nullptr || - !(next->clearTo() && next->rangeVersion == entry.pointVersion)) { + if (next == nullptr || !next->clearTo()) { impl->mutations[impl->mutationCount++] = { entry.getKey(), nullptr, entry.keyLen, 0, VersionedMap::Clear, entry.pointVersion}; @@ -1176,8 +1229,18 @@ bool VersionedMap::Iterator::operator==(const Iterator &other) const { return impl->equals(*other.impl); } -void VersionedMap::Impl::firstGeq(const Key *key, const int64_t *version, - Iterator *iterator, int count) const { +bool geq(const VersionedMap::Iterator::VersionedMutation &m, + const VersionedMap::Key &k) { + if (m.type == VersionedMap::Set || m.param2Len == 0) { + return VersionedMap::Key{m.param1, m.param1Len} >= k; + } else { + return VersionedMap::Key{m.param2, m.param2Len} > k; + } +} + +void VersionedMap::Impl::firstGeq(const weaselab::VersionedMap::Key *key, + const int64_t *version, Iterator *iterator, + int count) const { // TODO ILP! auto handle = roots.getThreadSafeHandle(); for (int i = 0; i < count; ++i) { @@ -1197,17 +1260,11 @@ void VersionedMap::Impl::firstGeq(const Key *key, const int64_t *version, Finger &finger = iterator[i].impl->finger; search(key[i], root, version[i], finger); - bool exact; - if (finger.searchPathSize() == 0) { - exact = false; - } else if (finger.backNode() == 0) { - exact = false; + if (finger.searchPathSize() > 0 && finger.backNode() == 0) { move(finger, version[i]); if (finger.searchPathSize() > 0) { assert(finger.backNode() != 0); } - } else { - exact = true; } iterator[i].impl->version = version[i]; @@ -1227,10 +1284,18 @@ void VersionedMap::Impl::firstGeq(const Key *key, const int64_t *version, iterator[i].impl->map->move( finger, iterator[i].impl->version); } - if (exact) { - iterator[i].impl->mutationIndex = iterator[i].impl->mutationCount - 1; + if (iterator[i].impl->mutationCount == 0) { + assert(finger.searchPathSize() == 0); } else { - iterator[i].impl->mutationIndex = 0; + [[maybe_unused]] bool match = false; + for (int j = 0; j < iterator[i].impl->mutationCount; ++j) { + if (geq(iterator[i].impl->mutations[j], key[i])) { + iterator[i].impl->mutationIndex = j; + match = true; + break; + } + } + assert(match); } } }