Closed Zannick closed 8 months ago
Replacing move_to
with the A version resulted in a 7-8% speedup of the "load_routes" benchmark, which seems promising. Replacing first_spot_with_locations_after_actions
with its A version (that allows for any number of actions repeated) results in the "can_win_from_scratch" benchmark hanging indefinitely, whereas it averages 15ms previously.
Potentially, we can reintroduce the limitation on global actions, but it's harder to make use of the shared expand functions if we include them in the heap--possibly we just introduce another map (keyed by Context which could be expensive), or indirect the heap input through provided lambdas...
Ok, having fixed what was causing the hang, the new search is significantly worse but not broken, hitting well over 8000% worse (nearly 1.4s vs the previous results around 15ms).
Not really sure what happened with that benchmark result; I have it within a margin of randomness/error on my better computer, at roughly 10ms before and after this last change, for can_win_from_scratch
. Unless these are failure results, which would be strange, but those are usually instantaneous (nanoseconds or microseconds).
Forgot to commit the last changes from the other computer, that's what happened.
So yes, the new A* brings can_win_from_scratch
from 10ms to 1s, and tracking the used global actions actually brings it up further to 10s.
While setting up a profiler, I also discovered this runs out of memory on the larger victory rules. So some improvements are needed.
For now I think what we'll do is this: revert the use of A-with-actions for the initial greedy search, and change the greedy thread to use the A version for getting to a specific required location. I think we'll need to track depth as well in order to prevent extreme cases.
Satisfied with this. The greedy thread is a lot faster now as well, having finished around 33 thousand states (searched from to each remaining location) in 371 million total rounds. It may also wind up using a lot of resources, but I've added a few caps to catch excessive usage, and I'll check in with heaptrack to make sure memory isn't overused in this area.
We have two greedy-search stages, which both use the same code. The first is the initial greedy search to establish an upper bound--which is usually quickly beaten by the main search. The second is the greedy thread in the main search, which searches for the fastest route to each remaining location as the first one by using a greedy search that removes all other remaining locations.
This stems mainly from the use of
expand
andfirst_spot_with_locations_after_actions
.expand
generates a mapping of every reachable spot with a context object.first_spot_with_locations_after_actions
generates a list of contexts to try along with a set of used actions, and this is fairly exponential if it doesn't find something on the first pass.When we have a specific location goal in mind, we can use A*: The priority queue metric is elapsed time plus min distance to the spot plus the location time. "Min distance" is a challenge, though, due to actions and warps not accounted for. Thankfully, we don't need to measure the metric for the initial context, so we can perform those once and then measure from there--I assert that we can still use those actions and warps afterward.
Here's what we're looking at:
expand
andmove_to
with an iterator to reachable spots. Each timenext()
is called to get a new spot, the previous one is used to step further and insert into the queue. Potentially we should also be able to modify during iteration by inserting new elements (i.e. the results of actions/locations) in which case this is just a priority queue wrapper with peek.HashSet<SpotId>
inexpand
avoids backtracking, but it can miss out on subtle side effects like visiting map tiles or resetting rooms. To handle that, we'd have to useHashSet<T>
, and hopefully the lazy iteration keeps the usage still lower thanexpand
to the entire map does for now.first_spot...
with the A* algorithm, but it needs to keep track ofused_globals
as usual.