[C++] Vector faster than priority-queue in A*

Started by
17 comments, last by Waterlimon 10 years, 1 month ago

... Atleast I thought so. For some reason, when the operator is defined like this


bool node::operator<(const node *right) const{
return (costF < right->costF);
}
it is beeing completly ignored by the priority queue. Deleting it doesn't give me ">operator not defined".

const node * is a pointer, not a reference. const node & is a reference.

Advertisement

You are right ... I shouldn't try coding this late at night.

But changing it didn't increase the speed of openList.push() at all.

So, we are back were we began sad.png

EDIT: I also tried what TiagoCosta mentioned, but it didn't increase the speed by a noticable amount.

Maybe your testing method is flawed.

How fast/slow is it? Have you compared with other implementations? What are you using to represent the graph (a grid, a navigation mesh, something else)?
Have you tried with several sizes of maps?

Currently working on a scene editor for ORX (http://orx-project.org), using kivy (http://kivy.org).

Hello.

Have you tryed a std::list for your Prioityqueue this way you can incert the lowest cost it works fast for me, my maps are a grid 1000 X 1000 plus cells

std::list<Cell *> Priorityqueue;//ordered list

For testing, I use

clock();
... Not the best, I know, but it does the job and doesn't seem to have a big negative impact on the performance.

I did compare with other implementations, but they were all much slower than mine. The graph is represented by a 2D array of nodes.

I went through several mapsizes, currently 256x256 is the only one that performs close to an acceptable speed, but still is kinda to slow when a player wants to path over the full map in one go(2-3 sec). On short or midrange paths it seems ok though.

I'm not exactly sure how NPC-movement is handled in onlinegames. They have to path too, so the server would have to comptue their paths - and that rather frequently. That really would impact that servers performance, right?

@ankhd

I don't really understand what you want to say. You suppose a std::list is faster than a priority-queue in terms of pushing? How fast do you manage to path over a 1kx1k grid from edge to edge?

Vectors have more cache coherent access patterns than heaps. Also if your help contains objects rather than pointers, then swaps are expensive operations. Unless your nodes are simple, your heap should definitely consist of pointers to nodes. With a bit of magic you could also wrap the pointers so that they updated the nodes to keep track of their position of their pointer in the heap, to make updates more efficient.

To properly understand whether a heap is justified you need to collect some statistics about the distribution of counts of open nodes at each exploration step.

But I can't do that with pointers - as soon as I try to update a value on a node that I pushed onto the openlist, I end up with the "invalid heap" runtime error.


I'm sorry if this is a stupid question, but are you measuring performance in a debug build? The push function is going to be much more expensive in a build with the valid heap check than without it.

Also, how do you find the node you want to update in the priority queue? Are you still just pushing it twice?

Unfortunately, I wouldn't use std::priority_queue because it isn't really built for updating anything in it. However, you should be able to program your own heap without much work with help from the STL.

Also, how are you storing your nodes? What does that class/struct look like? I see it might be the same thing as in your map. You might want to streamline it as much as possible. I see you said you've tried that, but it's hard to know exactly what you tried and if there were any flaws based on this limited amount of information.

Do you have the ability to use a profiler instead of clock()?

Hello, sorry for my late reply.

I was profiling in a debug build, I didn't think of that beeing able to influence the performance. Switching it improved the speed by like 25%.

I never used a profiler before, but if you are able to point me to a good one, I'd be willing to give it a try.

I'm still pushing twice instead of updateing the nodes. I take it you suggest updating rather than doing that?

I'm storing the nodes in a simple 2D array


node *map[size][size];

Here is the .h of the node class:


 
 
class node{
public:
node *parent;
int2 pos;
D3DXVECTOR2 posInNode;
bool isOpen;
 
bool isOnClosedList, isOnOpenList; // not using closedlist, and isOnOpenList lets me skip the sorting through the openlist when I want to see if a node is on it
int costF, costG, costH;
 
bool pushedOntoQueue;
 
public:
node(){
resetData();
isOpen = true;
pos = int2(0, 0);
}
node(int2 posIn){
pos = posIn;
isOpen = true;
resetData();
}
~node(){}
 
// reset for reusing
void resetData(){
posInNode = D3DXVECTOR2(-1, -1);
parent = NULL;
costF = costG = costH = 0;
isOnClosedList = false;
isOnOpenList = false;
 
pushedOntoQueue = false;
}
};
 

This is what I use in the priorityqueue instead of the full Node:


struct nodePH{
int2 pos;
int costF;
bool operator<(const nodePH &right) const{
return (costF > right.costF);
}
};

I use Very Sleepy for my profiling, it essentially looks what function you are in (using the stack?) many times a second to get an approximate of where you spend the most time. You then get percentages and times for the most visited code.

o3o

This topic is closed to new replies.

Advertisement