Add more tracing for turn processing.

Most of the time (1-2 seconds) is going to flight plan layout. There
don't seem to be any easy opportunities for improvement.
This commit is contained in:
Dan Albert 2021-05-28 15:27:53 -07:00
parent 6e41c36a44
commit d4e843983d
3 changed files with 63 additions and 18 deletions

View File

@ -352,7 +352,9 @@ class Game:
def pass_turn(self, no_action: bool = False) -> None: def pass_turn(self, no_action: bool = False) -> None:
logging.info("Pass turn") logging.info("Pass turn")
with logged_duration("Turn finalization"):
self.finish_turn(no_action) self.finish_turn(no_action)
with logged_duration("Turn initialization"):
self.initialize_turn() self.initialize_turn()
# Autosave progress # Autosave progress
@ -399,22 +401,28 @@ class Game:
return self.process_win_loss(turn_state) return self.process_win_loss(turn_state)
# Plan flights & combat for next turn # Plan flights & combat for next turn
with logged_duration("Computing conflict positions"):
self.compute_conflicts_position() self.compute_conflicts_position()
with logged_duration("Threat zone computation"):
self.compute_threat_zones() self.compute_threat_zones()
with logged_duration("Transit network identification"):
self.compute_transit_networks() self.compute_transit_networks()
self.ground_planners = {} self.ground_planners = {}
self.blue_procurement_requests.clear() self.blue_procurement_requests.clear()
self.red_procurement_requests.clear() self.red_procurement_requests.clear()
with logged_duration("Procurement of airlift assets"):
self.transfers.order_airlift_assets() self.transfers.order_airlift_assets()
with logged_duration("Transport planning"):
self.transfers.plan_transports() self.transfers.plan_transports()
with logged_duration("Mission planning"): with logged_duration("Blue mission planning"):
if self.settings.auto_ato_behavior is not AutoAtoBehavior.Disabled: if self.settings.auto_ato_behavior is not AutoAtoBehavior.Disabled:
blue_planner = CoalitionMissionPlanner(self, is_player=True) blue_planner = CoalitionMissionPlanner(self, is_player=True)
blue_planner.plan_missions() blue_planner.plan_missions()
with logged_duration("Red mission planning"):
red_planner = CoalitionMissionPlanner(self, is_player=False) red_planner = CoalitionMissionPlanner(self, is_player=False)
red_planner.plan_missions() red_planner.plan_missions()

View File

@ -1,5 +1,8 @@
from __future__ import annotations
import logging import logging
import timeit import timeit
from collections import defaultdict
from contextlib import contextmanager from contextlib import contextmanager
from datetime import timedelta from datetime import timedelta
from typing import Iterator from typing import Iterator
@ -11,3 +14,22 @@ def logged_duration(event: str) -> Iterator[None]:
yield yield
end = timeit.default_timer() end = timeit.default_timer()
logging.debug("%s took %s", event, timedelta(seconds=end - start)) logging.debug("%s took %s", event, timedelta(seconds=end - start))
class MultiEventTracer:
def __init__(self) -> None:
self.events: dict[str, timedelta] = defaultdict(timedelta)
def __enter__(self) -> MultiEventTracer:
return self
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
for event, duration in self.events.items():
logging.debug("%s took %s", event, duration)
@contextmanager
def trace(self, event: str) -> Iterator[None]:
start = timeit.default_timer()
yield
end = timeit.default_timer()
self.events[event] += timedelta(seconds=end - start)

View File

