From 0367ba9856397185440493d2bca47bac21c10282 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Sun, 30 Jun 2024 20:33:54 -0700 Subject: [PATCH] Fast path for prefix reads --- ConflictSet.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/ConflictSet.cpp b/ConflictSet.cpp index 98555c2..6395271 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -1708,6 +1708,83 @@ downLeftSpine: } } +// Logically this is the same as performing firstGeq and then checking against +// max version or range version if this prefix doesn't exist, but this version +// short circuits as soon as it can prove that there's no conflict. +bool checkPrefixRead(Node *n, const std::span key, + InternalVersionT readVersion, ConflictSet::Impl *impl) { +#if DEBUG_VERBOSE && !defined(NDEBUG) + fprintf(stderr, "Check prefix read: %s\n", printable(key).c_str()); +#endif + auto remaining = key; + for (;;) { + auto m = maxVersion(n, impl); + if (remaining.size() == 0) { + return m <= readVersion; + } + + if (m <= readVersion) { + return true; + } + + auto *child = getChild(n, remaining[0]); + if (child == nullptr) { + int c = getChildGeq(n, remaining[0]); + if (c >= 0) { + n = getChildExists(n, c); + goto downLeftSpine; + } else { + n = nextSibling(n); + if (n == nullptr) { + return true; + } + goto downLeftSpine; + } + } + + n = child; + remaining = remaining.subspan(1, remaining.size() - 1); + + if (n->partialKeyLen > 0) { + int commonLen = std::min(n->partialKeyLen, remaining.size()); + int i = longestCommonPrefix(n->partialKey(), remaining.data(), commonLen); + if (i < commonLen) { + auto c = n->partialKey()[i] <=> remaining[i]; + if (c > 0) { + goto downLeftSpine; + } else { + n = nextSibling(n); + if (n == nullptr) { + return true; + } + goto downLeftSpine; + } + } + if (commonLen == n->partialKeyLen) { + // partial key matches + remaining = remaining.subspan(commonLen, remaining.size() - commonLen); + } else if (n->partialKeyLen > int(remaining.size())) { + // n is the first physical node greater than remaining, and there's no + // eq node. All physical nodes that start with prefix are reachable from + // n. + if (maxVersion(n, impl) > readVersion) { + return false; + } + goto downLeftSpine; + } + } + } +downLeftSpine: + for (;;) { + if (n->entryPresent) { + return n->entry.rangeVersion <= readVersion; + } + int c = getChildGeq(n, 0); + assert(c >= 0); + n = getChildExists(n, c); + } +} + #ifdef HAS_AVX uint32_t compare16_32bit(const InternalVersionT *vs, InternalVersionT rv) { uint32_t compared = 0; @@ -2367,6 +2444,10 @@ bool checkRangeReadImpl(Node *n, std::span begin, end.back() == 0) { return checkPointRead(n, begin, readVersion, impl); } + if (lcp == int(begin.size() - 1) && end.size() == begin.size() && + int(begin.back()) + 1 == int(end.back())) { + return checkPrefixRead(n, begin, readVersion, impl); + } SearchStepWise search{n, begin.subspan(0, lcp)}; Arena arena;