WIP my understanding of the memory bound induction was wrong

This commit is contained in:
2024-03-12 15:44:25 -07:00
parent aefb83dbc6
commit 75c304bbe7

View File

@@ -261,17 +261,20 @@ struct Node256 : Node {
// Bound memory usage following the analysis in the ART paper // Bound memory usage following the analysis in the ART paper
constexpr int kBytesPerKey = 121; constexpr int kBytesPerKey = 120;
constexpr int kMinChildrenNode4 = 1; constexpr int kMinChildrenNode4 = 2;
constexpr int kMinChildrenNode16 = 5; constexpr int kMinChildrenNode16 = 5;
constexpr int kMinChildrenNode48 = 17; constexpr int kMinChildrenNode48 = 17;
constexpr int kMinChildrenNode256 = 49; constexpr int kMinChildrenNode256 = 49;
static_assert(sizeof(Node256) < kMinChildrenNode256 * kBytesPerKey); static_assert(sizeof(Node256) + kBytesPerKey <=
static_assert(sizeof(Node48) < kMinChildrenNode48 * kBytesPerKey); kMinChildrenNode256 * kBytesPerKey);
static_assert(sizeof(Node16) < kMinChildrenNode16 * kBytesPerKey); static_assert(sizeof(Node48) + kBytesPerKey <=
static_assert(sizeof(Node4) < kMinChildrenNode4 * kBytesPerKey); kMinChildrenNode48 * kBytesPerKey);
static_assert(sizeof(Node0) < kBytesPerKey); static_assert(sizeof(Node16) + kBytesPerKey <=
kMinChildrenNode16 * kBytesPerKey);
static_assert(sizeof(Node4) + kBytesPerKey <= kMinChildrenNode4 * kBytesPerKey);
static_assert(sizeof(Node0) <= kBytesPerKey);
// setOldestVersion will additionally try to maintain this property: // setOldestVersion will additionally try to maintain this property:
// `max(children, 1) * length >= capacity` // `max(children, 1) * length >= capacity`
@@ -736,21 +739,14 @@ Node *nextLogical(Node *node) {
return node; return node;
} }
// Fix larger-than-desired capacities. Does not return nodes to freelists, // Invalidates `self`, replacing it with a node of at least capacity.
// since that wouldn't actually reclaim the memory used for partial key // Does not return nodes to freelists.
// capacity. void makeCapacityAtLeast(Node *&self, int capacity, NodeAllocators *allocators,
void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators, ConflictSet::Impl *impl) {
ConflictSet::Impl *impl) {
const int maxCapacity =
std::max<int>(self->numChildren, 1) * self->partialKeyLen;
if (self->partialKeyCapacity <= maxCapacity) {
return;
}
switch (self->type) { switch (self->type) {
case Type::Node0: { case Type::Node0: {
auto *self0 = (Node0 *)self; auto *self0 = (Node0 *)self;
auto *newSelf = allocators->node0.allocate(maxCapacity); auto *newSelf = allocators->node0.allocate(capacity);
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin, memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
kNodeCopySize); kNodeCopySize);
memcpy(newSelf->partialKey(), self0->partialKey(), self->partialKeyLen); memcpy(newSelf->partialKey(), self0->partialKey(), self->partialKeyLen);
@@ -760,7 +756,7 @@ void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
} break; } break;
case Type::Node4: { case Type::Node4: {
auto *self4 = (Node4 *)self; auto *self4 = (Node4 *)self;
auto *newSelf = allocators->node4.allocate(maxCapacity); auto *newSelf = allocators->node4.allocate(capacity);
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin, memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
kNodeCopySize); kNodeCopySize);
memcpy(newSelf->partialKey(), self4->partialKey(), self->partialKeyLen); memcpy(newSelf->partialKey(), self4->partialKey(), self->partialKeyLen);
@@ -776,7 +772,7 @@ void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
} break; } break;
case Type::Node16: { case Type::Node16: {
auto *self16 = (Node16 *)self; auto *self16 = (Node16 *)self;
auto *newSelf = allocators->node16.allocate(maxCapacity); auto *newSelf = allocators->node16.allocate(capacity);
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin, memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
kNodeCopySize); kNodeCopySize);
memcpy(newSelf->partialKey(), self16->partialKey(), self->partialKeyLen); memcpy(newSelf->partialKey(), self16->partialKey(), self->partialKeyLen);
@@ -792,7 +788,7 @@ void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
} break; } break;
case Type::Node48: { case Type::Node48: {
auto *self48 = (Node48 *)self; auto *self48 = (Node48 *)self;
auto *newSelf = allocators->node48.allocate(maxCapacity); auto *newSelf = allocators->node48.allocate(capacity);
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin, memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
kNodeCopySize); kNodeCopySize);
memcpy(newSelf->partialKey(), self48->partialKey(), self->partialKeyLen); memcpy(newSelf->partialKey(), self48->partialKey(), self->partialKeyLen);
@@ -812,7 +808,7 @@ void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
} break; } break;
case Type::Node256: { case Type::Node256: {
auto *self256 = (Node256 *)self; auto *self256 = (Node256 *)self;
auto *newSelf = allocators->node256.allocate(maxCapacity); auto *newSelf = allocators->node256.allocate(capacity);
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin, memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
kNodeCopySize); kNodeCopySize);
memcpy(newSelf->partialKey(), self256->partialKey(), self->partialKeyLen); memcpy(newSelf->partialKey(), self256->partialKey(), self->partialKeyLen);
@@ -827,6 +823,19 @@ void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
} }
} }
// Fix larger-than-desired capacities. Does not return nodes to freelists,
// since that wouldn't actually reclaim the memory used for partial key
// capacity.
void maybeDecreaseCapacity(Node *&self, NodeAllocators *allocators,
ConflictSet::Impl *impl) {
const int maxCapacity =
std::max<int>(self->numChildren, 1) * self->partialKeyLen;
if (self->partialKeyCapacity <= maxCapacity) {
return;
}
makeCapacityAtLeast(self, maxCapacity, allocators, impl);
}
// 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, NodeAllocators *allocators, void maybeDownsize(Node *self, NodeAllocators *allocators,
ConflictSet::Impl *impl, Node *&dontInvalidate) { ConflictSet::Impl *impl, Node *&dontInvalidate) {
@@ -885,7 +894,7 @@ void maybeDownsize(Node *self, NodeAllocators *allocators,
} }
} break; } break;
case Type::Node16: case Type::Node16:
if (self->numChildren < kMinChildrenNode16) { if (self->numChildren + int(self->entryPresent) < kMinChildrenNode16) {
auto *self16 = (Node16 *)self; auto *self16 = (Node16 *)self;
auto *newSelf = allocators->node4.allocate(self->partialKeyLen); auto *newSelf = allocators->node4.allocate(self->partialKeyLen);
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin, memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
@@ -902,7 +911,7 @@ void maybeDownsize(Node *self, NodeAllocators *allocators,
} }
break; break;
case Type::Node48: case Type::Node48:
if (self->numChildren < kMinChildrenNode48) { if (self->numChildren + int(self->entryPresent) < kMinChildrenNode48) {
auto *self48 = (Node48 *)self; auto *self48 = (Node48 *)self;
auto *newSelf = allocators->node16.allocate(self->partialKeyLen); auto *newSelf = allocators->node16.allocate(self->partialKeyLen);
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin, memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
@@ -915,7 +924,7 @@ void maybeDownsize(Node *self, NodeAllocators *allocators,
// Suppress a false positive -Waggressive-loop-optimizations warning // Suppress a false positive -Waggressive-loop-optimizations warning
// in gcc. `assume` doesn't work for some reason. // in gcc. `assume` doesn't work for some reason.
if (!(i < 16)) { if (!(i < 16)) {
__builtin_unreachable(); __builtin_unreachable(); // GCOVR_EXCL_LINE
} }
newSelf->index[i] = c; newSelf->index[i] = c;
newSelf->children[i] = self48->children[self48->index[c]]; newSelf->children[i] = self48->children[self48->index[c]];
@@ -929,7 +938,7 @@ void maybeDownsize(Node *self, NodeAllocators *allocators,
} }
break; break;
case Type::Node256: case Type::Node256:
if (self->numChildren < kMinChildrenNode256) { if (self->numChildren + int(self->entryPresent) < kMinChildrenNode256) {
auto *self256 = (Node256 *)self; auto *self256 = (Node256 *)self;
auto *newSelf = allocators->node48.allocate(self->partialKeyLen); auto *newSelf = allocators->node48.allocate(self->partialKeyLen);
memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin, memcpy((char *)newSelf + kNodeCopyBegin, (char *)self + kNodeCopyBegin,
@@ -2500,7 +2509,7 @@ Iterator firstGeq(Node *n, std::string_view key) {
minNumChildren = kMinChildrenNode256; minNumChildren = kMinChildrenNode256;
break; break;
} }
if (node->numChildren < minNumChildren) { if (node->numChildren + int(node->entryPresent) < minNumChildren) {
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,