Bound memory, and disable free list for now
All checks were successful
Tests / Release [gcc] total: 827, passed: 827
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend
|:-:|:-:|:-:|:-:|:-:
|1|0|1|0|:zzz:
Tests / Release [gcc,aarch64] total: 826, passed: 826
Tests / Coverage total: 825, passed: 825
weaselab/conflict-set/pipeline/head This commit looks good
All checks were successful
Tests / Release [gcc] total: 827, passed: 827
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend
|:-:|:-:|:-:|:-:|:-:
|1|0|1|0|:zzz:
Tests / Release [gcc,aarch64] total: 826, passed: 826
Tests / Coverage total: 825, passed: 825
weaselab/conflict-set/pipeline/head This commit looks good
CC #9
This commit is contained in:
149
ConflictSet.cpp
149
ConflictSet.cpp
@@ -275,8 +275,8 @@ static_assert(sizeof(Node16) < kMinChildrenNode16 * kBytesPerKey);
|
|||||||
static_assert(sizeof(Node4) < kMinChildrenNode4 * kBytesPerKey);
|
static_assert(sizeof(Node4) < kMinChildrenNode4 * kBytesPerKey);
|
||||||
static_assert(sizeof(Node0) < kBytesPerKey);
|
static_assert(sizeof(Node0) < kBytesPerKey);
|
||||||
|
|
||||||
template <class T, int64_t kMemoryBound = (1 << 20),
|
// TODO revive freeList? It's making bounding memory usage tricky
|
||||||
int64_t kMaxIndividual = (1 << 10)>
|
template <class T, int64_t kMemoryBound = 0, int64_t kMaxIndividual = (1 << 10)>
|
||||||
struct BoundedFreeListAllocator {
|
struct BoundedFreeListAllocator {
|
||||||
static_assert(sizeof(T) >= sizeof(void *));
|
static_assert(sizeof(T) >= sizeof(void *));
|
||||||
static_assert(std::derived_from<T, Node>);
|
static_assert(std::derived_from<T, Node>);
|
||||||
@@ -464,6 +464,8 @@ int64_t getChildMaxVersion(Node *self, uint8_t index) {
|
|||||||
// Precondition - an entry for index must exist in the node
|
// Precondition - an entry for index must exist in the node
|
||||||
int64_t &maxVersion(Node *n, ConflictSet::Impl *);
|
int64_t &maxVersion(Node *n, ConflictSet::Impl *);
|
||||||
|
|
||||||
|
Node *&getRoot(ConflictSet::Impl *);
|
||||||
|
|
||||||
Node *getChild(Node *self, uint8_t index) {
|
Node *getChild(Node *self, uint8_t index) {
|
||||||
if (self->type <= Type::Node16) {
|
if (self->type <= Type::Node16) {
|
||||||
auto *self16 = static_cast<Node16 *>(self);
|
auto *self16 = static_cast<Node16 *>(self);
|
||||||
@@ -711,8 +713,107 @@ Node *&getOrCreateChild(Node *&self, uint8_t index,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string getSearchPathPrintable(Node *n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void maybeDecreaseCapacity(Node *&self, Node *&root,
|
||||||
|
NodeAllocators *allocators) {
|
||||||
|
if (self->numChildren > 0 &&
|
||||||
|
self->numChildren * self->partialKeyLen >= self->partialKeyCapacity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newCapacity =
|
||||||
|
std::max(self->partialKeyLen, self->numChildren * self->partialKeyLen);
|
||||||
|
|
||||||
|
switch (self->type) {
|
||||||
|
case Type::Node0: {
|
||||||
|
auto *self0 = (Node0 *)self;
|
||||||
|
auto *newSelf = allocators->node0.allocate(newCapacity);
|
||||||
|
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
|
||||||
|
kNodeCopySize);
|
||||||
|
memcpy(newSelf->partialKey(), self0->partialKey(), self->partialKeyLen);
|
||||||
|
(self->parent ? getChildExists(self->parent, self->parentsIndex) : root) =
|
||||||
|
newSelf;
|
||||||
|
allocators->node0.release(self0);
|
||||||
|
self = newSelf;
|
||||||
|
} break;
|
||||||
|
case Type::Node4: {
|
||||||
|
auto *self4 = (Node4 *)self;
|
||||||
|
auto *newSelf = allocators->node4.allocate(newCapacity);
|
||||||
|
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
|
||||||
|
kNodeCopySize);
|
||||||
|
memcpy(newSelf->partialKey(), self4->partialKey(), self->partialKeyLen);
|
||||||
|
// TODO replace with memcpy?
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
newSelf->index[i] = self4->index[i];
|
||||||
|
newSelf->children[i] = self4->children[i];
|
||||||
|
}
|
||||||
|
(self->parent ? getChildExists(self->parent, self->parentsIndex) : root) =
|
||||||
|
newSelf;
|
||||||
|
setChildrenParents(newSelf);
|
||||||
|
allocators->node4.release(self4);
|
||||||
|
self = newSelf;
|
||||||
|
} break;
|
||||||
|
case Type::Node16: {
|
||||||
|
auto *self16 = (Node16 *)self;
|
||||||
|
auto *newSelf = allocators->node16.allocate(newCapacity);
|
||||||
|
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
|
||||||
|
kNodeCopySize);
|
||||||
|
memcpy(newSelf->partialKey(), self16->partialKey(), self->partialKeyLen);
|
||||||
|
// TODO replace with memcpy?
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
newSelf->index[i] = self16->index[i];
|
||||||
|
newSelf->children[i] = self16->children[i];
|
||||||
|
}
|
||||||
|
(self->parent ? getChildExists(self->parent, self->parentsIndex) : root) =
|
||||||
|
newSelf;
|
||||||
|
setChildrenParents(newSelf);
|
||||||
|
allocators->node16.release(self16);
|
||||||
|
self = newSelf;
|
||||||
|
} break;
|
||||||
|
case Type::Node48: {
|
||||||
|
auto *self48 = (Node48 *)self;
|
||||||
|
auto *newSelf = allocators->node48.allocate(newCapacity);
|
||||||
|
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
|
||||||
|
kNodeCopySize);
|
||||||
|
memcpy(newSelf->partialKey(), self48->partialKey(), self->partialKeyLen);
|
||||||
|
newSelf->bitSet = self48->bitSet;
|
||||||
|
newSelf->bitSet.forEachInRange(
|
||||||
|
[&](int c) {
|
||||||
|
int index = newSelf->nextFree;
|
||||||
|
newSelf->index[c] = index;
|
||||||
|
newSelf->children[index] = self48->children[self48->index[c]];
|
||||||
|
++newSelf->nextFree;
|
||||||
|
},
|
||||||
|
0, 256);
|
||||||
|
(self->parent ? getChildExists(self->parent, self->parentsIndex) : root) =
|
||||||
|
newSelf;
|
||||||
|
setChildrenParents(newSelf);
|
||||||
|
allocators->node48.release(self48);
|
||||||
|
self = newSelf;
|
||||||
|
} break;
|
||||||
|
case Type::Node256: {
|
||||||
|
auto *self256 = (Node256 *)self;
|
||||||
|
auto *newSelf = allocators->node256.allocate(newCapacity);
|
||||||
|
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
|
||||||
|
kNodeCopySize);
|
||||||
|
memcpy(newSelf->partialKey(), self256->partialKey(), self->partialKeyLen);
|
||||||
|
newSelf->bitSet = self256->bitSet;
|
||||||
|
newSelf->bitSet.forEachInRange(
|
||||||
|
[&](int c) { newSelf->children[c] = self256->children[c]; }, 0, 256);
|
||||||
|
(self->parent ? getChildExists(self->parent, self->parentsIndex) : root) =
|
||||||
|
newSelf;
|
||||||
|
setChildrenParents(newSelf);
|
||||||
|
allocators->node256.release(self256);
|
||||||
|
self = newSelf;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO fuse into erase child so we don't need to repeat branches on type
|
// TODO fuse into erase child so we don't need to repeat branches on type
|
||||||
void maybeDownsize(Node *self, Node *&root, NodeAllocators *allocators,
|
void maybeDownsize(Node *&self, Node *&root, NodeAllocators *allocators,
|
||||||
ConflictSet::Impl *impl) {
|
ConflictSet::Impl *impl) {
|
||||||
switch (self->type) {
|
switch (self->type) {
|
||||||
case Type::Node0:
|
case Type::Node0:
|
||||||
@@ -732,6 +833,7 @@ void maybeDownsize(Node *self, Node *&root, NodeAllocators *allocators,
|
|||||||
}
|
}
|
||||||
|
|
||||||
allocators->node4.release(self4);
|
allocators->node4.release(self4);
|
||||||
|
self = newSelf;
|
||||||
} else if (self->numChildren == 1) {
|
} else if (self->numChildren == 1) {
|
||||||
if (!self->entryPresent) {
|
if (!self->entryPresent) {
|
||||||
auto *child = self4->children[0].child;
|
auto *child = self4->children[0].child;
|
||||||
@@ -773,6 +875,7 @@ void maybeDownsize(Node *self, Node *&root, NodeAllocators *allocators,
|
|||||||
maxVersion(child, impl) = childMaxVersion;
|
maxVersion(child, impl) = childMaxVersion;
|
||||||
|
|
||||||
allocators->node4.release(self4);
|
allocators->node4.release(self4);
|
||||||
|
self = child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -795,6 +898,7 @@ void maybeDownsize(Node *self, Node *&root, NodeAllocators *allocators,
|
|||||||
} else {
|
} else {
|
||||||
getChildExists(newSelf->parent, newSelf->parentsIndex) = newSelf;
|
getChildExists(newSelf->parent, newSelf->parentsIndex) = newSelf;
|
||||||
}
|
}
|
||||||
|
self = newSelf;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::Node48:
|
case Type::Node48:
|
||||||
@@ -821,6 +925,7 @@ void maybeDownsize(Node *self, Node *&root, NodeAllocators *allocators,
|
|||||||
} else {
|
} else {
|
||||||
getChildExists(newSelf->parent, newSelf->parentsIndex) = newSelf;
|
getChildExists(newSelf->parent, newSelf->parentsIndex) = newSelf;
|
||||||
}
|
}
|
||||||
|
self = newSelf;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::Node256:
|
case Type::Node256:
|
||||||
@@ -847,6 +952,7 @@ void maybeDownsize(Node *self, Node *&root, NodeAllocators *allocators,
|
|||||||
} else {
|
} else {
|
||||||
getChildExists(newSelf->parent, newSelf->parentsIndex) = newSelf;
|
getChildExists(newSelf->parent, newSelf->parentsIndex) = newSelf;
|
||||||
}
|
}
|
||||||
|
self = newSelf;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -907,6 +1013,7 @@ void eraseChild(Node *self, uint8_t index, NodeAllocators *allocators,
|
|||||||
eraseChild(self->parent, self->parentsIndex, allocators, root, impl);
|
eraseChild(self->parent, self->parentsIndex, allocators, root, impl);
|
||||||
} else {
|
} else {
|
||||||
maybeDownsize(self, root, allocators, impl);
|
maybeDownsize(self, root, allocators, impl);
|
||||||
|
maybeDecreaseCapacity(self, root, allocators);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1131,10 +1238,6 @@ struct SearchStepWise {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::string getSearchPathPrintable(Node *n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logically this is the same as performing firstGeq and then checking against
|
// Logically this is the same as performing firstGeq and then checking against
|
||||||
// point or range version according to cmp, but this version short circuits as
|
// point or range version according to cmp, but this version short circuits as
|
||||||
// soon as it can prove that there's no conflict.
|
// soon as it can prove that there's no conflict.
|
||||||
@@ -1750,6 +1853,19 @@ template <bool kBegin>
|
|||||||
memmove(old->partialKey(), old->partialKey() + partialKeyIndex + 1,
|
memmove(old->partialKey(), old->partialKey() + partialKeyIndex + 1,
|
||||||
old->partialKeyLen - (partialKeyIndex + 1));
|
old->partialKeyLen - (partialKeyIndex + 1));
|
||||||
old->partialKeyLen -= partialKeyIndex + 1;
|
old->partialKeyLen -= partialKeyIndex + 1;
|
||||||
|
|
||||||
|
[[maybe_unused]] int oldCap = old->partialKeyCapacity;
|
||||||
|
maybeDecreaseCapacity(old, getRoot(impl), allocators);
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
if (old->partialKeyCapacity < oldCap) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: Length: %d, capacity: %d, numChildren: %d, oldCapacity: "
|
||||||
|
"%d\n",
|
||||||
|
getSearchPathPrintable(old).c_str(), old->partialKeyLen,
|
||||||
|
old->partialKeyCapacity, old->numChildren, oldCap);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
key = key.subspan(partialKeyIndex, key.size() - partialKeyIndex);
|
key = key.subspan(partialKeyIndex, key.size() - partialKeyIndex);
|
||||||
|
|
||||||
@@ -2106,6 +2222,8 @@ int64_t &maxVersion(Node *n, ConflictSet::Impl *impl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node *&getRoot(ConflictSet::Impl *impl) { return impl->root; }
|
||||||
|
|
||||||
// ==================== END IMPLEMENTATION ====================
|
// ==================== END IMPLEMENTATION ====================
|
||||||
|
|
||||||
// GCOVR_EXCL_START
|
// GCOVR_EXCL_START
|
||||||
@@ -2377,7 +2495,7 @@ Iterator firstGeq(Node *n, std::string_view key) {
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] void checkMinChildCount(Node *node, bool &success) {
|
[[maybe_unused]] void checkMemoryBoundInvariants(Node *node, bool &success) {
|
||||||
int minNumChildren;
|
int minNumChildren;
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case Type::Node0:
|
case Type::Node0:
|
||||||
@@ -2397,16 +2515,25 @@ Iterator firstGeq(Node *n, std::string_view key) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (node->numChildren < minNumChildren) {
|
if (node->numChildren < minNumChildren) {
|
||||||
Arena arena;
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%s has %d children, which is less than the minimum required %d\n",
|
"%s has %d children, which is less than the minimum required %d\n",
|
||||||
getSearchPathPrintable(node).c_str(), node->numChildren,
|
getSearchPathPrintable(node).c_str(), node->numChildren,
|
||||||
minNumChildren);
|
minNumChildren);
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
if (node->numChildren > 0 &&
|
||||||
|
node->numChildren * node->partialKeyLen < node->partialKeyCapacity) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s has %d children, partial key length %d, and partial key "
|
||||||
|
"capacity %d. It's required that nodes with children have children "
|
||||||
|
"* length >= capacity\n",
|
||||||
|
getSearchPathPrintable(node).c_str(), node->numChildren,
|
||||||
|
node->partialKeyLen, node->partialKeyCapacity);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
for (int i = getChildGeq(node, 0); i >= 0; i = getChildGeq(node, i + 1)) {
|
for (int i = getChildGeq(node, 0); i >= 0; i = getChildGeq(node, i + 1)) {
|
||||||
auto *child = getChildExists(node, i);
|
auto *child = getChildExists(node, i);
|
||||||
checkMinChildCount(child, success);
|
checkMemoryBoundInvariants(child, success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2417,7 +2544,7 @@ bool checkCorrectness(Node *node, int64_t oldestVersion,
|
|||||||
checkParentPointers(node, success);
|
checkParentPointers(node, success);
|
||||||
checkMaxVersion(node, node, oldestVersion, success, impl);
|
checkMaxVersion(node, node, oldestVersion, success, impl);
|
||||||
checkEntriesExist(node, success);
|
checkEntriesExist(node, success);
|
||||||
checkMinChildCount(node, success);
|
checkMemoryBoundInvariants(node, success);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user