Advertisement

A* speed

Started by March 02, 2012 01:35 PM
22 comments, last by slicer4ever 12 years, 8 months ago
Not sure about differences in PC's, but my A* implementation finds path on 400x400 maze field that stretches from one corner to another in ~7 msec.
There are many different reasons why your A* implementation may not perform well. The most common reasons in my experience have typically been:

  1. Using the wrong heuristic
  2. Poorly managing the open list (for example, using a slow sort or next-best search)
  3. Having a closed list.


Make sure you're using the correct distance approximation heuristic for your map. For 2D tile-based games Manhattan distance is usually a good heuristic.

If you can avoid iterating over the entire open list each time you need to check the next grid cell then do so.

Also, you can optimize away your closed list of cells by instead adding a closed flag to your map for pathfinding. When you start a pathfinding operation you reset the flag on the entire map (you can actually avoid this step by using an integer flag, but that's probably not necessary). Whenever you close a cell, instead of adding the cell to a list, you just set the flag. This will enable you to check if a cell's closed with a simple lookup instead of having to search. This does depend on you being willing to add this flag to your map, which does violate some principles of encapsulation and may offend some of the stricter coders out there. You could instead allocate a separate structure for this maintained by your pathfinder, but you'd have to make sure your structure always matches that of your map. Implementing that separation could be more trouble than it's worth.
Advertisement
Thanks smr for your response. I think it mainly has to do with the iteration of my closed list. My heuristic is allready calculated Manhattan style. Maybe I could improve the open list a bit, but I surely should get rid of the closed one as everyone is telling me ;).
OP, probably one of the most overlooked optimizations for a* is that instead of maintaining seperate open and closed "lists", have a 2d array of chars that represent the state of a node (0=n/a, 1=open, 2=closed, 3=unpassable), with a seperate 2d array of uints for important node-specific statistics such as distance travelled.

Another extremely useful optimization is to use binary heaps to sort your open list values. I recall that when I tested the binary heap sort versus the std::list sort, the difference in speed was half an order of magnitude, even when there were very few values in the heap which is one of the binary sorting algorithm's weaknesses.

[quote name='Cornstalks' timestamp='1330953939' post='4919431']
@OP: 71ms for a 40x23 map seems like a longish time to me (I know, the average is ~7.8). Was that just a weird burp in the system (i.e. maybe another process was started up that consumed some of the CPU and the OS gave less CPU time slices), or did you see ~71ms more than once?

71ms is indeed a long time. I'm not realy sure why, but sometimes the calc-time spikes. Could have something to do with with some other program running at the same time (maybe the snipping tool I used for screenshot?). I'm not realy worrying as the average is way lower. Sometimes it's also way lower then average.


I'm not sure how fast it compares to professional games, so I can't tell you that. Have you run it with a profiler to find your bottlenecks though?

Good tip, will do! I will post the results.

I just see the first picture of the stresstest isn't right, replacing it right now!
[/quote]


Excluding the rendering from the recorded time ???

An A* testing app I helped work on years ago had selectable parameter for render every X path steps (was a 512x512 map) along with high performance timers for the actual recorded A* processing time. (heapQ, flags on map etc.. amazing how far its performance we managed to increase)
--------------------------------------------[size="1"]Ratings are Opinion, not Fact

Excluding the rendering from the recorded time ???


What I do is get a DateTime.Now before calling the algorithm-function and directly after the function returns the path I take the difference between the old DateTime.Now and the current one. In this way I exclude any other code from the time tracking.
Advertisement
how are you sorting your open list? pushing/popping items off the list is probably one of the most time consuming aspect's of pathfinding systems. binary heap would speed up most systems 10 fold if your not already using them.
Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.
For comparison, I had an A* implementation that could cover a loosely connected network of ~30k nodes in roughly a millisecond on a Core 2 Quad a couple of years ago (no multithreading involved). I could probably dig up and sanitize the source if you're interested, but it's scary stuff :-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

I would love to see it, even just as a way to see the differences! Which sorting mechanism did it use?
I started with a std::priority_queue but ended up writing some custom gibberish (IIRC) by the end. I honestly don't recall clearly.

It's buried on a hard drive on a dormant machine I don't use anymore, so it may be a day or three before I can dig it up. Add another evening or so to sanitize and document it.


If you don't hear back from me relatively soon, feel free to drop me a PM and pester me into doing it, I've been meaning to publish that code for a while :-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement