I have troubles coming up with a good strategy to reduce the memory allocations for the following problem:
I am constructing a tree. At start, I only have the root which contains some data ( a list (std::vector
) of indices ). I split in two parts where part of the indices go to the left child and the other part go to the right one. I do not know how many would go to left / right. Once, I am done processing the root, I no longer need to store the indices for it. In fact, I am only interested in those for the leaves. Furthermore, additional data can be added at each split!
So, if the root has 20 elements, then after the split the left one may have 12 elements and the right one 10.
In each node, I keep an std::vector
which contains those indices. When I am adding the elements, I push_back()
the element which leads to many memory allocations.
What would be a good strategy to keep the indices?
The question is relevant to the generation of the SBVH data structure.
Code:
struct Node {
std::vector<unsigned> indices_;
// ... some other stuff here
}
void partition(Node* node) {
Node* left = new Node();
Node* right = new Node();
float axis = findSplitPosition(node);
for(size_t i = 0; i < node->indices_.size(); ++i) {
if (index is strictly left on axis) {
left->indices_.push_back(node->indices_[i]);
} else if(index is strictly right on axis) {
right->indices_.push_back(node->indices_[i]);
} else {
// it intersects the axis -> add to left and right
left->indices_.push_back(node->indices_[i]);
right->indices_.push_back(node->indices_[i]);
}
}
// root indices no longer needed
node->indices_.clear();
}
Basically if you cannot
reserve
the vectors based on some heuristics, you fall victim to Schlemiel's algorithm (a milder version though, because the geometric growth ensures a O(n) time complexity onn
consecutive insertions instead of O(n^2)).But you can get away with a constant number of allocations if first you go through the node's indices and record whether the given index should go to the left subnode, the right subnode or both. Also keep track of the left/right subnode's index count:
This way you only do 3 allocations, namely
foo
,leftNodes
,rightNodes
. Though you have to iterate through the indices twice, the heavy lifting (the geometric calculations) is done in the first loop exclusively.