我目前正在为游戏编写一个A *寻路算法,并遇到了一个非常奇怪的性能问题,关于 priority_queue 。
我正在使用一个典型的开放节点列表',其中我存储找到但尚未处理的节点。这是作为指向 PathNodeRecord 对象的指针的 STL priority_queue ( openList )实现的,该对象存储有关受访节点的信息。它们按估计的到达成本排序( estimatedTotalCost )。
现在我注意到,无论何时调用路径查找方法,相应的AI线程都会完全卡住,并且需要几(~5)秒来处理算法并计算路径。随后我使用VS2013分析器来查看它为什么以及在哪里花了这么长时间。
事实证明,从打开列表推送和弹出(priority_queue)占用了大量的时间。我不是STL容器的专家,但我之前从未遇到过效率问题,这对我来说很奇怪。
奇怪的是,只有在使用VS' Debug'构建配置。 '发布' CONF。对我来说很好,时间也恢复正常。
我在这里做了一些根本错误的事情,或者为什么priority_queue对我来说表现如此糟糕?目前的情况对我来说是不可接受的,所以如果我不能尽快解决,我将需要回到使用更简单的容器并手动将其插入正确的位置。
任何可能发生这种情况的指示都会非常有用!
以下是探查者向我展示的片段:
http://i.stack.imgur.com/gEyD3.jpg
这是路径寻找算法的相关部分,它循环打开列表,直到没有打开的节点:
// set up arrays and other variables
PathNodeRecord** records = new PathNodeRecord*[graph->getNodeAmount()]; // holds records for all nodes
std::priority_queue<PathNodeRecord*> openList; // holds records of open nodes, sorted by estimated rest cost (most promising node first)
// null all record pointers
memset(records, NULL, sizeof(PathNodeRecord*) * graph->getNodeAmount());
// set up record for start node and put into open list
PathNodeRecord* startNodeRecord = new PathNodeRecord();
startNodeRecord->node = startNode;
startNodeRecord->connection = NULL;
startNodeRecord->closed = false;
startNodeRecord->costToHere = 0.f;
startNodeRecord->estimatedTotalCost = heuristic->estimate(startNode, goalNode);
records[startNode] = startNodeRecord;
openList.push(startNodeRecord);
// ### pathfind algorithm ###
// declare current node variable
PathNodeRecord* currentNode = NULL;
// loop-process open nodes
while (openList.size() > 0) // while there are open nodes to process
{
// retrieve most promising node and immediately remove from open list
currentNode = openList.top();
openList.pop(); // ### THIS IS, WHERE IT GETS STUCK
// if current node is the goal node, end the search here
if (currentNode->node == goalNode)
break;
// look at connections outgoing from this node
for (auto connection : graph->getConnections(currentNode->node))
{
// get end node
PathNodeRecord* toNodeRecord = records[connection->toNode];
if (toNodeRecord == NULL) // UNVISITED -> path record needs to be created and put into open list
{
// set up path node record
toNodeRecord = new PathNodeRecord();
toNodeRecord->node = connection->toNode;
toNodeRecord->connection = connection;
toNodeRecord->closed = false;
toNodeRecord->costToHere = currentNode->costToHere + connection->cost;
toNodeRecord->estimatedTotalCost = toNodeRecord->costToHere + heuristic->estimate(connection->toNode, goalNode);
// store in record array
records[connection->toNode] = toNodeRecord;
// put into open list for future processing
openList.push(toNodeRecord);
}
else if (!toNodeRecord->closed) // OPEN -> evaluate new cost to here and, if better, update open list entry; otherwise skip
{
float newCostToHere = currentNode->costToHere + connection->cost;
if (newCostToHere < toNodeRecord->costToHere)
{
// update record
toNodeRecord->connection = connection;
toNodeRecord->estimatedTotalCost = newCostToHere + (toNodeRecord->estimatedTotalCost - toNodeRecord->costToHere);
toNodeRecord->costToHere = newCostToHere;
}
}
else // CLOSED -> evaluate new cost to here and, if better, put back on open list and reset closed status; otherwise skip
{
float newCostToHere = currentNode->costToHere + connection->cost;
if (newCostToHere < toNodeRecord->costToHere)
{
// update record
toNodeRecord->connection = connection;
toNodeRecord->estimatedTotalCost = newCostToHere + (toNodeRecord->estimatedTotalCost - toNodeRecord->costToHere);
toNodeRecord->costToHere = newCostToHere;
// reset node to open and push into open list
toNodeRecord->closed = false;
openList.push(toNodeRecord); // ### THIS IS, WHERE IT GETS STUCK
}
}
}
// set node to closed
currentNode->closed = true;
}
这是我的PathNodeRecord,其中包含&#39; less&#39;运算符重载以启用priority_queue中的排序:
namespace AI
{
struct PathNodeRecord
{
Node node;
NodeConnection* connection;
float costToHere;
float estimatedTotalCost;
bool closed;
// overload less operator comparing estimated total cost; used by priority queue
// nodes with a higher estimated total cost are considered "less"
bool operator < (const PathNodeRecord &otherRecord)
{
return this->estimatedTotalCost > otherRecord.estimatedTotalCost;
}
};
}
答案 0 :(得分:2)
std::priority_queue<PathNodeRecord*> openList
我认为原因是你有priority_queue
指针到PathNodeRecord
。
并且没有为指针定义排序。
首先尝试将其更改为std::priority_queue<PathNodeRecord>
,如果它有所不同,那么您只需传递自己的比较器,该比较器知道如何比较指向PathNodeRecord
的指针,它将首先取消引用指针,然后进行比较。
修改强> 我猜测为什么你的执行时间非常慢,我认为这些指针是根据他们的地址进行比较的。并且地址从内存中的一个点开始分配并上升。 所以这导致你的堆的极端情况(堆在数据结构中而不是内存部分),所以你的堆实际上是一个列表,(每个节点有一个子节点的树,依此类推)。 所以你的操作花了一个线性的时间,再次猜测。
答案 1 :(得分:0)
您不能指望调试版本与发布优化版本一样快,但您似乎做了很多可能与调试运行时交互不良的动态分配。
我建议您在项目的调试属性页的环境设置中添加_NO_DEBUG_HEAP=1
。