refactor(pathfind): Optimize pathfind snippets for higher performance - #3198
refactor(pathfind): Optimize pathfind snippets for higher performance#3198Skyaero42 wants to merge 3 commits into
Conversation
…perHackers#3198) PathNode::appendToList() walks the entire list from the head to find the tail on every call, making repeated appendNode() calls O(n^2) in path length. Path already tracks m_pathTail, so append directly onto it in O(1) instead. Removed PathNode::appendToList() as it is not used anywhere else.
…for optimization (TheSuperHackers#3198) The parent cell's world position fromPos never changes across the neighbour loop, so compute it once instead.
7df663c to
c95c628
Compare
PR Summary by QodoOptimize path construction and neighbor evaluation
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
Code Review by Qodo
1. Terrain lookup becomes unconditional
|
| { | ||
| ExamineCellsStruct* d = (ExamineCellsStruct*)userData; | ||
| Bool isCrusher = d->obj ? d->obj->getCrusherLevel() > 0 : false; | ||
| if (d->thePathfinder->m_isTunneling) return 1; // abort. |
There was a problem hiding this comment.
Is this check necessary? Could maybe be changed to an assertion given that it's already checked at the call site.
| fromPos.y = parentCell->getYIndex() * PATHFIND_CELL_SIZE_F ; | ||
| fromPos.z = TheTerrainLogic->getGroundHeight(fromPos.x , fromPos.y); | ||
|
|
||
| Coord3D toPos; |
There was a problem hiding this comment.
I wonder if pulling the creation of toPos outside of the loops along with fromPos can help perf a little since we aren't creating and destroying a more complex object constantly.
There was a problem hiding this comment.
toPos is updated each iteration due to its dependency on newCellCoord.
There was a problem hiding this comment.
toPosis updated each iteration due to its dependency onnewCellCoord.
i meant just the variable, the assignment to it can be left in the loop.
There was a problem hiding this comment.
Why would that help? It doesn't even have user-defined constructors, so the x y z assignment is the cost of construction.
There was a problem hiding this comment.
According to Godbolt, the location of the definition doesn't matter.
| } | ||
| else | ||
| { | ||
| m_path = node->prependToList(nullptr); |
There was a problem hiding this comment.
The appendNode code seems wrong, it never does any checking to see if the list is initialised properly and will only update m_pathTail.
it might be worth adding an initial check before this block.
PathNode *node = newInstance(PathNode);
node->setPosition( pos );
node->setLayer(layer);
if (!m_path)
{
m_path = node;
m_pathTail = node;
#ifdef CPOP_STARTS_FROM_PREV_SEG
m_cpopRecentStart = nullptr;
#endif
return;
}This block you added can then be simplified to m_pathTail->apend(node);
This PR optimizes three code snippets in the pathfinding algorithm to improve its performance. In end-games like FFA's with high number of units, pathfinding can take up to 80% of all CPU time.
For convenience, each snippet optimization is its own commit. PR can be merged by either squash or rebase.
Commits
refactor(pathfind): Optimize appending node to end of the path.
PathNode::appendToList()walks the entire list from the head to find the tail on every call, making repeatedappendNode()calls O(n^2) in path length. Path already tracksm_pathTail, so append directly onto it in O(1) instead. RemovedpathNode::appendToList()as it is not used anywhere else.refactor(pathfind): Take parents cell's position outside of for-loop for optimization.
The parent cell's world position
fromPosnever changes across the neighbour loop, so compute it once instead.refactor(pathfind): Remove redundant isCrusher recomputation for optimization.
The parameter
isCrusheris calculated in theExamineCellsStructand then recalculated in theexamineCellsCallback. By caching the result in theExamineCellStructit reduced the number of evaluations needed.Performance
VS's performance analyser was used.
The appending node commit reduced 3.1% in absolute CPU time for pathfinding.
The parent's cells position and isCrusher optimizations combined reduced 1.2% in absolute CPU time for pathfinding.
Testing
This PR has been tested against 50 normal replays and 1 replay with the pathfind failover activated.
Disclaimer
This PR and its description was fully made by a human.