簡體   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