【发布时间】:2016-05-06 13:07:15
【问题描述】:
几天来,我正在努力加快我的力导向图的实施。到目前为止,我已经实现了使用八叉树来减少计算次数的 Barnes-Hut 算法。我已经对其进行了多次测试,并且与力相关的计算数量确实大大减少了。下面是没有 Barns-Hut(蓝线)和有(红线)的节点数的计算图: 尽管现在它应该快很多,但事实是,在速度(时间)方面,升级只有百分之几。
我想可能导致这种情况的一部分是树的创建和树放置中的元素。因为元素不断移动,我需要在每个循环中重新创建树,直到达到某个停止条件。但是,如果我将花费大量时间创建树,我将失去我在力计算增加方面获得的时间。至少这是我的想法。这就是我在主文件循环中添加元素的方式:
void AddTreeElements(Octree* tree, glm::vec3* boundries, Graph& graph)
{
for(auto& node:graph.NodeVector())
{
node.parent_group = nullptr;
if(node.pos[0] < boundries[1][0] && node.pos[0] > boundries[0][0] &&
node.pos[1] > boundries[4][1] && node.pos[1] < boundries[1][1] &&
node.pos[2] < boundries[0][2] && node.pos[2] > boundries[3][2])
{
tree->AddObject(&node.second);
continue;
}
if(node.pos[0] < boundries[0][0])
{
boundries[0][0] = node.pos[0]-1.0f;
boundries[3][0] = node.pos[0]-1.0f;
boundries[4][0] = node.pos[0]-1.0f;
boundries[7][0] = node.pos[0]-1.0f;
}
else if(node.pos[0] > boundries[1][0])
{
boundries[1][0] = node.pos[0]+1.0f;
boundries[2][0] = node.pos[0]+1.0f;
boundries[5][0] = node.pos[0]+1.0f;
boundries[6][0] = node.pos[0]+1.0f;
}
if(node.pos[1] < boundries[4][1])
{
boundries[4][1] = node.pos[1]-1.0f;
boundries[5][1] = node.pos[1]-1.0f;
boundries[6][1] = node.pos[1]-1.0f;
boundries[7][1] = node.pos[1]-1.0f;
}
else if(node.pos[1] > boundries[0][1])
{
boundries[0][1] = node.pos[1]+1.0f;
boundries[1][1] = node.pos[1]+1.0f;
boundries[2][1] = node.pos[1]+1.0f;
boundries[3][1] = node.pos[1]+1.0f;
}
if(node.pos[2] < boundries[3][2])
{
boundries[2][2] = node.pos[2]-1.0f;
boundries[3][2] = node.pos[2]-1.0f;
boundries[6][2] = node.pos[2]-1.0f;
boundries[7][2] = node.pos[2]-1.0f;
}
else if(node.pos[2] > boundries[0][2])
{
boundries[0][2] = node.pos[2]+1.0f;
boundries[1][2] = node.pos[2]+1.0f;
boundries[4][2] = node.pos[2]+1.0f;
boundries[5][2] = node.pos[2]+1.0f;
}
}
}
我在这里做的是遍历图中的所有元素并将它们添加到树根。另外,我正在扩展代表我的八叉树边界的框以用于下一个循环,因此所有节点都将适合其中。
八叉树结构更新的重要字段如下:
Octree* trees[2][2][2];
glm::vec3 vBoundriesBox[8];
bool leaf;
float combined_weight = 0;
std::vector<Element*> objects;
以及负责更新的部分代码:
#define MAX_LEVELS 5
void Octree::AddObject(Element* object)
{
this->objects.push_back(object);
}
void Octree::Update()
{
if(this->objects.size()<=1 || level > MAX_LEVELS)
{
for(Element* Element:this->objects)
{
Element->parent_group = this;
}
return;
}
if(leaf)
{
GenerateChildren();
leaf = false;
}
while (!this->objects.empty())
{
Element* obj = this->objects.back();
this->objects.pop_back();
if(contains(trees[0][0][0],obj))
{
trees[0][0][0]->AddObject(obj);
trees[0][0][0]->combined_weight += obj->weight;
} else if(contains(trees[0][0][1],obj))
{
trees[0][0][1]->AddObject(obj);
trees[0][0][1]->combined_weight += obj->weight;
} else if(contains(trees[0][1][0],obj))
{
trees[0][1][0]->AddObject(obj);
trees[0][1][0]->combined_weight += obj->weight;
} else if(contains(trees[0][1][1],obj))
{
trees[0][1][1]->AddObject(obj);
trees[0][1][1]->combined_weight += obj->weight;
} else if(contains(trees[1][0][0],obj))
{
trees[1][0][0]->AddObject(obj);
trees[1][0][0]->combined_weight += obj->weight;
} else if(contains(trees[1][0][1],obj))
{
trees[1][0][1]->AddObject(obj);
trees[1][0][1]->combined_weight += obj->weight;
} else if(contains(trees[1][1][0],obj))
{
trees[1][1][0]->AddObject(obj);
trees[1][1][0]->combined_weight += obj->weight;
} else if(contains(trees[1][1][1],obj))
{
trees[1][1][1]->AddObject(obj);
trees[1][1][1]->combined_weight += obj->weight;
}
}
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
trees[i][j][k]->Update();
}
}
}
}
bool Octree::contains(Octree* child, Element* object)
{
if(object->pos[0] >= child->vBoundriesBox[0][0] && object->pos[0] <= child->vBoundriesBox[1][0] &&
object->pos[1] >= child->vBoundriesBox[4][1] && object->pos[1] <= child->vBoundriesBox[0][1] &&
object->pos[2] >= child->vBoundriesBox[3][2] && object->pos[2] <= child->vBoundriesBox[0][2])
return true;
return false;
}
因为我使用指针来移动树元素,所以我认为对象创建/销毁在这里不是问题。我认为可能会影响速度的一个地方是:
Element* obj = this->objects.back();
this->objects.pop_back();
if(contains(trees[0][0][0],obj))
虽然我不确定如何省略/加快速度。有人有什么建议可以在这里做什么吗?
编辑:
我做了一些餐巾数学运算,我想还有一个地方可能会导致速度大幅下降。 Update 方法中的边界检查看起来做了很多工作,而我计算得出的是,在最坏的情况下,这样做会增加复杂性:
number_of_elements*number_of_childern*number_of_faces*MAX_LEVELS
在我的情况下等于 number_of_elements*240。
有人可以确认我的想法是否合理吗?
【问题讨论】:
-
@Mihai 根据您的建议,我已将其发布在那里:codereview.stackexchange.com/questions/127693/…
-
DrunkCoder 所说的可能会有所帮助,但请记住性能优化的前三个规则:测量、测量、测量!为您的平台使用采样 CPU 分析器(例如 Linux 上的 perf+hotspot、Windows 上的 Visual Studio 分析器或 macOS 上的 Instruments),然后使用该数据找出性能问题的罪魁祸首。