22 Commits

Author SHA1 Message Date
5a132799a4 Add cycles_total
Some checks failed
Tests / Clang total: 2840, passed: 2840
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Debug total: 2838, passed: 2838
weaselab/conflict-set/pipeline/head There was a failure building this commit
2024-08-15 15:13:00 -07:00
72469ebb6e Erase along left spine. Not faster 2024-08-15 15:07:44 -07:00
6c79847a42 Add instructions_total for linux 2024-08-15 15:06:53 -07:00
405a2ca161 Fix typo 2024-08-15 13:52:51 -07:00
f93466316a Pass in-tree reference to mergeWithChild 2024-08-15 13:52:06 -07:00
5626cd09d9 Add to corpus 2024-08-15 11:50:04 -07:00
41840220c3 Optimize version handling in mergeWithChild 2024-08-15 11:49:13 -07:00
7ff00e7846 Extract mergeWithChild to function 2024-08-15 11:40:52 -07:00
6242f40d48 Require that eraseBetween leave at least one child or entryPresent 2024-08-15 11:37:36 -07:00
403d70a1d3 Prefer not copying node in eraseBetween
If numChildren + entryPresent is enough, we don't have to copy even if
it would fit in a smaller node.

If we have to copy, we might as well use the smallest acceptable node
type.
2024-08-15 11:33:16 -07:00
9763452713 Separate beginIsPrefix path and simplify slightly 2024-08-15 11:29:15 -07:00
73d0593fca Remove separate prefix write codepath for now 2024-08-14 21:29:43 -07:00
23c2a3e1c6 SIMD for eraseBetween (Node16)
Some checks failed
Tests / Clang total: 2688, passed: 2688
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Debug total: 2686, passed: 2686
weaselab/conflict-set/pipeline/head There was a failure building this commit
2024-08-14 18:12:46 -07:00
a64e792964 Remove unused function 2024-08-14 17:40:04 -07:00
5e362d5330 Add to corpus 2024-08-14 17:37:18 -07:00
cc526cb6ba Call eraseBetween on useAsRoot in addWriteRange 2024-08-14 17:08:55 -07:00
7e49888bec More eraseBetween optimizations 2024-08-14 16:40:29 -07:00
e64ebabced eraseBetween optimizations 2024-08-14 16:13:37 -07:00
1e34951a77 Fix use-of-uninit in eraseBetween (Node256) 2024-08-14 15:25:10 -07:00
baf64520d6 Have eraseBetween take in-tree node by reference 2024-08-14 15:04:11 -07:00
3499626127 Fix potential strict aliasing issues 2024-08-14 15:01:34 -07:00
b7f9084694 destroyTree -> eraseTree. Use freelist 2024-08-14 14:47:22 -07:00
146 changed files with 343 additions and 283 deletions

View File

@@ -195,7 +195,6 @@ struct Node {
/* end section that's copied to the next node */
uint8_t *partialKey();
size_t size() const;
Type getType() const { return type; }
int32_t getCapacity() const { return partialKeyCapacity; }
@@ -771,23 +770,6 @@ uint8_t *Node::partialKey() {
}
}
size_t Node::size() const {
switch (type) {
case Type_Node0:
return ((Node0 *)this)->size();
case Type_Node3:
return ((Node3 *)this)->size();
case Type_Node16:
return ((Node16 *)this)->size();
case Type_Node48:
return ((Node48 *)this)->size();
case Type_Node256:
return ((Node256 *)this)->size();
default: // GCOVR_EXCL_LINE
__builtin_unreachable(); // GCOVR_EXCL_LINE
}
}
// A type that's plumbed along the check call tree. Lifetime ends after each
// check call.
struct ReadContext {
@@ -818,7 +800,6 @@ struct WriteContext {
int64_t nodes_allocated;
int64_t nodes_released;
int64_t point_writes;
int64_t prefix_writes;
int64_t range_writes;
int64_t write_bytes;
} accum;
@@ -1623,6 +1604,46 @@ void rezero(Node *n, InternalVersionT z) {
}
}
void mergeWithChild(Node *&self, WriteContext *tls, ConflictSet::Impl *impl,
Node *&dontInvalidate, Node3 *self3) {
assert(!self3->entryPresent);
auto *child = self3->children[0];
int minCapacity = self3->partialKeyLen + 1 + child->partialKeyLen;
if (minCapacity > child->getCapacity()) {
const bool update = child == dontInvalidate;
freeAndMakeCapacityAtLeast(child, minCapacity, tls, impl, true);
if (update) {
dontInvalidate = child;
}
}
// Merge partial key with child
#if DEBUG_VERBOSE && !defined(NDEBUG)
fprintf(stderr, "Merge %s into %s\n", getSearchPathPrintable(self).c_str(),
getSearchPathPrintable(child).c_str());
#endif
InternalVersionT childMaxVersion = self3->childMaxVersion[0];
// Construct new partial key for child
memmove(child->partialKey() + self3->partialKeyLen + 1, child->partialKey(),
child->partialKeyLen);
memcpy(child->partialKey(), self3->partialKey(), self->partialKeyLen);
child->partialKey()[self3->partialKeyLen] = self3->index[0];
child->partialKeyLen += 1 + self3->partialKeyLen;
child->parent = self->parent;
child->parentsIndex = self->parentsIndex;
// Max versions are stored in the parent, so we need to update it now
// that we have a new parent.
setMaxVersion(child, impl, std::max(childMaxVersion, tls->zero));
self = child;
tls->release(self3);
}
void maybeDownsize(Node *self, WriteContext *tls, ConflictSet::Impl *impl,
Node *&dontInvalidate) {
@@ -1642,45 +1663,7 @@ void maybeDownsize(Node *self, WriteContext *tls, ConflictSet::Impl *impl,
getInTree(self, impl) = newSelf;
tls->release(self3);
} else if (self->numChildren == 1 && !self->entryPresent) {
auto *child = self3->children[0];
int minCapacity = self3->partialKeyLen + 1 + child->partialKeyLen;
if (minCapacity > child->getCapacity()) {
const bool update = child == dontInvalidate;
freeAndMakeCapacityAtLeast(child, minCapacity, tls, impl, true);
if (update) {
dontInvalidate = child;
}
}
// Merge partial key with child
#if DEBUG_VERBOSE && !defined(NDEBUG)
fprintf(stderr, "Merge %s into %s\n",
getSearchPathPrintable(self).c_str(),
getSearchPathPrintable(child).c_str());
#endif
InternalVersionT childMaxVersion = maxVersion(child, impl);
// Construct new partial key for child
memmove(child->partialKey() + self3->partialKeyLen + 1,
child->partialKey(), child->partialKeyLen);
memcpy(child->partialKey(), self3->partialKey(), self->partialKeyLen);
child->partialKey()[self3->partialKeyLen] = self3->index[0];
child->partialKeyLen += 1 + self3->partialKeyLen;
child->parent = self->parent;
child->parentsIndex = self->parentsIndex;
// Max versions are stored in the parent, so we need to update it now
// that we have a new parent.
setMaxVersion(child, impl, childMaxVersion);
if (child->parent) {
rezero(child->parent, tls->zero);
}
getInTree(self, impl) = child;
tls->release(self3);
mergeWithChild(getInTree(self, impl), tls, impl, dontInvalidate, self3);
}
} break;
case Type_Node16:
@@ -1715,55 +1698,55 @@ void maybeDownsize(Node *self, WriteContext *tls, ConflictSet::Impl *impl,
}
}
void destroyTree(Node *root, WriteContext::Accum *accum) {
void eraseTree(Node *root, WriteContext *tls) {
Arena arena;
auto toFree = vector<Node *>(arena);
toFree.push_back(root);
#if SHOW_MEMORY
for (auto *iter = root; iter != nullptr; iter = nextPhysical(iter)) {
removeNode(iter);
removeKey(iter);
}
#endif
while (toFree.size() > 0) {
auto *n = toFree.back();
toFree.pop_back();
accum->entries_erased += n->entryPresent;
++accum->nodes_released;
tls->accum.entries_erased += n->entryPresent;
++tls->accum.nodes_released;
removeNode(n);
removeKey(n);
switch (n->getType()) {
case Type_Node0: {
auto *n0 = static_cast<Node0 *>(n);
tls->release(n0);
} break;
case Type_Node3: {
auto *n3 = static_cast<Node3 *>(n);
toFree.append(std::span<Node *>(n3->children, n3->numChildren));
tls->release(n3);
} break;
case Type_Node16: {
auto *n16 = static_cast<Node16 *>(n);
toFree.append(std::span<Node *>(n16->children, n16->numChildren));
tls->release(n16);
} break;
case Type_Node48: {
auto *n48 = static_cast<Node48 *>(n);
toFree.append(std::span<Node *>(n48->children, n48->numChildren));
tls->release(n48);
} break;
case Type_Node256: {
auto *n256 = static_cast<Node256 *>(n);
auto *out = toFree.unsafePrepareAppend(n256->numChildren).data();
n256->bitSet.forEachSet([&](int i) { *out++ = n256->children[i]; });
assert(out == toFree.end());
tls->release(n256);
} break;
default: // GCOVR_EXCL_LINE
__builtin_unreachable(); // GCOVR_EXCL_LINE
}
removeNode(n);
safe_free(n, n->size());
}
}
void eraseBetween(Node3 *&n, int begin, int end, WriteContext *tls,
ConflictSet::Impl *impl) {
void eraseBetween(Node **inTree, Node3 *n, int begin, int end,
WriteContext *tls) {
const unsigned shiftUpperBound = end - begin;
const unsigned shiftAmount = begin;
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
@@ -1772,7 +1755,7 @@ void eraseBetween(Node3 *&n, int begin, int end, WriteContext *tls,
InternalVersionT *maxVOut = n->childMaxVersion;
for (int i = 0; i < n->numChildren; ++i) {
if (inBounds(n->index[i])) {
destroyTree(n->children[i], &tls->accum);
eraseTree(n->children[i], tls);
} else {
*nodeOut++ = n->children[i];
*indexOut++ = n->index[i];
@@ -1784,144 +1767,167 @@ void eraseBetween(Node3 *&n, int begin, int end, WriteContext *tls,
if (n->numChildren == 0) {
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
*inTree = newNode;
}
}
void eraseBetween(Node16 *&n, int begin, int end, WriteContext *tls,
ConflictSet::Impl *impl) {
void eraseBetween(Node **inTree, Node16 *n, int begin, int end,
WriteContext *tls) {
if (end - begin == 256) {
for (int i = 0; i < n->numChildren; ++i) {
eraseTree(n->children[i], tls);
}
n->numChildren = 0;
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
tls->release(n);
*inTree = newNode;
return;
}
assert(end - begin < 256);
#ifdef HAS_ARM_NEON
uint8x16_t indices;
memcpy(&indices, n->index, 16);
// 0xff for each in bounds
auto results =
vcltq_u8(vsubq_u8(indices, vdupq_n_u8(begin)), vdupq_n_u8(end - begin));
// 0xf for each 0xff
uint64_t mask = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(results), 4)), 0);
#elif defined(HAS_AVX)
__m128i indices;
memcpy(&indices, n->index, 16);
indices = _mm_sub_epi8(indices, _mm_set1_epi8(begin));
uint32_t mask = ~_mm_movemask_epi8(_mm_cmpeq_epi8(
indices, _mm_max_epu8(indices, _mm_set1_epi8(end - begin))));
#else
const unsigned shiftUpperBound = end - begin;
const unsigned shiftAmount = begin;
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
Node **nodeOut = n->children;
uint8_t *indexOut = n->index;
InternalVersionT *maxVOut = n->childMaxVersion;
for (int i = 0; i < n->numChildren; ++i) {
if (inBounds(n->index[i])) {
destroyTree(n->children[i], &tls->accum);
} else {
*nodeOut++ = n->children[i];
*indexOut++ = n->index[i];
*maxVOut++ = n->childMaxVersion[i];
}
uint32_t mask = 0;
for (int i = 0; i < 16; ++i) {
mask |= inBounds(is[i]) << i;
}
n->numChildren = nodeOut - n->children;
#endif
mask &= (decltype(mask)(1) << n->numChildren) - 1;
if (n->numChildren == 0) {
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
} else if (n->numChildren <= Node3::kMaxNodes) {
if (!mask) {
return;
}
int first = std::countr_zero(mask);
int count = std::popcount(mask);
n->numChildren -= count;
for (int i = first; i < first + count; ++i) {
eraseTree(n->children[i], tls);
}
for (int i = first; i < n->numChildren; ++i) {
n->children[i] = n->children[i + count];
n->childMaxVersion[i] = n->childMaxVersion[i + count];
n->index[i] = n->index[i + count];
}
if (n->numChildren + n->entryPresent >= kMinChildrenNode16) {
// nop
} else if (n->numChildren > 0) {
auto *newNode = tls->allocate<Node3>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
*inTree = newNode;
} else {
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
tls->release(n);
*inTree = newNode;
}
}
void eraseBetween(Node48 *&n, int begin, int end, WriteContext *tls,
ConflictSet::Impl *impl) {
const unsigned shiftUpperBound = end - begin;
const unsigned shiftAmount = begin;
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
Node **nodeOut = n->children;
uint8_t *indexOut = n->reverseIndex;
InternalVersionT *maxVOut = n->childMaxVersion;
for (auto &v : n->maxOfMax) {
v = tls->zero;
}
n->bitSet = {};
memset(n->index, -1, sizeof(n->index));
n->nextFree = 0;
for (int i = 0; i < n->numChildren; ++i) {
if (inBounds(n->reverseIndex[i])) {
destroyTree(n->children[i], &tls->accum);
} else {
*nodeOut++ = n->children[i];
*indexOut++ = n->reverseIndex[i];
*maxVOut++ = n->childMaxVersion[i];
n->maxOfMax[i >> Node48::kMaxOfMaxShift] = std::max(
n->maxOfMax[i >> Node48::kMaxOfMaxShift], n->childMaxVersion[i]);
n->bitSet.set(n->reverseIndex[i]);
n->index[n->reverseIndex[i]] = n->nextFree++;
void eraseBetween(Node **inTree, Node48 *n, int begin, int end,
WriteContext *tls) {
for (int i = n->bitSet.firstSetGeq(begin); i >= 0 && i < end;
i = n->bitSet.firstSetGeq(i)) {
n->bitSet.reset(i);
int8_t toRemoveChildrenIndex = std::exchange(n->index[i], -1);
int8_t lastChildrenIndex = --n->nextFree;
assert(toRemoveChildrenIndex >= 0);
assert(lastChildrenIndex >= 0);
eraseTree(n->children[toRemoveChildrenIndex], tls);
if (toRemoveChildrenIndex != lastChildrenIndex) {
n->children[toRemoveChildrenIndex] = n->children[lastChildrenIndex];
n->childMaxVersion[toRemoveChildrenIndex] =
n->childMaxVersion[lastChildrenIndex];
n->maxOfMax[toRemoveChildrenIndex >> Node48::kMaxOfMaxShift] =
std::max(n->maxOfMax[toRemoveChildrenIndex >> Node48::kMaxOfMaxShift],
n->childMaxVersion[toRemoveChildrenIndex]);
auto parentIndex = n->children[toRemoveChildrenIndex]->parentsIndex;
n->index[parentIndex] = toRemoveChildrenIndex;
n->reverseIndex[toRemoveChildrenIndex] = parentIndex;
}
n->childMaxVersion[lastChildrenIndex] = tls->zero;
--n->numChildren;
}
n->numChildren = n->nextFree;
if (n->numChildren == 0) {
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
} else if (n->numChildren <= Node3::kMaxNodes) {
auto *newNode = tls->allocate<Node3>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
} else if (n->numChildren <= Node16::kMaxNodes) {
if (n->numChildren + n->entryPresent >= kMinChildrenNode48) {
// nop
} else if (n->numChildren > Node3::kMaxNodes) {
auto *newNode = tls->allocate<Node16>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
*inTree = newNode;
} else if (n->numChildren > 0) {
auto *newNode = tls->allocate<Node3>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
tls->release(n);
*inTree = newNode;
} else {
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
tls->release(n);
*inTree = newNode;
}
}
void eraseBetween(Node256 *&n, int begin, int end, WriteContext *tls,
ConflictSet::Impl *impl) {
const unsigned shiftUpperBound = end - begin;
const unsigned shiftAmount = begin;
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
n->numChildren = 0;
BitSet newBitSet;
n->bitSet.forEachSet([&](int i) {
if (inBounds(i)) {
destroyTree(std::exchange(n->children[i], nullptr), &tls->accum);
} else {
++n->numChildren;
newBitSet.set(i);
}
});
n->bitSet = newBitSet;
// Don't need to update childMaxVersion or maxOfMax because of monotonicity
if (n->numChildren == 0) {
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
} else if (n->numChildren <= Node3::kMaxNodes) {
auto *newNode = tls->allocate<Node3>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
} else if (n->numChildren <= Node16::kMaxNodes) {
auto *newNode = tls->allocate<Node16>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
} else if (n->numChildren <= Node48::kMaxNodes) {
void eraseBetween(Node **inTree, Node256 *n, int begin, int end,
WriteContext *tls) {
for (int i = n->bitSet.firstSetGeq(begin); i >= 0 && i < end;
i = n->bitSet.firstSetGeq(i)) {
assert(n->children[i] != nullptr);
eraseTree(std::exchange(n->children[i], nullptr), tls);
n->bitSet.reset(i);
--n->numChildren;
}
if (n->numChildren + n->entryPresent >= kMinChildrenNode256) {
// nop
} else if (n->numChildren > Node16::kMaxNodes) {
auto *newNode = tls->allocate<Node48>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
getInTree(n, impl) = newNode;
tls->release(n);
(Node *&)n = newNode;
*inTree = newNode;
} else if (n->numChildren > Node3::kMaxNodes) {
auto *newNode = tls->allocate<Node16>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
tls->release(n);
*inTree = newNode;
} else if (n->numChildren > 0) {
auto *newNode = tls->allocate<Node3>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
tls->release(n);
*inTree = newNode;
} else {
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
newNode->copyChildrenAndKeyFrom(*n);
tls->release(n);
*inTree = newNode;
}
}
// Erase all nodes with a search path starting with n + [child],
// where child in [begin, end).
void eraseBetween(Node *&n, int begin, int end, WriteContext *tls,
ConflictSet::Impl *impl) {
// where child in [begin, end). To avoid the need to propagate erases up the
// search path, the caller must ensure that the result has at least one child or
// has entryPresent.
void eraseBetween(Node *&n, int begin, int end, WriteContext *tls) {
#if DEBUG_VERBOSE && !defined(NDEBUG)
fprintf(stderr, "eraseBetween: %s + [%d,%d)\n",
getSearchPathPrintable(n).c_str(), begin, end);
@@ -1930,20 +1936,21 @@ void eraseBetween(Node *&n, int begin, int end, WriteContext *tls,
case Type_Node0:
break;
case Type_Node3:
eraseBetween((Node3 *&)n, begin, end, tls, impl);
eraseBetween(&n, (Node3 *)n, begin, end, tls);
break;
case Type_Node16:
eraseBetween((Node16 *&)n, begin, end, tls, impl);
eraseBetween(&n, (Node16 *)n, begin, end, tls);
break;
case Type_Node48:
eraseBetween((Node48 *&)n, begin, end, tls, impl);
eraseBetween(&n, (Node48 *)n, begin, end, tls);
break;
case Type_Node256:
eraseBetween((Node256 *&)n, begin, end, tls, impl);
eraseBetween(&n, (Node256 *)n, begin, end, tls);
break;
default: // GCOVR_EXCL_LINE
__builtin_unreachable(); // GCOVR_EXCL_LINE
}
assert(n->numChildren > 0 || n->entryPresent);
}
// Precondition: self is not the root. May invalidate nodes along the search
@@ -3185,55 +3192,6 @@ void fixupMaxVersion(Node *node, ConflictSet::Impl *impl, WriteContext *tls) {
setMaxVersion(node, impl, max);
}
void addPrefixWrite(Node *&root, std::span<const uint8_t> begin,
std::span<const uint8_t> end, InternalVersionT writeVersion,
WriteContext *tls, ConflictSet::Impl *impl) {
++tls->accum.prefix_writes;
int lcp = begin.size() - 1;
Node **useAsRoot =
insert(&root, begin.subspan(0, lcp), writeVersion, tls, impl);
auto *beginNode =
*insert(useAsRoot, begin.subspan(lcp, 1), writeVersion, tls, impl);
const bool insertedBegin = !beginNode->entryPresent;
addKey(beginNode);
beginNode->entryPresent = true;
if (insertedBegin) {
++tls->accum.entries_inserted;
auto *p = nextLogical(beginNode);
beginNode->entry.rangeVersion =
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
beginNode->entry.pointVersion = writeVersion;
}
assert(writeVersion >= beginNode->entry.pointVersion);
beginNode->entry.pointVersion = writeVersion;
auto *endNode =
*insert(useAsRoot, end.subspan(lcp, 1), writeVersion, tls, impl);
const bool insertedEnd = !endNode->entryPresent;
addKey(endNode);
endNode->entryPresent = true;
if (insertedEnd) {
++tls->accum.entries_inserted;
auto *p = nextLogical(endNode);
endNode->entry.pointVersion =
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
}
endNode->entry.rangeVersion = writeVersion;
eraseBetween(beginNode, 0, 256, tls, impl);
// Inserting end trashed endNode's maxVersion. Fix that
fixupMaxVersion(endNode, impl, tls);
}
void addWriteRange(Node *&root, std::span<const uint8_t> begin,
std::span<const uint8_t> end, InternalVersionT writeVersion,
WriteContext *tls, ConflictSet::Impl *impl) {
@@ -3244,64 +3202,98 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
end.back() == 0) {
return addPointWrite(root, begin, writeVersion, tls, impl);
}
if (lcp == int(begin.size() - 1) && end.size() == begin.size() &&
int(begin.back()) + 1 == int(end.back())) {
return addPrefixWrite(root, begin, end, writeVersion, tls, impl);
}
++tls->accum.range_writes;
const bool beginIsPrefix = lcp == int(begin.size());
Node **useAsRoot =
insert(&root, begin.subspan(0, lcp), writeVersion, tls, impl);
begin = begin.subspan(lcp, begin.size() - lcp);
end = end.subspan(lcp, end.size() - lcp);
int consumed = lcp;
begin = begin.subspan(consumed, begin.size() - consumed);
end = end.subspan(consumed, end.size() - consumed);
auto *beginNode = *insert(useAsRoot, begin, writeVersion, tls, impl);
const bool insertedBegin = !beginNode->entryPresent;
addKey(beginNode);
beginNode->entryPresent = true;
if (insertedBegin) {
++tls->accum.entries_inserted;
auto *p = nextLogical(beginNode);
beginNode->entry.rangeVersion =
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
if (beginIsPrefix) {
auto *beginNode = *useAsRoot;
addKey(beginNode);
if (!beginNode->entryPresent) {
++tls->accum.entries_inserted;
auto *p = nextLogical(beginNode);
beginNode->entry.rangeVersion =
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
beginNode->entryPresent = true;
}
beginNode->entry.pointVersion = writeVersion;
}
assert(writeVersion >= beginNode->entry.pointVersion);
beginNode->entry.pointVersion = writeVersion;
auto *endNode = *insert(useAsRoot, end, writeVersion, tls, impl);
const bool insertedEnd = !endNode->entryPresent;
addKey(endNode);
endNode->entryPresent = true;
if (insertedEnd) {
++tls->accum.entries_inserted;
auto *p = nextLogical(endNode);
endNode->entry.pointVersion =
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
if (beginIsPrefix) {
auto *endNode = *insert(useAsRoot, end, writeVersion, tls, impl);
addKey(endNode);
if (!endNode->entryPresent) {
++tls->accum.entries_inserted;
auto *p = nextLogical(endNode);
endNode->entry.pointVersion =
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
// beginNode may have been invalidated when inserting end
beginNode = *useAsRoot;
assert(beginNode->entryPresent);
endNode->entryPresent = true;
}
}
endNode->entry.rangeVersion = writeVersion;
endNode->entry.rangeVersion = writeVersion;
for (beginNode = nextLogical(beginNode); beginNode != endNode;
beginNode = erase(beginNode, tls, impl, /*logical*/ true, endNode)) {
}
for (beginNode = nextLogical(beginNode); beginNode != endNode;
beginNode = erase(beginNode, tls, impl, /*logical*/ true, endNode)) {
}
// Inserting end trashed endNode's maxVersion. Fix that
fixupMaxVersion(endNode, impl, tls);
// Inserting end trashed endNode's maxVersion. Fix that
fixupMaxVersion(endNode, impl, tls);
} else /*!beginIsPrefix*/ {
auto *beginNode = *insert(useAsRoot, begin, writeVersion, tls, impl);
addKey(beginNode);
if (!beginNode->entryPresent) {
++tls->accum.entries_inserted;
auto *p = nextLogical(beginNode);
beginNode->entry.rangeVersion =
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
beginNode->entryPresent = true;
}
beginNode->entry.pointVersion = writeVersion;
auto *endNode = *insert(useAsRoot, end, writeVersion, tls, impl);
addKey(endNode);
if (!endNode->entryPresent) {
++tls->accum.entries_inserted;
auto *p = nextLogical(endNode);
endNode->entry.pointVersion =
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
endNode->entryPresent = true;
}
endNode->entry.rangeVersion = writeVersion;
eraseBetween(*useAsRoot, begin[0] + 1, end[0], tls);
// // Erase along left spine
// for (auto [n, key] = std::make_tuple(useAsRoot, begin);;) {
// auto before = key;
// auto **child = &getOrCreateChild(*n, key, writeVersion, tls);
// if (key.size() > 0) {
// eraseBetween(*child, int(key[0]) + 1, 256, tls);
// if ((*child)->numChildren == 1 && !(*child)->entryPresent) {
// Node *dummy = nullptr;
// mergeWithChild(*child, tls, impl, dummy,
// static_cast<Node3 *>(*child));
// key = before;
// continue;
// }
// } else {
// eraseBetween(*child, 0, 256, tls);
// beginNode = *child;
// break;
// }
// n = child;
// }
for (beginNode = nextLogical(beginNode); beginNode != endNode;
beginNode = erase(beginNode, tls, impl, /*logical*/ true, endNode)) {
}
// Inserting end trashed endNode's maxVersion. Fix that
fixupMaxVersion(endNode, impl, tls);
}
}
Node *firstGeqPhysical(Node *n, const std::span<const uint8_t> key) {
@@ -3412,7 +3404,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
if (oldestExtantVersion < writeVersion - kMaxCorrectVersionWindow)
[[unlikely]] {
if (writeVersion > newestVersionFullPrecision + kNominalVersionWindow) {
destroyTree(root, &tls.accum);
eraseTree(root, &tls);
init(writeVersion - kNominalVersionWindow);
}
@@ -3450,7 +3442,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
memory_bytes.set(totalBytes);
point_writes_total.add(tls.accum.point_writes);
prefix_writes_total.add(tls.accum.prefix_writes);
range_writes_total.add(tls.accum.range_writes);
nodes_allocated_total.add(tls.accum.nodes_allocated);
nodes_released_total.add(tls.accum.nodes_released);
@@ -3581,7 +3572,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
initMetrics();
}
~Impl() {
destroyTree(root, &tls.accum);
eraseTree(root, &tls);
safe_free(metrics, metricsCount * sizeof(metrics[0]));
}
@@ -3649,7 +3640,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
"Total number of checks where the result is \"too old\"");
COUNTER(check_bytes_total, "Total number of key bytes checked");
COUNTER(point_writes_total, "Total number of point writes");
COUNTER(prefix_writes_total, "Total number of prefix writes");
COUNTER(range_writes_total, "Total number of range writes");
GAUGE(memory_bytes, "Total number of bytes in use");
COUNTER(nodes_allocated_total,
@@ -3757,6 +3747,7 @@ InternalVersionT exchangeMaxVersion(Node *n, InternalVersionT newMax) {
}
void setMaxVersion(Node *n, ConflictSet::Impl *impl, InternalVersionT newMax) {
assert(newMax >= InternalVersionT::zero);
int index = n->parentsIndex;
n = n->parent;
if (n == nullptr) {
@@ -4267,7 +4258,7 @@ checkMaxVersion(Node *root, Node *node, InternalVersionT oldestVersion,
bool success = true;
if (node->partialKeyLen > 0) {
fprintf(stderr, "Root cannot have a partial key");
fprintf(stderr, "Root cannot have a partial key\n");
success = false;
}
checkParentPointers(node, success);

View File

@@ -164,6 +164,63 @@ double toSeconds(timeval t) {
return double(t.tv_sec) + double(t.tv_usec) * 1e-6;
}
#include <linux/perf_event.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef __linux__
struct PerfCounter {
explicit PerfCounter(int event) {
struct perf_event_attr pe;
memset(&pe, 0, sizeof(pe));
pe.type = PERF_TYPE_HARDWARE;
pe.size = sizeof(pe);
pe.config = event;
pe.inherit = 1;
pe.exclude_kernel = 1;
pe.exclude_hv = 1;
fd = perf_event_open(&pe, 0, -1, -1, 0);
if (fd == -1) {
fprintf(stderr, "Error opening leader %llx\n", pe.config);
exit(EXIT_FAILURE);
}
}
int64_t total() {
int64_t count;
if (read(fd, &count, sizeof(count)) != sizeof(count)) {
perror("read instructions from perf");
abort();
}
return count;
}
~PerfCounter() { close(fd); }
private:
int fd;
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags) {
int ret;
ret = syscall(SYS_perf_event_open, hw_event, pid, cpu, group_fd, flags);
return ret;
}
};
#else
struct PerfCounter {
explicit PerPerfCounter(int) {}
int64_t total() { return 0; }
};
#endif
int main(int argc, char **argv) {
if (argc != 3) {
goto fail;
@@ -176,6 +233,8 @@ int main(int argc, char **argv) {
int metricsCount;
cs.getMetricsV1(&metrics, &metricsCount);
PerfCounter instructions{PERF_COUNT_HW_INSTRUCTIONS};
PerfCounter cycles{PERF_COUNT_HW_CPU_CYCLES};
auto w = std::thread{workload, &cs};
for (;;) {
@@ -203,6 +262,16 @@ int main(int argc, char **argv) {
"transactions_total ";
body += std::to_string(transactions.load(std::memory_order_relaxed));
body += "\n";
body += "# HELP instructions_total Total number of instructions\n"
"# TYPE instructions_total counter\n"
"instructions_total ";
body += std::to_string(instructions.total());
body += "\n";
body += "# HELP cycles_total Total number of cycles\n"
"# TYPE cycles_total counter\n"
"cycles_total ";
body += std::to_string(cycles.total());
body += "\n";
for (int i = 0; i < metricsCount; ++i) {
body += "# HELP ";

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More