Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a132799a4 | |||
| 72469ebb6e | |||
| 6c79847a42 | |||
| 405a2ca161 | |||
| f93466316a | |||
| 5626cd09d9 | |||
| 41840220c3 | |||
| 7ff00e7846 | |||
| 6242f40d48 | |||
| 403d70a1d3 | |||
| 9763452713 | |||
| 73d0593fca | |||
| 23c2a3e1c6 | |||
| a64e792964 | |||
| 5e362d5330 | |||
| cc526cb6ba | |||
| 7e49888bec | |||
| e64ebabced | |||
| 1e34951a77 | |||
| baf64520d6 | |||
| 3499626127 | |||
| b7f9084694 |
+264
-273
@@ -195,7 +195,6 @@ struct Node {
|
|||||||
/* end section that's copied to the next node */
|
/* end section that's copied to the next node */
|
||||||
|
|
||||||
uint8_t *partialKey();
|
uint8_t *partialKey();
|
||||||
size_t size() const;
|
|
||||||
|
|
||||||
Type getType() const { return type; }
|
Type getType() const { return type; }
|
||||||
int32_t getCapacity() const { return partialKeyCapacity; }
|
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
|
// A type that's plumbed along the check call tree. Lifetime ends after each
|
||||||
// check call.
|
// check call.
|
||||||
struct ReadContext {
|
struct ReadContext {
|
||||||
@@ -818,7 +800,6 @@ struct WriteContext {
|
|||||||
int64_t nodes_allocated;
|
int64_t nodes_allocated;
|
||||||
int64_t nodes_released;
|
int64_t nodes_released;
|
||||||
int64_t point_writes;
|
int64_t point_writes;
|
||||||
int64_t prefix_writes;
|
|
||||||
int64_t range_writes;
|
int64_t range_writes;
|
||||||
int64_t write_bytes;
|
int64_t write_bytes;
|
||||||
} accum;
|
} 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,
|
void maybeDownsize(Node *self, WriteContext *tls, ConflictSet::Impl *impl,
|
||||||
Node *&dontInvalidate) {
|
Node *&dontInvalidate) {
|
||||||
|
|
||||||
@@ -1642,45 +1663,7 @@ void maybeDownsize(Node *self, WriteContext *tls, ConflictSet::Impl *impl,
|
|||||||
getInTree(self, impl) = newSelf;
|
getInTree(self, impl) = newSelf;
|
||||||
tls->release(self3);
|
tls->release(self3);
|
||||||
} else if (self->numChildren == 1 && !self->entryPresent) {
|
} else if (self->numChildren == 1 && !self->entryPresent) {
|
||||||
auto *child = self3->children[0];
|
mergeWithChild(getInTree(self, impl), tls, impl, dontInvalidate, self3);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case Type_Node16:
|
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;
|
Arena arena;
|
||||||
auto toFree = vector<Node *>(arena);
|
auto toFree = vector<Node *>(arena);
|
||||||
toFree.push_back(root);
|
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) {
|
while (toFree.size() > 0) {
|
||||||
auto *n = toFree.back();
|
auto *n = toFree.back();
|
||||||
toFree.pop_back();
|
toFree.pop_back();
|
||||||
accum->entries_erased += n->entryPresent;
|
tls->accum.entries_erased += n->entryPresent;
|
||||||
++accum->nodes_released;
|
++tls->accum.nodes_released;
|
||||||
|
|
||||||
|
removeNode(n);
|
||||||
|
removeKey(n);
|
||||||
|
|
||||||
switch (n->getType()) {
|
switch (n->getType()) {
|
||||||
case Type_Node0: {
|
case Type_Node0: {
|
||||||
|
auto *n0 = static_cast<Node0 *>(n);
|
||||||
|
tls->release(n0);
|
||||||
} break;
|
} break;
|
||||||
case Type_Node3: {
|
case Type_Node3: {
|
||||||
auto *n3 = static_cast<Node3 *>(n);
|
auto *n3 = static_cast<Node3 *>(n);
|
||||||
toFree.append(std::span<Node *>(n3->children, n3->numChildren));
|
toFree.append(std::span<Node *>(n3->children, n3->numChildren));
|
||||||
|
tls->release(n3);
|
||||||
} break;
|
} break;
|
||||||
case Type_Node16: {
|
case Type_Node16: {
|
||||||
auto *n16 = static_cast<Node16 *>(n);
|
auto *n16 = static_cast<Node16 *>(n);
|
||||||
toFree.append(std::span<Node *>(n16->children, n16->numChildren));
|
toFree.append(std::span<Node *>(n16->children, n16->numChildren));
|
||||||
|
tls->release(n16);
|
||||||
} break;
|
} break;
|
||||||
case Type_Node48: {
|
case Type_Node48: {
|
||||||
auto *n48 = static_cast<Node48 *>(n);
|
auto *n48 = static_cast<Node48 *>(n);
|
||||||
toFree.append(std::span<Node *>(n48->children, n48->numChildren));
|
toFree.append(std::span<Node *>(n48->children, n48->numChildren));
|
||||||
|
tls->release(n48);
|
||||||
} break;
|
} break;
|
||||||
case Type_Node256: {
|
case Type_Node256: {
|
||||||
auto *n256 = static_cast<Node256 *>(n);
|
auto *n256 = static_cast<Node256 *>(n);
|
||||||
auto *out = toFree.unsafePrepareAppend(n256->numChildren).data();
|
auto *out = toFree.unsafePrepareAppend(n256->numChildren).data();
|
||||||
n256->bitSet.forEachSet([&](int i) { *out++ = n256->children[i]; });
|
n256->bitSet.forEachSet([&](int i) { *out++ = n256->children[i]; });
|
||||||
assert(out == toFree.end());
|
assert(out == toFree.end());
|
||||||
|
tls->release(n256);
|
||||||
} break;
|
} break;
|
||||||
default: // GCOVR_EXCL_LINE
|
default: // GCOVR_EXCL_LINE
|
||||||
__builtin_unreachable(); // 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,
|
void eraseBetween(Node **inTree, Node3 *n, int begin, int end,
|
||||||
ConflictSet::Impl *impl) {
|
WriteContext *tls) {
|
||||||
const unsigned shiftUpperBound = end - begin;
|
const unsigned shiftUpperBound = end - begin;
|
||||||
const unsigned shiftAmount = begin;
|
const unsigned shiftAmount = begin;
|
||||||
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
|
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;
|
InternalVersionT *maxVOut = n->childMaxVersion;
|
||||||
for (int i = 0; i < n->numChildren; ++i) {
|
for (int i = 0; i < n->numChildren; ++i) {
|
||||||
if (inBounds(n->index[i])) {
|
if (inBounds(n->index[i])) {
|
||||||
destroyTree(n->children[i], &tls->accum);
|
eraseTree(n->children[i], tls);
|
||||||
} else {
|
} else {
|
||||||
*nodeOut++ = n->children[i];
|
*nodeOut++ = n->children[i];
|
||||||
*indexOut++ = n->index[i];
|
*indexOut++ = n->index[i];
|
||||||
@@ -1784,144 +1767,167 @@ void eraseBetween(Node3 *&n, int begin, int end, WriteContext *tls,
|
|||||||
if (n->numChildren == 0) {
|
if (n->numChildren == 0) {
|
||||||
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
|
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
|
||||||
newNode->copyChildrenAndKeyFrom(*n);
|
newNode->copyChildrenAndKeyFrom(*n);
|
||||||
getInTree(n, impl) = newNode;
|
|
||||||
tls->release(n);
|
tls->release(n);
|
||||||
(Node *&)n = newNode;
|
*inTree = newNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void eraseBetween(Node16 *&n, int begin, int end, WriteContext *tls,
|
void eraseBetween(Node **inTree, Node16 *n, int begin, int end,
|
||||||
ConflictSet::Impl *impl) {
|
WriteContext *tls) {
|
||||||
const unsigned shiftUpperBound = end - begin;
|
if (end - begin == 256) {
|
||||||
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) {
|
for (int i = 0; i < n->numChildren; ++i) {
|
||||||
if (inBounds(n->index[i])) {
|
eraseTree(n->children[i], tls);
|
||||||
destroyTree(n->children[i], &tls->accum);
|
|
||||||
} else {
|
|
||||||
*nodeOut++ = n->children[i];
|
|
||||||
*indexOut++ = n->index[i];
|
|
||||||
*maxVOut++ = n->childMaxVersion[i];
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
n->numChildren = nodeOut - n->children;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
auto *newNode = tls->allocate<Node16>(n->partialKeyLen);
|
|
||||||
newNode->copyChildrenAndKeyFrom(*n);
|
|
||||||
getInTree(n, impl) = newNode;
|
|
||||||
tls->release(n);
|
|
||||||
(Node *&)n = 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;
|
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);
|
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
|
||||||
newNode->copyChildrenAndKeyFrom(*n);
|
newNode->copyChildrenAndKeyFrom(*n);
|
||||||
getInTree(n, impl) = newNode;
|
|
||||||
tls->release(n);
|
tls->release(n);
|
||||||
(Node *&)n = newNode;
|
*inTree = newNode;
|
||||||
} else if (n->numChildren <= Node3::kMaxNodes) {
|
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; };
|
||||||
|
uint32_t mask = 0;
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
mask |= inBounds(is[i]) << i;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
mask &= (decltype(mask)(1) << n->numChildren) - 1;
|
||||||
|
|
||||||
|
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);
|
auto *newNode = tls->allocate<Node3>(n->partialKeyLen);
|
||||||
newNode->copyChildrenAndKeyFrom(*n);
|
newNode->copyChildrenAndKeyFrom(*n);
|
||||||
getInTree(n, impl) = newNode;
|
|
||||||
tls->release(n);
|
tls->release(n);
|
||||||
(Node *&)n = newNode;
|
*inTree = newNode;
|
||||||
} else if (n->numChildren <= Node16::kMaxNodes) {
|
} else {
|
||||||
|
auto *newNode = tls->allocate<Node0>(n->partialKeyLen);
|
||||||
|
newNode->copyChildrenAndKeyFrom(*n);
|
||||||
|
tls->release(n);
|
||||||
|
*inTree = newNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->numChildren + n->entryPresent >= kMinChildrenNode48) {
|
||||||
|
// nop
|
||||||
|
} else if (n->numChildren > Node3::kMaxNodes) {
|
||||||
auto *newNode = tls->allocate<Node16>(n->partialKeyLen);
|
auto *newNode = tls->allocate<Node16>(n->partialKeyLen);
|
||||||
newNode->copyChildrenAndKeyFrom(*n);
|
newNode->copyChildrenAndKeyFrom(*n);
|
||||||
getInTree(n, impl) = newNode;
|
|
||||||
tls->release(n);
|
tls->release(n);
|
||||||
(Node *&)n = newNode;
|
*inTree = newNode;
|
||||||
} else if (n->numChildren <= Node48::kMaxNodes) {
|
} 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(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);
|
auto *newNode = tls->allocate<Node48>(n->partialKeyLen);
|
||||||
newNode->copyChildrenAndKeyFrom(*n);
|
newNode->copyChildrenAndKeyFrom(*n);
|
||||||
getInTree(n, impl) = newNode;
|
|
||||||
tls->release(n);
|
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],
|
// Erase all nodes with a search path starting with n + [child],
|
||||||
// where child in [begin, end).
|
// where child in [begin, end). To avoid the need to propagate erases up the
|
||||||
void eraseBetween(Node *&n, int begin, int end, WriteContext *tls,
|
// search path, the caller must ensure that the result has at least one child or
|
||||||
ConflictSet::Impl *impl) {
|
// has entryPresent.
|
||||||
|
void eraseBetween(Node *&n, int begin, int end, WriteContext *tls) {
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
fprintf(stderr, "eraseBetween: %s + [%d,%d)\n",
|
fprintf(stderr, "eraseBetween: %s + [%d,%d)\n",
|
||||||
getSearchPathPrintable(n).c_str(), begin, end);
|
getSearchPathPrintable(n).c_str(), begin, end);
|
||||||
@@ -1930,20 +1936,21 @@ void eraseBetween(Node *&n, int begin, int end, WriteContext *tls,
|
|||||||
case Type_Node0:
|
case Type_Node0:
|
||||||
break;
|
break;
|
||||||
case Type_Node3:
|
case Type_Node3:
|
||||||
eraseBetween((Node3 *&)n, begin, end, tls, impl);
|
eraseBetween(&n, (Node3 *)n, begin, end, tls);
|
||||||
break;
|
break;
|
||||||
case Type_Node16:
|
case Type_Node16:
|
||||||
eraseBetween((Node16 *&)n, begin, end, tls, impl);
|
eraseBetween(&n, (Node16 *)n, begin, end, tls);
|
||||||
break;
|
break;
|
||||||
case Type_Node48:
|
case Type_Node48:
|
||||||
eraseBetween((Node48 *&)n, begin, end, tls, impl);
|
eraseBetween(&n, (Node48 *)n, begin, end, tls);
|
||||||
break;
|
break;
|
||||||
case Type_Node256:
|
case Type_Node256:
|
||||||
eraseBetween((Node256 *&)n, begin, end, tls, impl);
|
eraseBetween(&n, (Node256 *)n, begin, end, tls);
|
||||||
break;
|
break;
|
||||||
default: // GCOVR_EXCL_LINE
|
default: // GCOVR_EXCL_LINE
|
||||||
__builtin_unreachable(); // 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
|
// 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);
|
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,
|
void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
||||||
std::span<const uint8_t> end, InternalVersionT writeVersion,
|
std::span<const uint8_t> end, InternalVersionT writeVersion,
|
||||||
WriteContext *tls, ConflictSet::Impl *impl) {
|
WriteContext *tls, ConflictSet::Impl *impl) {
|
||||||
@@ -3244,55 +3202,36 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
|||||||
end.back() == 0) {
|
end.back() == 0) {
|
||||||
return addPointWrite(root, begin, writeVersion, tls, impl);
|
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;
|
++tls->accum.range_writes;
|
||||||
const bool beginIsPrefix = lcp == int(begin.size());
|
const bool beginIsPrefix = lcp == int(begin.size());
|
||||||
|
|
||||||
Node **useAsRoot =
|
Node **useAsRoot =
|
||||||
insert(&root, begin.subspan(0, lcp), writeVersion, tls, impl);
|
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;
|
if (beginIsPrefix) {
|
||||||
|
auto *beginNode = *useAsRoot;
|
||||||
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);
|
addKey(beginNode);
|
||||||
beginNode->entryPresent = true;
|
if (!beginNode->entryPresent) {
|
||||||
|
|
||||||
if (insertedBegin) {
|
|
||||||
++tls->accum.entries_inserted;
|
++tls->accum.entries_inserted;
|
||||||
auto *p = nextLogical(beginNode);
|
auto *p = nextLogical(beginNode);
|
||||||
beginNode->entry.rangeVersion =
|
beginNode->entry.rangeVersion =
|
||||||
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
||||||
beginNode->entry.pointVersion = writeVersion;
|
beginNode->entryPresent = true;
|
||||||
}
|
}
|
||||||
assert(writeVersion >= beginNode->entry.pointVersion);
|
|
||||||
beginNode->entry.pointVersion = writeVersion;
|
beginNode->entry.pointVersion = writeVersion;
|
||||||
|
|
||||||
auto *endNode = *insert(useAsRoot, end, writeVersion, tls, impl);
|
auto *endNode = *insert(useAsRoot, end, writeVersion, tls, impl);
|
||||||
|
|
||||||
const bool insertedEnd = !endNode->entryPresent;
|
|
||||||
|
|
||||||
addKey(endNode);
|
addKey(endNode);
|
||||||
endNode->entryPresent = true;
|
if (!endNode->entryPresent) {
|
||||||
|
|
||||||
if (insertedEnd) {
|
|
||||||
++tls->accum.entries_inserted;
|
++tls->accum.entries_inserted;
|
||||||
auto *p = nextLogical(endNode);
|
auto *p = nextLogical(endNode);
|
||||||
endNode->entry.pointVersion =
|
endNode->entry.pointVersion =
|
||||||
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
p == nullptr ? tls->zero : std::max(p->entry.rangeVersion, tls->zero);
|
||||||
if (beginIsPrefix) {
|
|
||||||
// beginNode may have been invalidated when inserting end
|
// beginNode may have been invalidated when inserting end
|
||||||
beginNode = *useAsRoot;
|
beginNode = *useAsRoot;
|
||||||
assert(beginNode->entryPresent);
|
endNode->entryPresent = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
endNode->entry.rangeVersion = writeVersion;
|
endNode->entry.rangeVersion = writeVersion;
|
||||||
|
|
||||||
@@ -3302,6 +3241,59 @@ void addWriteRange(Node *&root, std::span<const uint8_t> begin,
|
|||||||
|
|
||||||
// Inserting end trashed endNode's maxVersion. Fix that
|
// Inserting end trashed endNode's maxVersion. Fix that
|
||||||
fixupMaxVersion(endNode, impl, tls);
|
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) {
|
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)
|
if (oldestExtantVersion < writeVersion - kMaxCorrectVersionWindow)
|
||||||
[[unlikely]] {
|
[[unlikely]] {
|
||||||
if (writeVersion > newestVersionFullPrecision + kNominalVersionWindow) {
|
if (writeVersion > newestVersionFullPrecision + kNominalVersionWindow) {
|
||||||
destroyTree(root, &tls.accum);
|
eraseTree(root, &tls);
|
||||||
init(writeVersion - kNominalVersionWindow);
|
init(writeVersion - kNominalVersionWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3450,7 +3442,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
|
|
||||||
memory_bytes.set(totalBytes);
|
memory_bytes.set(totalBytes);
|
||||||
point_writes_total.add(tls.accum.point_writes);
|
point_writes_total.add(tls.accum.point_writes);
|
||||||
prefix_writes_total.add(tls.accum.prefix_writes);
|
|
||||||
range_writes_total.add(tls.accum.range_writes);
|
range_writes_total.add(tls.accum.range_writes);
|
||||||
nodes_allocated_total.add(tls.accum.nodes_allocated);
|
nodes_allocated_total.add(tls.accum.nodes_allocated);
|
||||||
nodes_released_total.add(tls.accum.nodes_released);
|
nodes_released_total.add(tls.accum.nodes_released);
|
||||||
@@ -3581,7 +3572,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
initMetrics();
|
initMetrics();
|
||||||
}
|
}
|
||||||
~Impl() {
|
~Impl() {
|
||||||
destroyTree(root, &tls.accum);
|
eraseTree(root, &tls);
|
||||||
safe_free(metrics, metricsCount * sizeof(metrics[0]));
|
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\"");
|
"Total number of checks where the result is \"too old\"");
|
||||||
COUNTER(check_bytes_total, "Total number of key bytes checked");
|
COUNTER(check_bytes_total, "Total number of key bytes checked");
|
||||||
COUNTER(point_writes_total, "Total number of point writes");
|
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");
|
COUNTER(range_writes_total, "Total number of range writes");
|
||||||
GAUGE(memory_bytes, "Total number of bytes in use");
|
GAUGE(memory_bytes, "Total number of bytes in use");
|
||||||
COUNTER(nodes_allocated_total,
|
COUNTER(nodes_allocated_total,
|
||||||
@@ -3757,6 +3747,7 @@ InternalVersionT exchangeMaxVersion(Node *n, InternalVersionT newMax) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setMaxVersion(Node *n, ConflictSet::Impl *impl, InternalVersionT newMax) {
|
void setMaxVersion(Node *n, ConflictSet::Impl *impl, InternalVersionT newMax) {
|
||||||
|
assert(newMax >= InternalVersionT::zero);
|
||||||
int index = n->parentsIndex;
|
int index = n->parentsIndex;
|
||||||
n = n->parent;
|
n = n->parent;
|
||||||
if (n == nullptr) {
|
if (n == nullptr) {
|
||||||
@@ -4267,7 +4258,7 @@ checkMaxVersion(Node *root, Node *node, InternalVersionT oldestVersion,
|
|||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
if (node->partialKeyLen > 0) {
|
if (node->partialKeyLen > 0) {
|
||||||
fprintf(stderr, "Root cannot have a partial key");
|
fprintf(stderr, "Root cannot have a partial key\n");
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
checkParentPointers(node, success);
|
checkParentPointers(node, success);
|
||||||
|
|||||||
@@ -164,6 +164,63 @@ double toSeconds(timeval t) {
|
|||||||
return double(t.tv_sec) + double(t.tv_usec) * 1e-6;
|
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) {
|
int main(int argc, char **argv) {
|
||||||
if (argc != 3) {
|
if (argc != 3) {
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -176,6 +233,8 @@ int main(int argc, char **argv) {
|
|||||||
int metricsCount;
|
int metricsCount;
|
||||||
cs.getMetricsV1(&metrics, &metricsCount);
|
cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
|
|
||||||
|
PerfCounter instructions{PERF_COUNT_HW_INSTRUCTIONS};
|
||||||
|
PerfCounter cycles{PERF_COUNT_HW_CPU_CYCLES};
|
||||||
auto w = std::thread{workload, &cs};
|
auto w = std::thread{workload, &cs};
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -203,6 +262,16 @@ int main(int argc, char **argv) {
|
|||||||
"transactions_total ";
|
"transactions_total ";
|
||||||
body += std::to_string(transactions.load(std::memory_order_relaxed));
|
body += std::to_string(transactions.load(std::memory_order_relaxed));
|
||||||
body += "\n";
|
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) {
|
for (int i = 0; i < metricsCount; ++i) {
|
||||||
body += "# HELP ";
|
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
Reference in New Issue
Block a user