WIP my understanding of the memory bound induction was wrong
This commit is contained in:
@@ -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,
|
||||||
|
Reference in New Issue
Block a user