Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance: Optimize monster planning #69176

Merged
merged 3 commits into from
Nov 11, 2023

Conversation

prharvey
Copy link
Contributor

@prharvey prharvey commented Nov 8, 2023

Summary

Performance "Reduced wait times in high traffic areas by ~15-20%"

Purpose of change

Waiting in TCL is slow, there were a few easy ways to make it less slow.

Describe the solution

Moved the reachability flood fill into map so it could be used in the monster planner to iterate over hostile monsters in O(monsters_in_zone) instead of O(all_monsters).

Describe alternatives you've considered

I originally was also using the zone reachability to optimize sees, but it ended up having no impact. I believe there are some other improvements that could be made to the zone reachability flood fill in general, but it's a pretty small part of the profile now. Worst part by far is the pathfinding.

Testing

Game doesn't crash and the tests pass. PR is still being worked on.

@github-actions github-actions bot added NPC / Factions NPCs, AI, Speech, Factions, Ownership Vehicles Vehicles, parts, mechanics & interactions [C++] Changes (can be) made in C++. Previously named `Code` Character / World Generation Issues and enhancements concerning stages of creating a character or a world Code: Performance Performance boosting code (CPU, memory, etc.) json-styled JSON lint passed, label assigned by github actions astyled astyled PR, label is assigned by github actions labels Nov 8, 2023
@andrei8l

This comment was marked as outdated.

@kevingranade
Copy link
Member

kevingranade commented Nov 8, 2023

I was wondering if using reachable zones to populate the faction map would be a win.
I didn't follow up on it because by the time I landed the change the pertinent parts of monster:: plan() weren't even showing up in profiling any more, so I'm a bit confused what's happening that could yield a 30% speedup.

@prharvey
Copy link
Contributor Author

prharvey commented Nov 8, 2023

I was wondering if using reachable zones to populate the faction map would be a win. I didn't follow up on it because by the time I landed the change the pertinent parts of monster:: plan() weren't even showing up in profiling any more, so I'm a bit confused what's happening that could yield a 30% speedup.

Essentially your change to add the flood fill didn't actually change the algorithmic complexity of the code, it just acted as a fast O(1) check to skip much more expensive O(1) processing. The monster planner as a whole was still O(n^2) where n is number of monsters, this uses the flood fill to turn it into O(n*m) where m is number of monsters in a given zone.

It's not responsible for all of the 30% time reduction (50% speed up), just the biggest part.

@prharvey prharvey changed the title Performance: Pick assorted low hanging fruit. Performance: Optimize monster planning Nov 8, 2023
@prharvey
Copy link
Contributor Author

prharvey commented Nov 8, 2023

Removed changes unrelated to the flood fill change. I'll PR them separately.

@github-actions github-actions bot added the BasicBuildPassed This PR builds correctly, label assigned by github actions label Nov 9, 2023
Copy link
Contributor

@andrei8l andrei8l left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing any improvement when waiting in the TCL from this save Yelm.tar.gz

Before

before

After

after?

But there's too much variance there, unrelated to monster planning.

Furthermore, monster::plan() barley registers when profiling visitable_zone_surface_test so that's not a good benchmark either
perf

Can you update the test or provide a stable benchmark to illustrate the gains here?

src/map.h Show resolved Hide resolved
@prharvey
Copy link
Contributor Author

prharvey commented Nov 9, 2023

I'm not seeing any improvement when waiting in the TCL from this save Yelm.tar.gz

Before
before

After
after?

But there's too much variance there, unrelated to monster planning.
Furthermore, monster::plan() barley registers when profiling visitable_zone_surface_test so that's not a good benchmark either perf

Can you update the test or provide a stable benchmark to illustrate the gains here?

Before
before

After
after

TCL Perf.zip

@andrei8l
Copy link
Contributor

andrei8l commented Nov 9, 2023

TCL Perf.zip

Sorry, I'm still not really seeing it. I had to wear a blindfold and ear plugs from your save because monsters wander into view.

Before

Screenshot from 2023-11-09 07-01-00

After

Screenshot from 2023-11-09 06-52-09

If there is an improvement there, it's masked by unrelated variance. A test unit would be best. Even running visitable_zone_surface_test 100 times might work.

This is with gcc 13.2.1 and and -Og build, btw.

@prharvey
Copy link
Contributor Author

prharvey commented Nov 9, 2023

TCL Perf.zip

Sorry, I'm still not really seeing it. I had to wear a blindfold and ear plugs from your save because monsters wander into view.

Before
Screenshot from 2023-11-09 07-01-00

After
Screenshot from 2023-11-09 06-52-09

If there is an improvement there, it's masked by unrelated variance. A test unit would be best. Even running visitable_zone_surface_test 100 times might work.

This is with gcc 13.2.1 and and -Og build, btw.

I'm using a release build on windows with MSVC. Could be some compiler shenanigans.

CPU is a i9-13980HX.

@prharvey
Copy link
Contributor Author

prharvey commented Nov 9, 2023

Using your save file:

Before
Screenshot 2023-11-08 213057

After
Screenshot 2023-11-08 212446

Results are very consistent for me. Are you sure it isn't something silly like you're accidently using the same binary each time?

@akrieger
Copy link
Member

akrieger commented Nov 9, 2023

This causes a segfault on quit. g is null already so get_map() can't be called.
image

Creature::~Creature()
{
    get_map().remove_creature_from_reachability( this );
}


map &get_map()
{
    return g->m;
}

@akrieger
Copy link
Member

akrieger commented Nov 9, 2023

Results wise, at least preliminarily, looks pretty good for me. Total cost across 3 hours was cut by about 25%. monster::plan's self cost was basically eliminated (total cost reduced from 23k to 12k samples, so cut in half basically). I can't tell though if that's just an inlining/outlining change, algorithmic, or something else entirely. For example, map visit reachable creatures was added, as well as a short circuit when the target is null.

This isn't just measurement error, other baseline functions like map::get_vehicles and map::process_fields_in_submap were basically constant.

image

@prharvey prharvey marked this pull request as ready for review November 10, 2023 07:30
@prharvey
Copy link
Contributor Author

This seems to be good as is for now. I have a few improvements I want to make later, but right now it is blocking #69198 which has way more impact.

@github-actions github-actions bot added BasicBuildPassed This PR builds correctly, label assigned by github actions and removed BasicBuildPassed This PR builds correctly, label assigned by github actions labels Nov 10, 2023
@Maleclypse Maleclypse merged commit f79a631 into CleverRaven:master Nov 11, 2023
36 checks passed
akrieger added a commit to akrieger/Cataclysm-DDA that referenced this pull request Nov 15, 2023
Maleclypse pushed a commit to Maleclypse/Cataclysm-DDA that referenced this pull request Nov 16, 2023
* Use the zone floodfill to optimize monster planning algorithmic complexity.

* Delete timed_event_list.output

* Fix crash on game exit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
astyled astyled PR, label is assigned by github actions BasicBuildPassed This PR builds correctly, label assigned by github actions [C++] Changes (can be) made in C++. Previously named `Code` Character / World Generation Issues and enhancements concerning stages of creating a character or a world Code: Performance Performance boosting code (CPU, memory, etc.) json-styled JSON lint passed, label assigned by github actions NPC / Factions NPCs, AI, Speech, Factions, Ownership Vehicles Vehicles, parts, mechanics & interactions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants