繁体   English   中英

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

[英]priority_queue becomes extremely slow in debug mode

我目前正在为游戏编写A *寻路算法,并且遇到了与priority_queue有关的非常奇怪的性能问题

我使用的是典型的“开放节点列表”,在其中存储找到的但尚未处理的节点。 这被实现为指向PathNodeRecord对象的指针的STL priority_queueopenList ),该指针存储有关访问的节点的信息。 它们按到达那里的估计成本( estimateTotalCost )进行排序。

现在我注意到,每当调用寻路方法时,相应的AI线程就会完全卡住,并花费几(〜5)秒来处理算法并计算路径。 随后,我使用VS2013分析器查看了花了这么长时间的原因,原因和地点。

事实证明, 推送到打开列表 (priority_queue) 和从打开列表中弹出会花费大量时间。 我不是STL容器的专家,但是我以前从未对它们的效率有任何疑问,这对我来说很奇怪。

奇怪的是,这仅在使用VS的“调试”构建配置时发生。 “发布”会议。 对我来说效果很好,现在一切恢复正常了。

我在这里做的事情根本上是错的,还是为什么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,带有“ less”运算符的重载以启用在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;
        }
    };
}

std::priority_queue<PathNodeRecord*> openList

我想原因是,你有一个priority_queue 指针PathNodeRecord 并且没有为指针定义顺序。

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

编辑:疯狂猜测您为什么会得到极慢的执行时间,我认为指针是根据其地址进行比较的。 从内存中的一点开始分配地址,然后递增。 这样就导致了堆的极端情况(数据结构中的堆而不是内存部分),因此堆实际上是一个列表(一棵树,其中每个节点都有一个子节点,依此类推)。 因此您的操作花费了线性时间,再次只是一个猜测。

您不能指望调试版本会像经过发行版优化的版本一样快,但是您似乎进行了很多动态分配,这些动态分配可能会与调试运行时发生严重的交互。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM