Remove unneeded subtrees when shrinking tree
This commit is contained in:
parent
cf38eb0eb3
commit
7a68757599
@ -77,18 +77,9 @@ void DataInnerNode::addChild(const DataNode &child) {
|
|||||||
LastChild()->setKey(child.key());
|
LastChild()->setKey(child.key());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataInnerNode::reduceNumChildren(uint32_t newNumChildren) {
|
|
||||||
ASSERT(node().Size() >= newNumChildren, "New num children given to reduceNumChildren() is larger than old num children.");
|
|
||||||
if (node().Size() != newNumChildren) {
|
|
||||||
for (auto entry = ChildrenBegin() + newNumChildren; entry != ChildrenEnd(); ++entry) {
|
|
||||||
entry->setKey(Key::Null());
|
|
||||||
}
|
|
||||||
node().setSize(newNumChildren);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataInnerNode::removeLastChild() {
|
void DataInnerNode::removeLastChild() {
|
||||||
ASSERT(node().Size() > 1, "There is no child to remove");
|
ASSERT(node().Size() > 1, "There is no child to remove");
|
||||||
|
LastChild()->setKey(Key::Null());
|
||||||
node().setSize(node().Size()-1);
|
node().setSize(node().Size()-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ public:
|
|||||||
uint32_t numChildren() const;
|
uint32_t numChildren() const;
|
||||||
|
|
||||||
void addChild(const DataNode &child_key);
|
void addChild(const DataNode &child_key);
|
||||||
void reduceNumChildren(uint32_t newNumChildren);
|
|
||||||
|
|
||||||
void removeLastChild();
|
void removeLastChild();
|
||||||
|
|
||||||
|
@ -88,6 +88,20 @@ void DataNodeStore::remove(unique_ref<DataNode> node) {
|
|||||||
_blockstore->remove(std::move(block));
|
_blockstore->remove(std::move(block));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DataNodeStore::removeSubtree(unique_ref<DataNode> node) {
|
||||||
|
//TODO Make this faster by not loading the leaves but just deleting them. Can be recognized, because of the depth of their parents.
|
||||||
|
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(node.get());
|
||||||
|
if (inner != nullptr) {
|
||||||
|
for (uint32_t i = 0; i < inner->numChildren(); ++i) {
|
||||||
|
auto child = load(inner->getChild(i)->key());
|
||||||
|
ASSERT(child != none, "Couldn't load child node");
|
||||||
|
removeSubtree(std::move(*child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remove(std::move(node));
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t DataNodeStore::numNodes() const {
|
uint64_t DataNodeStore::numNodes() const {
|
||||||
return _blockstore->numBlocks();
|
return _blockstore->numBlocks();
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ public:
|
|||||||
cpputils::unique_ref<DataNode> overwriteNodeWith(cpputils::unique_ref<DataNode> target, const DataNode &source);
|
cpputils::unique_ref<DataNode> overwriteNodeWith(cpputils::unique_ref<DataNode> target, const DataNode &source);
|
||||||
|
|
||||||
void remove(cpputils::unique_ref<DataNode> node);
|
void remove(cpputils::unique_ref<DataNode> node);
|
||||||
|
void removeSubtree(cpputils::unique_ref<DataNode> node);
|
||||||
|
|
||||||
//TODO Test blocksizeBytes/numBlocks/estimateSpaceForNumBlocksLeft
|
//TODO Test blocksizeBytes/numBlocks/estimateSpaceForNumBlocksLeft
|
||||||
uint64_t virtualBlocksizeBytes() const;
|
uint64_t virtualBlocksizeBytes() const;
|
||||||
|
@ -168,14 +168,21 @@ void DataTree::resizeNumBytes(uint64_t newNumBytes) {
|
|||||||
// This is only called, if the new last leaf was not existing yet
|
// This is only called, if the new last leaf was not existing yet
|
||||||
return Data(newLastLeafSize).FillWithZeroes();
|
return Data(newLastLeafSize).FillWithZeroes();
|
||||||
};
|
};
|
||||||
auto onBacktrackFromSubtree = [newNumLeaves, maxChildrenPerInnerNode] (DataInnerNode* node) {
|
auto onBacktrackFromSubtree = [this, newNumLeaves, maxChildrenPerInnerNode] (DataInnerNode* node) {
|
||||||
// This is only called for the right border nodes of the new tree.
|
// This is only called for the right border nodes of the new tree.
|
||||||
// When growing size, the following is a no-op. When shrinking, we're deleting the children that aren't needed anymore.
|
// When growing size, the following is a no-op. When shrinking, we're deleting the children that aren't needed anymore.
|
||||||
uint32_t maxLeavesPerChild = utils::intPow((uint64_t)maxChildrenPerInnerNode, ((uint64_t)node->depth()-1));
|
uint32_t maxLeavesPerChild = utils::intPow((uint64_t)maxChildrenPerInnerNode, ((uint64_t)node->depth()-1));
|
||||||
uint32_t neededNodesOnChildLevel = utils::ceilDivision(newNumLeaves, maxLeavesPerChild);
|
uint32_t neededNodesOnChildLevel = utils::ceilDivision(newNumLeaves, maxLeavesPerChild);
|
||||||
uint32_t neededSiblings = utils::ceilDivision(neededNodesOnChildLevel, maxChildrenPerInnerNode);
|
uint32_t neededSiblings = utils::ceilDivision(neededNodesOnChildLevel, maxChildrenPerInnerNode);
|
||||||
uint32_t neededChildrenForRightBorderNode = neededNodesOnChildLevel - (neededSiblings-1) * maxChildrenPerInnerNode;
|
uint32_t neededChildrenForRightBorderNode = neededNodesOnChildLevel - (neededSiblings-1) * maxChildrenPerInnerNode;
|
||||||
node->reduceNumChildren(neededChildrenForRightBorderNode);
|
ASSERT(neededChildrenForRightBorderNode <= node->numChildren(), "Node has too few children");
|
||||||
|
// All children to the right of the new right-border-node are removed including their subtree.
|
||||||
|
while(node->numChildren() > neededChildrenForRightBorderNode) {
|
||||||
|
// TODO removeSubtree() should get the key, I shouldn't load the block here.
|
||||||
|
// TODO removeSubtree() needs perf optimization: Don't load leaves.
|
||||||
|
_nodeStore->removeSubtree(_nodeStore->load(node->LastChild()->key()).value());
|
||||||
|
node->removeLastChild();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_traverseLeaves(newNumLeaves - 1, newNumLeaves, onExistingLeaf, onCreateLeaf, onBacktrackFromSubtree);
|
_traverseLeaves(newNumLeaves - 1, newNumLeaves, onExistingLeaf, onCreateLeaf, onBacktrackFromSubtree);
|
||||||
|
@ -37,6 +37,7 @@ unique_ref<DataTree> DataTreeStore::createNewTree() {
|
|||||||
void DataTreeStore::remove(unique_ref<DataTree> tree) {
|
void DataTreeStore::remove(unique_ref<DataTree> tree) {
|
||||||
// Remove all nodes except for the root, which will be a leaf.
|
// Remove all nodes except for the root, which will be a leaf.
|
||||||
tree->resizeNumBytes(0);
|
tree->resizeNumBytes(0);
|
||||||
|
// Then remove the root node
|
||||||
_nodeStore->remove(tree->releaseRootNode());
|
_nodeStore->remove(tree->releaseRootNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +223,19 @@ TEST_P(DataTreeTest_ResizeNumBytes_P, DataStaysIntact) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(DataTreeTest_ResizeNumBytes_P, UnneededBlocksGetDeletedWhenShrinking) {
|
||||||
|
tree->resizeNumBytes(newSize);
|
||||||
|
tree->flush();
|
||||||
|
|
||||||
|
uint64_t expectedNumNodes = 1; // 1 for the root node
|
||||||
|
uint64_t nodesOnCurrentLevel = newNumberOfLeaves;
|
||||||
|
while (nodesOnCurrentLevel > 1) {
|
||||||
|
expectedNumNodes += nodesOnCurrentLevel;
|
||||||
|
nodesOnCurrentLevel = ceilDivision(nodesOnCurrentLevel, nodeStore->layout().maxChildrenPerInnerNode());
|
||||||
|
}
|
||||||
|
EXPECT_EQ(expectedNumNodes, nodeStore->numNodes());
|
||||||
|
}
|
||||||
|
|
||||||
//Resize to zero is not caught in the parametrized test above, in the following, we test it separately.
|
//Resize to zero is not caught in the parametrized test above, in the following, we test it separately.
|
||||||
|
|
||||||
TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_NumBytesIsCorrect) {
|
TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_NumBytesIsCorrect) {
|
||||||
@ -241,3 +254,10 @@ TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_KeyDoesntChange) {
|
|||||||
tree->flush();
|
tree->flush();
|
||||||
EXPECT_EQ(key, tree->key());
|
EXPECT_EQ(key, tree->key());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_UnneededBlocksGetDeletedWhenShrinking) {
|
||||||
|
auto tree = CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(10u);
|
||||||
|
tree->resizeNumBytes(0);
|
||||||
|
tree->flush();
|
||||||
|
EXPECT_EQ(1u, nodeStore->numNodes());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user