aed3 / poke-sim

Creating a Pokemon battling and analyzing C++ library designed for speed.
BSD 3-Clause "New" or "Revised" License
19 stars 1 forks source link

Battle.runEvent (and similar functions), Battle.findEventHandlers (and helper functions) [Performance] #3

Closed aed3 closed 1 year ago

aed3 commented 1 year ago

When taking a CPU profile of Showdown when running my AI, this set of functions uses the most time overall and the most time per call. However, changing these functions to be performant might require changing the entire structure of the code, so finding the best way to handle events that various parts of a battle can activate is step 1.

aed3 commented 1 year ago

Initial thought

aed3 commented 1 year ago

Hard-coding Events

After reading through the design work in pkmn/engine - which I'll probably be referencing a lot because of how thorough it is - I have another idea which gets rid of looking up events altogether.

This allows for all the event handlers from the same effect type to continue to be written in the same place and resused, keeping the code organized. Also, separating the code out like this allows for swapping out runEvent functions depending on the game being simulated.

Not sure yet how well this will work with the "Analyze Effect" portion of the code or of its performance benefits over other options. This also breaks if the order of events for a specific type ever changes (like factoring in a Choice Band's boost on a move changes from before to after factoring in any weather-based boosts), but right now I'm not aware of that ever happening.

aed3 commented 1 year ago

Hard-coding Events

After reading through the design work in pkmn/engine - which I'll probably be referencing a lot because of how thorough it is - I have another idea which gets rid of looking up events altogether.

  • Maintain the battle.runEvent calls as the exist now and replace them with deliberate functions for each event type (battle.runEvent(UseItem, ...) becomes battle.onUseItem(...))
  • Within those specific runEvent functions, all the possible event handler functions for it will be listed and the one to run will be chosen based on various switch and if statements
    • For example, if onModifyDamage is called, that function will check run a switch statement against the move user's item, and if the user has a Life Orb, LifeOrb::onModifyDamage will get called
    • The order in which event handlers are listed in these functions will act as a replacement for the "priority" parameter some events use

This allows for all the event handlers from the same effect type to continue to be written in the same place and resused, keeping the code organized. Also, separating the code out like this allows for swapping out runEvent functions depending on the game being simulated.

Not sure yet how well this will work with the "Analyze Effect" portion of the code or of its performance benefits over other options. This also breaks if the order of events for a specific type ever changes (like factoring in a Choice Band's boost on a move changes from before to after factoring in any weather-based boosts), but right now I'm not aware of that ever happening.

Maybe using an inheritance/virtual method design instead of switch and if statements here is better. It is for sure from a maintainability standpoint, but performance is TBD: I'm looking into it.

aed3 commented 1 year ago

Settled with 945900d6ff8f9637909ea6161f8b155ba7c34ea1 in designs/Features/EventHandling.md