@ -25,6 +25,7 @@ from dcs.unittype import FlyingType
from game.factions.faction import Faction from game.factions.faction import Faction
from game.infos.information import Information from game.infos.information import Information
from game.procurement import AircraftProcurementRequest from game.procurement import AircraftProcurementRequest
from game.profiling import logged_duration, MultiEventTracer
from game.squadrons import AirWing, Squadron from game.squadrons import AirWing, Squadron
from game.theater import ( from game.theater import (
Airfield, Airfield,
@ -807,12 +808,18 @@ class CoalitionMissionPlanner:
def plan_missions(self) -> None: def plan_missions(self) -> None:
"""Identifies and plans mission for the turn.""" """Identifies and plans mission for the turn."""
player = "Blue" if self.is_player else "Red"
with logged_duration(f"{player} mission identification and fulfillment"):
with MultiEventTracer() as tracer:
for proposed_mission in self.propose_missions(): for proposed_mission in self.propose_missions():
self.plan_mission(proposed_mission) self.plan_mission(proposed_mission, tracer)
with logged_duration(f"{player} reserve mission planning"):
with MultiEventTracer() as tracer:
for critical_mission in self.critical_missions(): for critical_mission in self.critical_missions():
self.plan_mission(critical_mission, reserves=True) self.plan_mission(critical_mission, tracer, reserves=True)
with logged_duration(f"{player} mission scheduling"):
self.stagger_missions() self.stagger_missions()
for cp in self.objective_finder.friendly_control_points(): for cp in self.objective_finder.friendly_control_points():
@ -878,7 +885,9 @@ class CoalitionMissionPlanner:
threats[EscortType.Sead] = True threats[EscortType.Sead] = True
return threats return threats
def plan_mission(self, mission: ProposedMission, reserves: bool = False) -> None: def plan_mission(
self, mission: ProposedMission, tracer: MultiEventTracer, reserves: bool = False
) -> None:
"""Allocates aircraft for a proposed mission and adds it to the ATO.""" """Allocates aircraft for a proposed mission and adds it to the ATO."""
builder = PackageBuilder( builder = PackageBuilder(
mission.location, mission.location,
@ -907,7 +916,10 @@ class CoalitionMissionPlanner:
# If the package does not need escorts they may be pruned. # If the package does not need escorts they may be pruned.
escorts.append(proposed_flight) escorts.append(proposed_flight)
continue continue
self.plan_flight(mission, proposed_flight, builder, missing_types, reserves) with tracer.trace("Flight planning"):
self.plan_flight(
mission, proposed_flight, builder, missing_types, reserves
)
if missing_types: if missing_types:
self.scrub_mission_missing_aircraft( self.scrub_mission_missing_aircraft(
@ -931,6 +943,7 @@ class CoalitionMissionPlanner:
self.game, builder.package, self.is_player self.game, builder.package, self.is_player
) )
for flight in builder.package.flights: for flight in builder.package.flights:
with tracer.trace("Flight plan population"):
flight_plan_builder.populate_flight_plan(flight) flight_plan_builder.populate_flight_plan(flight)
needed_escorts = self.check_needed_escorts(builder) needed_escorts = self.check_needed_escorts(builder)
@ -939,6 +952,7 @@ class CoalitionMissionPlanner:
# impossible. # impossible.
assert escort.escort_type is not None assert escort.escort_type is not None
if needed_escorts[escort.escort_type]: if needed_escorts[escort.escort_type]:
with tracer.trace("Flight planning"):
self.plan_flight(mission, escort, builder, missing_types, reserves) self.plan_flight(mission, escort, builder, missing_types, reserves)
# Check again for unavailable aircraft. If the escort was required and # Check again for unavailable aircraft. If the escort was required and
@ -959,6 +973,7 @@ class CoalitionMissionPlanner:
# Add flight plans for escorts. # Add flight plans for escorts.
for flight in package.flights: for flight in package.flights:
if not flight.flight_plan.waypoints: if not flight.flight_plan.waypoints:
with tracer.trace("Flight plan population"):
flight_plan_builder.populate_flight_plan(flight) flight_plan_builder.populate_flight_plan(flight)
if package.has_players and self.game.settings.auto_ato_player_missions_asap: if package.has_players and self.game.settings.auto_ato_player_missions_asap: