Compare commits
9 Commits
01488880ef
...
e59fee39c7
| Author | SHA1 | Date | |
|---|---|---|---|
| e59fee39c7 | |||
| 3e2c8310bb | |||
| 8264f1342d | |||
| 5d7e9c6f85 | |||
| cdf42fcb34 | |||
| cbe40b5dba | |||
| a04e81b3ff | |||
| 0be97a34b6 | |||
| 68ab9a9f08 |
@@ -59,10 +59,6 @@ cmake_pop_check_state()
|
|||||||
|
|
||||||
option(USE_SIMD_FALLBACK
|
option(USE_SIMD_FALLBACK
|
||||||
"Use fallback implementations of functions that use SIMD" OFF)
|
"Use fallback implementations of functions that use SIMD" OFF)
|
||||||
option(
|
|
||||||
USE_32_BIT_VERSIONS
|
|
||||||
"Store 32 bit versions internally, and rely on versions never being different by more than 2e9"
|
|
||||||
OFF)
|
|
||||||
|
|
||||||
# This is encouraged according to
|
# This is encouraged according to
|
||||||
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
||||||
@@ -107,10 +103,6 @@ if(NOT USE_SIMD_FALLBACK)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_32_BIT_VERSIONS)
|
|
||||||
add_compile_definitions(INTERNAL_VERSION_32_BIT=1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
|
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
|
||||||
|
|
||||||
add_library(${PROJECT_NAME}-object OBJECT ConflictSet.cpp)
|
add_library(${PROJECT_NAME}-object OBJECT ConflictSet.cpp)
|
||||||
|
|||||||
+261
-108
@@ -78,18 +78,18 @@ constexpr void removeKey(struct Node *) {}
|
|||||||
|
|
||||||
// ==================== BEGIN IMPLEMENTATION ====================
|
// ==================== BEGIN IMPLEMENTATION ====================
|
||||||
|
|
||||||
#ifndef INTERNAL_VERSION_32_BIT
|
constexpr int64_t kNominalVersionWindow = 2e9;
|
||||||
#define INTERNAL_VERSION_32_BIT 0
|
constexpr int64_t kMaxCorrectVersionWindow =
|
||||||
#endif
|
std::numeric_limits<int32_t>::max();
|
||||||
|
static_assert(kNominalVersionWindow <= kMaxCorrectVersionWindow);
|
||||||
|
|
||||||
#if INTERNAL_VERSION_32_BIT
|
|
||||||
struct InternalVersionT {
|
struct InternalVersionT {
|
||||||
constexpr InternalVersionT() = default;
|
constexpr InternalVersionT() = default;
|
||||||
constexpr explicit InternalVersionT(int64_t value) : value(value) {}
|
constexpr explicit InternalVersionT(int64_t value) : value(value) {}
|
||||||
constexpr int64_t toInt64() const { return value; } // GCOVR_EXCL_LINE
|
constexpr int64_t toInt64() const { return value; } // GCOVR_EXCL_LINE
|
||||||
constexpr auto operator<=>(const InternalVersionT &rhs) const {
|
constexpr auto operator<=>(const InternalVersionT &rhs) const {
|
||||||
// Maintains ordering after overflow, as long as the full-precision versions
|
// Maintains ordering after overflow, as long as the full-precision versions
|
||||||
// are within ~2e9 of eachother.
|
// are within `kMaxCorrectVersionWindow` of eachother.
|
||||||
return int32_t(value - rhs.value) <=> 0;
|
return int32_t(value - rhs.value) <=> 0;
|
||||||
}
|
}
|
||||||
constexpr bool operator==(const InternalVersionT &) const = default;
|
constexpr bool operator==(const InternalVersionT &) const = default;
|
||||||
@@ -99,20 +99,6 @@ private:
|
|||||||
uint32_t value;
|
uint32_t value;
|
||||||
};
|
};
|
||||||
thread_local InternalVersionT InternalVersionT::zero;
|
thread_local InternalVersionT InternalVersionT::zero;
|
||||||
#else
|
|
||||||
struct InternalVersionT {
|
|
||||||
constexpr InternalVersionT() = default;
|
|
||||||
constexpr explicit InternalVersionT(int64_t value) : value(value) {}
|
|
||||||
constexpr int64_t toInt64() const { return value; } // GCOVR_EXCL_LINE
|
|
||||||
constexpr auto operator<=>(const InternalVersionT &rhs) const = default;
|
|
||||||
constexpr bool operator==(const InternalVersionT &) const = default;
|
|
||||||
static const InternalVersionT zero;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int64_t value;
|
|
||||||
};
|
|
||||||
const InternalVersionT InternalVersionT::zero{0};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Entry {
|
struct Entry {
|
||||||
InternalVersionT pointVersion;
|
InternalVersionT pointVersion;
|
||||||
@@ -527,13 +513,8 @@ std::string getSearchPath(Node *n);
|
|||||||
// Each node with an entry present gets a budget of kBytesPerKey. Node0 always
|
// Each node with an entry present gets a budget of kBytesPerKey. Node0 always
|
||||||
// has an entry present.
|
// has an entry present.
|
||||||
// Induction hypothesis is that each node's surplus is >= kMinNodeSurplus
|
// Induction hypothesis is that each node's surplus is >= kMinNodeSurplus
|
||||||
#if INTERNAL_VERSION_32_BIT
|
|
||||||
constexpr int kBytesPerKey = 112;
|
constexpr int kBytesPerKey = 112;
|
||||||
constexpr int kMinNodeSurplus = 80;
|
constexpr int kMinNodeSurplus = 80;
|
||||||
#else
|
|
||||||
constexpr int kBytesPerKey = 144;
|
|
||||||
constexpr int kMinNodeSurplus = 104;
|
|
||||||
#endif
|
|
||||||
constexpr int kMinChildrenNode3 = 2;
|
constexpr int kMinChildrenNode3 = 2;
|
||||||
constexpr int kMinChildrenNode16 = 4;
|
constexpr int kMinChildrenNode16 = 4;
|
||||||
constexpr int kMinChildrenNode48 = 17;
|
constexpr int kMinChildrenNode48 = 17;
|
||||||
@@ -1230,6 +1211,53 @@ void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
|
|||||||
freeAndMakeCapacityAtLeast(self, maxCapacity, allocators, impl, false);
|
freeAndMakeCapacityAtLeast(self, maxCapacity, allocators, impl, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rezero(Node *n, InternalVersionT z) {
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
fprintf(stderr, "rezero to %" PRId64 ": %s\n", z.toInt64(),
|
||||||
|
getSearchPathPrintable(n).c_str());
|
||||||
|
#endif
|
||||||
|
if (n->entryPresent) {
|
||||||
|
n->entry.pointVersion = std::max(n->entry.pointVersion, z);
|
||||||
|
n->entry.rangeVersion = std::max(n->entry.rangeVersion, z);
|
||||||
|
}
|
||||||
|
switch (n->getType()) {
|
||||||
|
case Type_Node0: {
|
||||||
|
} break;
|
||||||
|
case Type_Node3: {
|
||||||
|
auto *self = static_cast<Node3 *>(n);
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
self->childMaxVersion[i] = std::max(self->childMaxVersion[i], z);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type_Node16: {
|
||||||
|
auto *self = static_cast<Node16 *>(n);
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
self->childMaxVersion[i] = std::max(self->childMaxVersion[i], z);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type_Node48: {
|
||||||
|
auto *self = static_cast<Node48 *>(n);
|
||||||
|
for (int i = 0; i < 48; ++i) {
|
||||||
|
self->childMaxVersion[i] = std::max(self->childMaxVersion[i], z);
|
||||||
|
}
|
||||||
|
for (auto &m : self->maxOfMax) {
|
||||||
|
m = std::max(m, z);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type_Node256: {
|
||||||
|
auto *self = static_cast<Node256 *>(n);
|
||||||
|
for (int i = 0; i < 256; ++i) {
|
||||||
|
self->childMaxVersion[i] = std::max(self->childMaxVersion[i], z);
|
||||||
|
}
|
||||||
|
for (auto &m : self->maxOfMax) {
|
||||||
|
m = std::max(m, z);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default: // GCOVR_EXCL_LINE
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void maybeDownsize(Node *self, NodeAllocators *allocators,
|
void maybeDownsize(Node *self, NodeAllocators *allocators,
|
||||||
ConflictSet::Impl *impl, Node *&dontInvalidate) {
|
ConflictSet::Impl *impl, Node *&dontInvalidate) {
|
||||||
|
|
||||||
@@ -1281,6 +1309,9 @@ void maybeDownsize(Node *self, NodeAllocators *allocators,
|
|||||||
// Max versions are stored in the parent, so we need to update it now
|
// Max versions are stored in the parent, so we need to update it now
|
||||||
// that we have a new parent.
|
// that we have a new parent.
|
||||||
setMaxVersion(child, impl, childMaxVersion);
|
setMaxVersion(child, impl, childMaxVersion);
|
||||||
|
if (child->parent) {
|
||||||
|
rezero(child->parent, InternalVersionT::zero);
|
||||||
|
}
|
||||||
|
|
||||||
getInTree(self, impl) = child;
|
getInTree(self, impl) = child;
|
||||||
allocators->node3.release(self3);
|
allocators->node3.release(self3);
|
||||||
@@ -1323,7 +1354,7 @@ void maybeDownsize(Node *self, NodeAllocators *allocators,
|
|||||||
// the node after self. If erase invalidates the pointee of `dontInvalidate`, it
|
// the node after self. If erase invalidates the pointee of `dontInvalidate`, it
|
||||||
// will update it to its new pointee as well.
|
// will update it to its new pointee as well.
|
||||||
Node *erase(Node *self, NodeAllocators *allocators, ConflictSet::Impl *impl,
|
Node *erase(Node *self, NodeAllocators *allocators, ConflictSet::Impl *impl,
|
||||||
Node *&dontInvalidate) {
|
bool logical, Node *&dontInvalidate) {
|
||||||
assert(self->parent != nullptr);
|
assert(self->parent != nullptr);
|
||||||
|
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
@@ -1333,7 +1364,7 @@ Node *erase(Node *self, NodeAllocators *allocators, ConflictSet::Impl *impl,
|
|||||||
Node *parent = self->parent;
|
Node *parent = self->parent;
|
||||||
uint8_t parentsIndex = self->parentsIndex;
|
uint8_t parentsIndex = self->parentsIndex;
|
||||||
|
|
||||||
auto *result = nextLogical(self);
|
auto *result = logical ? nextLogical(self) : nextPhysical(self);
|
||||||
|
|
||||||
removeKey(self);
|
removeKey(self);
|
||||||
self->entryPresent = false;
|
self->entryPresent = false;
|
||||||
@@ -1833,7 +1864,6 @@ bool scan16(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
|
|||||||
uint64_t mask = vget_lane_u64(
|
uint64_t mask = vget_lane_u64(
|
||||||
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(results), 4)), 0);
|
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(results), 4)), 0);
|
||||||
|
|
||||||
#if INTERNAL_VERSION_32_BIT
|
|
||||||
uint32x4_t w4[4];
|
uint32x4_t w4[4];
|
||||||
memcpy(w4, vs, sizeof(w4));
|
memcpy(w4, vs, sizeof(w4));
|
||||||
uint32_t rv;
|
uint32_t rv;
|
||||||
@@ -1853,12 +1883,6 @@ bool scan16(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
|
|||||||
|
|
||||||
uint64_t compared = vget_lane_u64(
|
uint64_t compared = vget_lane_u64(
|
||||||
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(combined), 4)), 0);
|
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(combined), 4)), 0);
|
||||||
#else
|
|
||||||
uint64_t compared = 0;
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
compared |= uint64_t(vs[i] > readVersion) << (i << 2);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return !(compared & mask);
|
return !(compared & mask);
|
||||||
|
|
||||||
@@ -1871,17 +1895,11 @@ bool scan16(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
|
|||||||
indices, _mm_max_epu8(indices, _mm_set1_epi8(end - begin))));
|
indices, _mm_max_epu8(indices, _mm_set1_epi8(end - begin))));
|
||||||
|
|
||||||
uint32_t compared = 0;
|
uint32_t compared = 0;
|
||||||
#if INTERNAL_VERSION_32_BIT
|
|
||||||
if constexpr (kAVX512) {
|
if constexpr (kAVX512) {
|
||||||
compared = compare16_32bit_avx512(vs, readVersion);
|
compared = compare16_32bit_avx512(vs, readVersion);
|
||||||
} else {
|
} else {
|
||||||
compared = compare16_32bit(vs, readVersion);
|
compared = compare16_32bit(vs, readVersion);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
compared |= (vs[i] > readVersion) << i;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return !(compared & mask);
|
return !(compared & mask);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -1914,7 +1932,7 @@ scan16(const InternalVersionT *vs, int begin, int end,
|
|||||||
assert(0 <= end && end <= 16);
|
assert(0 <= end && end <= 16);
|
||||||
assert(begin <= end);
|
assert(begin <= end);
|
||||||
|
|
||||||
#if INTERNAL_VERSION_32_BIT && defined(HAS_ARM_NEON)
|
#if defined(HAS_ARM_NEON)
|
||||||
uint32x4_t w4[4];
|
uint32x4_t w4[4];
|
||||||
memcpy(w4, vs, sizeof(w4));
|
memcpy(w4, vs, sizeof(w4));
|
||||||
uint32_t rv;
|
uint32_t rv;
|
||||||
@@ -1938,7 +1956,7 @@ scan16(const InternalVersionT *vs, int begin, int end,
|
|||||||
conflict &= end == 16 ? -1 : (uint64_t(1) << (end << 2)) - 1;
|
conflict &= end == 16 ? -1 : (uint64_t(1) << (end << 2)) - 1;
|
||||||
conflict >>= begin << 2;
|
conflict >>= begin << 2;
|
||||||
return !conflict;
|
return !conflict;
|
||||||
#elif INTERNAL_VERSION_32_BIT && defined(HAS_AVX)
|
#elif defined(HAS_AVX)
|
||||||
uint32_t conflict;
|
uint32_t conflict;
|
||||||
if constexpr (kAVX512) {
|
if constexpr (kAVX512) {
|
||||||
conflict = compare16_32bit_avx512(vs, readVersion);
|
conflict = compare16_32bit_avx512(vs, readVersion);
|
||||||
@@ -2678,9 +2696,9 @@ void destroyTree(Node *root) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPointWrite(Node *&root, InternalVersionT oldestVersion,
|
void addPointWrite(Node *&root, std::span<const uint8_t> key,
|
||||||
std::span<const uint8_t> key, InternalVersionT writeVersion,
|
InternalVersionT writeVersion, NodeAllocators *allocators,
|
||||||
NodeAllocators *allocators, ConflictSet::Impl *impl) {
|
ConflictSet::Impl *impl) {
|
||||||
auto *n = insert<true>(&root, key, writeVersion, allocators, impl);
|
auto *n = insert<true>(&root, key, writeVersion, allocators, impl);
|
||||||
if (!n->entryPresent) {
|
if (!n->entryPresent) {
|
||||||
auto *p = nextLogical(n);
|
auto *p = nextLogical(n);
|
||||||
@@ -2691,24 +2709,23 @@ void addPointWrite(Node *&root, InternalVersionT oldestVersion,
|
|||||||
n->entry.pointVersion = writeVersion;
|
n->entry.pointVersion = writeVersion;
|
||||||
setMaxVersion(n, impl, writeVersion);
|
setMaxVersion(n, impl, writeVersion);
|
||||||
n->entry.rangeVersion =
|
n->entry.rangeVersion =
|
||||||
p != nullptr ? p->entry.rangeVersion : oldestVersion;
|
p == nullptr ? InternalVersionT::zero
|
||||||
|
: std::max(p->entry.rangeVersion, InternalVersionT::zero);
|
||||||
} else {
|
} else {
|
||||||
assert(writeVersion >= n->entry.pointVersion);
|
assert(writeVersion >= n->entry.pointVersion);
|
||||||
n->entry.pointVersion = writeVersion;
|
n->entry.pointVersion = writeVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addWriteRange(Node *&root, InternalVersionT oldestVersion,
|
void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
||||||
std::span<const uint8_t> begin, std::span<const uint8_t> end,
|
std::span<const uint8_t> end, InternalVersionT writeVersion,
|
||||||
InternalVersionT writeVersion, NodeAllocators *allocators,
|
NodeAllocators *allocators, ConflictSet::Impl *impl) {
|
||||||
ConflictSet::Impl *impl) {
|
|
||||||
|
|
||||||
int lcp = longestCommonPrefix(begin.data(), end.data(),
|
int lcp = longestCommonPrefix(begin.data(), end.data(),
|
||||||
std::min(begin.size(), end.size()));
|
std::min(begin.size(), end.size()));
|
||||||
if (lcp == int(begin.size()) && end.size() == begin.size() + 1 &&
|
if (lcp == int(begin.size()) && end.size() == begin.size() + 1 &&
|
||||||
end.back() == 0) {
|
end.back() == 0) {
|
||||||
return addPointWrite(root, oldestVersion, begin, writeVersion, allocators,
|
return addPointWrite(root, begin, writeVersion, allocators, impl);
|
||||||
impl);
|
|
||||||
}
|
}
|
||||||
const bool beginIsPrefix = lcp == int(begin.size());
|
const bool beginIsPrefix = lcp == int(begin.size());
|
||||||
auto remaining = begin.subspan(0, lcp);
|
auto remaining = begin.subspan(0, lcp);
|
||||||
@@ -2756,7 +2773,8 @@ void addWriteRange(Node *&root, InternalVersionT oldestVersion,
|
|||||||
if (insertedBegin) {
|
if (insertedBegin) {
|
||||||
auto *p = nextLogical(beginNode);
|
auto *p = nextLogical(beginNode);
|
||||||
beginNode->entry.rangeVersion =
|
beginNode->entry.rangeVersion =
|
||||||
p != nullptr ? p->entry.rangeVersion : oldestVersion;
|
p == nullptr ? InternalVersionT::zero
|
||||||
|
: std::max(p->entry.rangeVersion, InternalVersionT::zero);
|
||||||
beginNode->entry.pointVersion = writeVersion;
|
beginNode->entry.pointVersion = writeVersion;
|
||||||
assert(maxVersion(beginNode, impl) <= writeVersion);
|
assert(maxVersion(beginNode, impl) <= writeVersion);
|
||||||
setMaxVersion(beginNode, impl, writeVersion);
|
setMaxVersion(beginNode, impl, writeVersion);
|
||||||
@@ -2775,7 +2793,8 @@ void addWriteRange(Node *&root, InternalVersionT oldestVersion,
|
|||||||
if (insertedEnd) {
|
if (insertedEnd) {
|
||||||
auto *p = nextLogical(endNode);
|
auto *p = nextLogical(endNode);
|
||||||
endNode->entry.pointVersion =
|
endNode->entry.pointVersion =
|
||||||
p != nullptr ? p->entry.rangeVersion : oldestVersion;
|
p == nullptr ? InternalVersionT::zero
|
||||||
|
: std::max(p->entry.rangeVersion, InternalVersionT::zero);
|
||||||
auto m = maxVersion(endNode, impl);
|
auto m = maxVersion(endNode, impl);
|
||||||
setMaxVersion(endNode, impl,
|
setMaxVersion(endNode, impl,
|
||||||
std::max<InternalVersionT>(m, endNode->entry.pointVersion));
|
std::max<InternalVersionT>(m, endNode->entry.pointVersion));
|
||||||
@@ -2790,21 +2809,16 @@ void addWriteRange(Node *&root, InternalVersionT oldestVersion,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (beginNode = nextLogical(beginNode); beginNode != endNode;
|
for (beginNode = nextLogical(beginNode); beginNode != endNode;
|
||||||
beginNode = erase(beginNode, allocators, impl, endNode)) {
|
beginNode =
|
||||||
|
erase(beginNode, allocators, impl, /*logical*/ true, endNode)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator firstGeq(Node *n, const std::span<const uint8_t> key) {
|
Node *firstGeqPhysical(Node *n, const std::span<const uint8_t> key) {
|
||||||
auto remaining = key;
|
auto remaining = key;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (remaining.size() == 0) {
|
if (remaining.size() == 0) {
|
||||||
if (n->entryPresent) {
|
return n;
|
||||||
return {n, 0};
|
|
||||||
}
|
|
||||||
int c = getChildGeq(n, 0);
|
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
goto downLeftSpine;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *child = getChild(n, remaining[0]);
|
auto *child = getChild(n, remaining[0]);
|
||||||
@@ -2812,7 +2826,7 @@ Iterator firstGeq(Node *n, const std::span<const uint8_t> key) {
|
|||||||
int c = getChildGeq(n, remaining[0]);
|
int c = getChildGeq(n, remaining[0]);
|
||||||
if (c >= 0) {
|
if (c >= 0) {
|
||||||
n = getChildExists(n, c);
|
n = getChildExists(n, c);
|
||||||
goto downLeftSpine;
|
return n;
|
||||||
} else {
|
} else {
|
||||||
n = nextSibling(n);
|
n = nextSibling(n);
|
||||||
if (n == nullptr) {
|
if (n == nullptr) {
|
||||||
@@ -2820,9 +2834,9 @@ Iterator firstGeq(Node *n, const std::span<const uint8_t> key) {
|
|||||||
// final library, since we can't remove a key without introducing a
|
// final library, since we can't remove a key without introducing a
|
||||||
// key after it, and the only production caller of firstGeq is for
|
// key after it, and the only production caller of firstGeq is for
|
||||||
// resuming the setOldestVersion scan.
|
// resuming the setOldestVersion scan.
|
||||||
return {nullptr, 1}; // GCOVR_EXCL_LINE
|
return nullptr; // GCOVR_EXCL_LINE
|
||||||
}
|
}
|
||||||
goto downLeftSpine;
|
return n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2835,10 +2849,10 @@ Iterator firstGeq(Node *n, const std::span<const uint8_t> key) {
|
|||||||
if (i < commonLen) {
|
if (i < commonLen) {
|
||||||
auto c = n->partialKey()[i] <=> remaining[i];
|
auto c = n->partialKey()[i] <=> remaining[i];
|
||||||
if (c > 0) {
|
if (c > 0) {
|
||||||
goto downLeftSpine;
|
return n;
|
||||||
} else {
|
} else {
|
||||||
n = nextSibling(n);
|
n = nextSibling(n);
|
||||||
goto downLeftSpine;
|
return n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (commonLen == n->partialKeyLen) {
|
if (commonLen == n->partialKeyLen) {
|
||||||
@@ -2847,19 +2861,10 @@ Iterator firstGeq(Node *n, const std::span<const uint8_t> key) {
|
|||||||
} else if (n->partialKeyLen > int(remaining.size())) {
|
} else if (n->partialKeyLen > int(remaining.size())) {
|
||||||
// n is the first physical node greater than remaining, and there's no
|
// n is the first physical node greater than remaining, and there's no
|
||||||
// eq node
|
// eq node
|
||||||
goto downLeftSpine;
|
return n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
downLeftSpine:
|
|
||||||
for (;;) {
|
|
||||||
if (n->entryPresent) {
|
|
||||||
return {n, 1};
|
|
||||||
}
|
|
||||||
int c = getChildGeq(n, 0);
|
|
||||||
assert(c >= 0);
|
|
||||||
n = getChildExists(n, c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||||
@@ -2870,8 +2875,9 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
auto begin = std::span<const uint8_t>(r.begin.p, r.begin.len);
|
auto begin = std::span<const uint8_t>(r.begin.p, r.begin.len);
|
||||||
auto end = std::span<const uint8_t>(r.end.p, r.end.len);
|
auto end = std::span<const uint8_t>(r.end.p, r.end.len);
|
||||||
result[i] =
|
result[i] =
|
||||||
InternalVersionT(reads[i].readVersion) < oldestVersion ||
|
reads[i].readVersion < oldestVersionFullPrecision ||
|
||||||
reads[i].readVersion < newestVersionFullPrecision - 2e9
|
reads[i].readVersion <
|
||||||
|
newestVersionFullPrecision - kNominalVersionWindow
|
||||||
? TooOld
|
? TooOld
|
||||||
: (end.size() > 0
|
: (end.size() > 0
|
||||||
? checkRangeRead(root, begin, end,
|
? checkRangeRead(root, begin, end,
|
||||||
@@ -2885,50 +2891,49 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
|
|
||||||
void addWrites(const WriteRange *writes, int count, int64_t writeVersion) {
|
void addWrites(const WriteRange *writes, int count, int64_t writeVersion) {
|
||||||
assert(writeVersion >= newestVersionFullPrecision);
|
assert(writeVersion >= newestVersionFullPrecision);
|
||||||
|
|
||||||
|
// TODO allow this condition
|
||||||
|
assert(writeVersion < newestVersionFullPrecision + kNominalVersionWindow);
|
||||||
|
|
||||||
newestVersionFullPrecision = writeVersion;
|
newestVersionFullPrecision = writeVersion;
|
||||||
#if INTERNAL_VERSION_32_BIT
|
setOldestVersion(
|
||||||
InternalVersionT::zero = oldestVersion;
|
std::max(oldestVersionFullPrecision,
|
||||||
#endif
|
newestVersionFullPrecision - kNominalVersionWindow));
|
||||||
|
while (oldestExtantVersion <
|
||||||
|
newestVersionFullPrecision - kMaxCorrectVersionWindow) {
|
||||||
|
gcScanStep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
const auto &w = writes[i];
|
const auto &w = writes[i];
|
||||||
auto begin = std::span<const uint8_t>(w.begin.p, w.begin.len);
|
auto begin = std::span<const uint8_t>(w.begin.p, w.begin.len);
|
||||||
auto end = std::span<const uint8_t>(w.end.p, w.end.len);
|
auto end = std::span<const uint8_t>(w.end.p, w.end.len);
|
||||||
if (w.end.len > 0) {
|
if (w.end.len > 0) {
|
||||||
keyUpdates += 3;
|
keyUpdates += 3;
|
||||||
addWriteRange(root, oldestVersion, begin, end,
|
addWriteRange(root, begin, end, InternalVersionT(writeVersion),
|
||||||
InternalVersionT(writeVersion), &allocators, this);
|
&allocators, this);
|
||||||
} else {
|
} else {
|
||||||
keyUpdates += 2;
|
keyUpdates += 2;
|
||||||
addPointWrite(root, oldestVersion, begin,
|
addPointWrite(root, begin, InternalVersionT(writeVersion), &allocators,
|
||||||
InternalVersionT(writeVersion), &allocators, this);
|
this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOldestVersion(int64_t o) {
|
// Spends up to `fuel` gc'ing, and returns its unused fuel. Reclaims memory
|
||||||
InternalVersionT oldestVersion{o};
|
// and updates oldestExtantVersion after spending enough fuel.
|
||||||
assert(o >= oldestVersionFullPrecision);
|
int64_t gcScanStep(int64_t fuel) {
|
||||||
this->oldestVersionFullPrecision = o;
|
Node *n = firstGeqPhysical(root, removalKey);
|
||||||
this->oldestVersion = oldestVersion;
|
|
||||||
#if INTERNAL_VERSION_32_BIT
|
|
||||||
InternalVersionT::zero = oldestVersion;
|
|
||||||
#endif
|
|
||||||
#ifdef NDEBUG
|
|
||||||
// This is here for performance reasons, since we want to amortize the cost
|
|
||||||
// of storing the search path as a string. In tests, we want to exercise the
|
|
||||||
// rest of the code often.
|
|
||||||
if (keyUpdates < 100) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Node *n = firstGeq(root, removalKey).n;
|
|
||||||
// There's no way to erase removalKey without introducing a key after it
|
// There's no way to erase removalKey without introducing a key after it
|
||||||
assert(n != nullptr);
|
assert(n != nullptr);
|
||||||
// Don't erase the root
|
// Don't erase the root
|
||||||
if (n == root) {
|
if (n == root) {
|
||||||
|
rezero(n, oldestVersion);
|
||||||
|
rootMaxVersion = std::max(rootMaxVersion, oldestVersion);
|
||||||
n = nextPhysical(n);
|
n = nextPhysical(n);
|
||||||
}
|
}
|
||||||
for (; keyUpdates > 0 && n != nullptr; --keyUpdates) {
|
for (; fuel > 0 && n != nullptr; --fuel) {
|
||||||
|
rezero(n, oldestVersion);
|
||||||
if (n->entryPresent && std::max(n->entry.pointVersion,
|
if (n->entryPresent && std::max(n->entry.pointVersion,
|
||||||
n->entry.rangeVersion) <= oldestVersion) {
|
n->entry.rangeVersion) <= oldestVersion) {
|
||||||
// Any transaction n would have prevented from committing is
|
// Any transaction n would have prevented from committing is
|
||||||
@@ -2938,7 +2943,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
// node is greater than the point version of the left node
|
// node is greater than the point version of the left node
|
||||||
assert(n->entry.rangeVersion <= oldestVersion);
|
assert(n->entry.rangeVersion <= oldestVersion);
|
||||||
Node *dummy = nullptr;
|
Node *dummy = nullptr;
|
||||||
n = erase(n, &allocators, this, dummy);
|
n = erase(n, &allocators, this, /*logical*/ false, dummy);
|
||||||
} else {
|
} else {
|
||||||
maybeDecreaseCapacity(n, &allocators, this);
|
maybeDecreaseCapacity(n, &allocators, this);
|
||||||
n = nextPhysical(n);
|
n = nextPhysical(n);
|
||||||
@@ -2946,14 +2951,44 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
}
|
}
|
||||||
if (n == nullptr) {
|
if (n == nullptr) {
|
||||||
removalKey = {};
|
removalKey = {};
|
||||||
|
oldestExtantVersion = oldestVersionAtGcBegin;
|
||||||
|
oldestVersionAtGcBegin = oldestVersionFullPrecision;
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
fprintf(stderr,
|
||||||
|
"new oldestExtantVersion: %" PRId64
|
||||||
|
", new oldestVersionAtGcBegin: %" PRId64 "\n",
|
||||||
|
oldestExtantVersion, oldestVersionAtGcBegin);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
removalKeyArena = Arena();
|
removalKeyArena = Arena();
|
||||||
removalKey = getSearchPath(removalKeyArena, n);
|
removalKey = getSearchPath(removalKeyArena, n);
|
||||||
}
|
}
|
||||||
|
return fuel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOldestVersion(int64_t o) {
|
||||||
|
if (o <= oldestVersionFullPrecision) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InternalVersionT oldestVersion{o};
|
||||||
|
this->oldestVersionFullPrecision = o;
|
||||||
|
this->oldestVersion = oldestVersion;
|
||||||
|
InternalVersionT::zero = oldestVersion;
|
||||||
|
#ifdef NDEBUG
|
||||||
|
// This is here for performance reasons, since we want to amortize the cost
|
||||||
|
// of storing the search path as a string. In tests, we want to exercise the
|
||||||
|
// rest of the code often.
|
||||||
|
if (keyUpdates < 100) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
keyUpdates = gcScanStep(keyUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit Impl(int64_t oldestVersion)
|
explicit Impl(int64_t oldestVersion)
|
||||||
: oldestVersion(oldestVersion), oldestVersionFullPrecision(oldestVersion),
|
: oldestVersion(oldestVersion), oldestVersionFullPrecision(oldestVersion),
|
||||||
|
oldestExtantVersion(oldestVersion),
|
||||||
|
oldestVersionAtGcBegin(oldestVersion),
|
||||||
newestVersionFullPrecision(oldestVersion) {
|
newestVersionFullPrecision(oldestVersion) {
|
||||||
#if DEBUG_VERBOSE
|
#if DEBUG_VERBOSE
|
||||||
fprintf(stderr, "radix_tree: create\n");
|
fprintf(stderr, "radix_tree: create\n");
|
||||||
@@ -2972,6 +3007,8 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
root->entryPresent = true;
|
root->entryPresent = true;
|
||||||
root->entry.pointVersion = this->oldestVersion;
|
root->entry.pointVersion = this->oldestVersion;
|
||||||
root->entry.rangeVersion = this->oldestVersion;
|
root->entry.rangeVersion = this->oldestVersion;
|
||||||
|
|
||||||
|
InternalVersionT::zero = this->oldestVersion;
|
||||||
}
|
}
|
||||||
~Impl() {
|
~Impl() {
|
||||||
#if DEBUG_VERBOSE
|
#if DEBUG_VERBOSE
|
||||||
@@ -2991,8 +3028,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
InternalVersionT oldestVersion;
|
InternalVersionT oldestVersion;
|
||||||
// TODO this doesn't fully mitigate the 32-bit precision issue, since we still
|
// TODO this doesn't fully mitigate the 32-bit precision issue, since we still
|
||||||
// need to make sure we clean up versions in the tree before they fall out of
|
// need to make sure we clean up versions in the tree before they fall out of
|
||||||
// the 2e9 window.
|
// the `kMaxCorrectVersionWindow` window.
|
||||||
int64_t oldestVersionFullPrecision;
|
int64_t oldestVersionFullPrecision;
|
||||||
|
int64_t oldestExtantVersion;
|
||||||
|
int64_t oldestVersionAtGcBegin;
|
||||||
int64_t newestVersionFullPrecision;
|
int64_t newestVersionFullPrecision;
|
||||||
int64_t totalBytes = 0;
|
int64_t totalBytes = 0;
|
||||||
};
|
};
|
||||||
@@ -3130,6 +3169,74 @@ int64_t internal_getBytes(ConflictSet::Impl *impl) { return impl->totalBytes; }
|
|||||||
|
|
||||||
// GCOVR_EXCL_START
|
// GCOVR_EXCL_START
|
||||||
|
|
||||||
|
Iterator firstGeqLogical(Node *n, const std::span<const uint8_t> key) {
|
||||||
|
auto remaining = key;
|
||||||
|
for (;;) {
|
||||||
|
if (remaining.size() == 0) {
|
||||||
|
if (n->entryPresent) {
|
||||||
|
return {n, 0};
|
||||||
|
}
|
||||||
|
int c = getChildGeq(n, 0);
|
||||||
|
assert(c >= 0);
|
||||||
|
n = getChildExists(n, c);
|
||||||
|
goto downLeftSpine;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// This line is genuinely unreachable from any entry point of the
|
||||||
|
// final library, since we can't remove a key without introducing a
|
||||||
|
// key after it, and the only production caller of firstGeq is for
|
||||||
|
// resuming the setOldestVersion scan.
|
||||||
|
return {nullptr, 1}; // GCOVR_EXCL_LINE
|
||||||
|
}
|
||||||
|
goto downLeftSpine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = child;
|
||||||
|
remaining = remaining.subspan(1, remaining.size() - 1);
|
||||||
|
|
||||||
|
if (n->partialKeyLen > 0) {
|
||||||
|
int commonLen = std::min<int>(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);
|
||||||
|
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
|
||||||
|
goto downLeftSpine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
downLeftSpine:
|
||||||
|
for (;;) {
|
||||||
|
if (n->entryPresent) {
|
||||||
|
return {n, 1};
|
||||||
|
}
|
||||||
|
int c = getChildGeq(n, 0);
|
||||||
|
assert(c >= 0);
|
||||||
|
n = getChildExists(n, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ConflictSet::check(const ReadRange *reads, Result *results,
|
void ConflictSet::check(const ReadRange *reads, Result *results,
|
||||||
int count) const {
|
int count) const {
|
||||||
internal_check(impl, reads, results, count);
|
internal_check(impl, reads, results, count);
|
||||||
@@ -3326,14 +3433,60 @@ void checkParentPointers(Node *node, bool &success) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Iterator firstGeq(Node *n, std::string_view key) {
|
Iterator firstGeq(Node *n, std::string_view key) {
|
||||||
return firstGeq(
|
return firstGeqLogical(
|
||||||
n, std::span<const uint8_t>((const uint8_t *)key.data(), key.size()));
|
n, std::span<const uint8_t>((const uint8_t *)key.data(), key.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkVersionsGeqOldestExtant(Node *n,
|
||||||
|
InternalVersionT oldestExtantVersion) {
|
||||||
|
if (n->entryPresent) {
|
||||||
|
assert(n->entry.pointVersion >= oldestExtantVersion);
|
||||||
|
assert(n->entry.rangeVersion >= oldestExtantVersion);
|
||||||
|
}
|
||||||
|
switch (n->getType()) {
|
||||||
|
case Type_Node0: {
|
||||||
|
} break;
|
||||||
|
case Type_Node3: {
|
||||||
|
auto *self = static_cast<Node3 *>(n);
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type_Node16: {
|
||||||
|
auto *self = static_cast<Node16 *>(n);
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type_Node48: {
|
||||||
|
auto *self = static_cast<Node48 *>(n);
|
||||||
|
for (int i = 0; i < 48; ++i) {
|
||||||
|
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||||
|
}
|
||||||
|
for (auto m : self->maxOfMax) {
|
||||||
|
assert(m >= oldestExtantVersion);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type_Node256: {
|
||||||
|
auto *self = static_cast<Node256 *>(n);
|
||||||
|
for (int i = 0; i < 256; ++i) {
|
||||||
|
assert(self->childMaxVersion[i] >= oldestExtantVersion);
|
||||||
|
}
|
||||||
|
for (auto m : self->maxOfMax) {
|
||||||
|
assert(m >= oldestExtantVersion);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default: // GCOVR_EXCL_LINE
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[[maybe_unused]] InternalVersionT
|
[[maybe_unused]] InternalVersionT
|
||||||
checkMaxVersion(Node *root, Node *node, InternalVersionT oldestVersion,
|
checkMaxVersion(Node *root, Node *node, InternalVersionT oldestVersion,
|
||||||
bool &success, ConflictSet::Impl *impl) {
|
bool &success, ConflictSet::Impl *impl) {
|
||||||
InternalVersionT expected{0};
|
checkVersionsGeqOldestExtant(node,
|
||||||
|
InternalVersionT(impl->oldestExtantVersion));
|
||||||
|
auto expected = InternalVersionT::zero;
|
||||||
if (node->entryPresent) {
|
if (node->entryPresent) {
|
||||||
expected = std::max(expected, node->entry.pointVersion);
|
expected = std::max(expected, node->entry.pointVersion);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-13
@@ -467,13 +467,15 @@ inline uint32_t Arbitrary::bounded(uint32_t s) {
|
|||||||
// ==================== END ARBITRARY IMPL ====================
|
// ==================== END ARBITRARY IMPL ====================
|
||||||
|
|
||||||
struct ReferenceImpl {
|
struct ReferenceImpl {
|
||||||
explicit ReferenceImpl(int64_t oldestVersion) : oldestVersion(oldestVersion) {
|
explicit ReferenceImpl(int64_t oldestVersion)
|
||||||
|
: oldestVersion(oldestVersion), newestVersion(oldestVersion) {
|
||||||
writeVersionMap[""] = oldestVersion;
|
writeVersionMap[""] = oldestVersion;
|
||||||
}
|
}
|
||||||
void check(const ConflictSet::ReadRange *reads, ConflictSet::Result *results,
|
void check(const ConflictSet::ReadRange *reads, ConflictSet::Result *results,
|
||||||
int count) const {
|
int count) const {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
if (reads[i].readVersion < oldestVersion) {
|
if (reads[i].readVersion < oldestVersion ||
|
||||||
|
reads[i].readVersion < newestVersion - 2e9) {
|
||||||
results[i] = ConflictSet::TooOld;
|
results[i] = ConflictSet::TooOld;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -495,6 +497,8 @@ struct ReferenceImpl {
|
|||||||
}
|
}
|
||||||
void addWrites(const ConflictSet::WriteRange *writes, int count,
|
void addWrites(const ConflictSet::WriteRange *writes, int count,
|
||||||
int64_t writeVersion) {
|
int64_t writeVersion) {
|
||||||
|
assert(writeVersion >= newestVersion);
|
||||||
|
newestVersion = writeVersion;
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
auto begin =
|
auto begin =
|
||||||
std::string((const char *)writes[i].begin.p, writes[i].begin.len);
|
std::string((const char *)writes[i].begin.p, writes[i].begin.len);
|
||||||
@@ -519,6 +523,7 @@ struct ReferenceImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t oldestVersion;
|
int64_t oldestVersion;
|
||||||
|
int64_t newestVersion;
|
||||||
std::map<std::string, int64_t> writeVersionMap;
|
std::map<std::string, int64_t> writeVersionMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -578,8 +583,8 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
explicit TestDriver(const uint8_t *data, size_t size)
|
explicit TestDriver(const uint8_t *data, size_t size)
|
||||||
: arbitrary({data, size}) {}
|
: arbitrary({data, size}) {}
|
||||||
|
|
||||||
int64_t oldestVersion = arbitrary.bounded(2) ? 0 : 0xfffffff0;
|
int64_t oldestVersion = 0;
|
||||||
int64_t writeVersion = oldestVersion + 100;
|
int64_t writeVersion = 0;
|
||||||
ConflictSetImpl cs{oldestVersion};
|
ConflictSetImpl cs{oldestVersion};
|
||||||
ReferenceImpl refImpl{oldestVersion};
|
ReferenceImpl refImpl{oldestVersion};
|
||||||
|
|
||||||
@@ -600,7 +605,7 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
{
|
{
|
||||||
int numPointWrites = arbitrary.bounded(100);
|
int numPointWrites = arbitrary.bounded(100);
|
||||||
int numRangeWrites = arbitrary.bounded(100);
|
int numRangeWrites = arbitrary.bounded(100);
|
||||||
int64_t v = (writeVersion += arbitrary.bounded(10));
|
int64_t v = (writeVersion += arbitrary.bounded(2e9));
|
||||||
auto *writes =
|
auto *writes =
|
||||||
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
||||||
auto keys = set<std::string_view>(arena);
|
auto keys = set<std::string_view>(arena);
|
||||||
@@ -642,18 +647,21 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
}
|
}
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
if (writes[i].end.len == 0) {
|
if (writes[i].end.len == 0) {
|
||||||
fprintf(stderr, "Write: {%s} -> %" PRId64 "\n",
|
fprintf(stderr, "Write: {%s}\n", printable(writes[i].begin).c_str());
|
||||||
printable(writes[i].begin).c_str(), writeVersion);
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Write: [%s, %s) -> %" PRId64 "\n",
|
fprintf(stderr, "Write: [%s, %s)\n",
|
||||||
printable(writes[i].begin).c_str(),
|
printable(writes[i].begin).c_str(),
|
||||||
printable(writes[i].end).c_str(), writeVersion);
|
printable(writes[i].end).c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
assert(iter == keys.end());
|
assert(iter == keys.end());
|
||||||
assert(i == numPointWrites + numRangeWrites);
|
assert(i == numPointWrites + numRangeWrites);
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
fprintf(stderr, "Write @ %" PRId64 "\n", v);
|
||||||
|
#endif
|
||||||
|
|
||||||
CALLGRIND_START_INSTRUMENTATION;
|
CALLGRIND_START_INSTRUMENTATION;
|
||||||
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
CALLGRIND_STOP_INSTRUMENTATION;
|
CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
@@ -710,12 +718,12 @@ template <class ConflictSetImpl> struct TestDriver {
|
|||||||
reads[i].readVersion = v;
|
reads[i].readVersion = v;
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
if (reads[i].end.len == 0) {
|
if (reads[i].end.len == 0) {
|
||||||
fprintf(stderr, "Read: {%s} @ %d\n",
|
fprintf(stderr, "Read: {%s} @ %" PRId64 "\n",
|
||||||
printable(reads[i].begin).c_str(), int(reads[i].readVersion));
|
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Read: [%s, %s) @ %d\n",
|
fprintf(stderr, "Read: [%s, %s) @ %" PRId64 "\n",
|
||||||
printable(reads[i].begin).c_str(),
|
printable(reads[i].begin).c_str(),
|
||||||
printable(reads[i].end).c_str(), int(reads[i].readVersion));
|
printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user