在调试模式下,priority_queue变得非常慢

时间:2014-03-27 08:47:41

标签: c++ performance stl priority-queue path-finding

我目前正在为游戏编写一个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;
        }
    };
}

2 个答案:

答案 0 :(得分:2)

std::priority_queue<PathNodeRecord*> openList

我认为原因是你有priority_queue 指针PathNodeRecord。 并且没有为指针定义排序。

首先尝试将其更改为std::priority_queue<PathNodeRecord>,如果它有所不同,那么您只需传递自己的比较器,该比较器知道如何比较指向PathNodeRecord的指针,它将首先取消引用指针,然后进行比较。

修改 我猜测为什么你的执行时间非常慢,我认为这些指针是根据他们的地址进行比较的。并且地址从内存中的一个点开始分配并上升。 所以这导致你的堆的极端情况(堆在数据结构中而不是内存部分),所以你的堆实际上是一个列表,(每个节点有一个子节点的树,依此类推)。 所以你的操作花了一个线性的时间,再次猜测。

答案 1 :(得分:0)

您不能指望调试版本与发布优化版本一样快,但您似乎做了很多可能与调试运行时交互不良的动态分配。

我建议您在项目的调试属性页的环境设置中添加_NO_DEBUG_HEAP=1