| 
									
										
										
										
											2024-02-04 09:09:07 +01:00
										 |  |  | def run_locations_benchmark(): | 
					
						
							|  |  |  |     import argparse | 
					
						
							|  |  |  |     import logging | 
					
						
							|  |  |  |     import gc | 
					
						
							|  |  |  |     import collections | 
					
						
							|  |  |  |     import typing | 
					
						
							|  |  |  |     import sys | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     from time_it import TimeIt | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     from Utils import init_logging | 
					
						
							|  |  |  |     from BaseClasses import MultiWorld, CollectionState, Location | 
					
						
							|  |  |  |     from worlds import AutoWorld | 
					
						
							|  |  |  |     from worlds.AutoWorld import call_all | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     init_logging("Benchmark Runner") | 
					
						
							|  |  |  |     logger = logging.getLogger("Benchmark") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class BenchmarkRunner: | 
					
						
							|  |  |  |         gen_steps: typing.Tuple[str, ...] = ( | 
					
						
							| 
									
										
										
										
											2025-01-20 16:07:15 +01:00
										 |  |  |             "generate_early", | 
					
						
							|  |  |  |             "create_regions", | 
					
						
							|  |  |  |             "create_items", | 
					
						
							|  |  |  |             "set_rules", | 
					
						
							|  |  |  |             "connect_entrances", | 
					
						
							|  |  |  |             "generate_basic", | 
					
						
							|  |  |  |             "pre_fill", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-04 09:09:07 +01:00
										 |  |  |         rule_iterations: int = 100_000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if sys.version_info >= (3, 9): | 
					
						
							|  |  |  |             @staticmethod | 
					
						
							|  |  |  |             def format_times_from_counter(counter: collections.Counter[str], top: int = 5) -> str: | 
					
						
							|  |  |  |                 return "\n".join(f"  {time:.4f} in {name}" for name, time in counter.most_common(top)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             @staticmethod | 
					
						
							|  |  |  |             def format_times_from_counter(counter: collections.Counter, top: int = 5) -> str: | 
					
						
							|  |  |  |                 return "\n".join(f"  {time:.4f} in {name}" for name, time in counter.most_common(top)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def location_test(self, test_location: Location, state: CollectionState, state_name: str) -> float: | 
					
						
							|  |  |  |             with TimeIt(f"{test_location.game} {self.rule_iterations} " | 
					
						
							|  |  |  |                         f"runs of {test_location}.access_rule({state_name})", logger) as t: | 
					
						
							|  |  |  |                 for _ in range(self.rule_iterations): | 
					
						
							|  |  |  |                     test_location.access_rule(state) | 
					
						
							|  |  |  |                 # if time is taken to disentangle complex ref chains, | 
					
						
							|  |  |  |                 # this time should be attributed to the rule. | 
					
						
							|  |  |  |                 gc.collect() | 
					
						
							|  |  |  |             return t.dif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def main(self): | 
					
						
							|  |  |  |             for game in sorted(AutoWorld.AutoWorldRegister.world_types): | 
					
						
							|  |  |  |                 summary_data: typing.Dict[str, collections.Counter[str]] = { | 
					
						
							|  |  |  |                     "empty_state": collections.Counter(), | 
					
						
							|  |  |  |                     "all_state": collections.Counter(), | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     multiworld = MultiWorld(1) | 
					
						
							|  |  |  |                     multiworld.game[1] = game | 
					
						
							|  |  |  |                     multiworld.player_name = {1: "Tester"} | 
					
						
							|  |  |  |                     multiworld.set_seed(0) | 
					
						
							|  |  |  |                     multiworld.state = CollectionState(multiworld) | 
					
						
							|  |  |  |                     args = argparse.Namespace() | 
					
						
							|  |  |  |                     for name, option in AutoWorld.AutoWorldRegister.world_types[game].options_dataclass.type_hints.items(): | 
					
						
							|  |  |  |                         setattr(args, name, { | 
					
						
							|  |  |  |                             1: option.from_any(getattr(option, "default")) | 
					
						
							|  |  |  |                         }) | 
					
						
							|  |  |  |                     multiworld.set_options(args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     gc.collect() | 
					
						
							|  |  |  |                     for step in self.gen_steps: | 
					
						
							|  |  |  |                         with TimeIt(f"{game} step {step}", logger): | 
					
						
							|  |  |  |                             call_all(multiworld, step) | 
					
						
							|  |  |  |                             gc.collect() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     locations = sorted(multiworld.get_unfilled_locations()) | 
					
						
							|  |  |  |                     if not locations: | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     all_state = multiworld.get_all_state(False) | 
					
						
							|  |  |  |                     for location in locations: | 
					
						
							|  |  |  |                         time_taken = self.location_test(location, multiworld.state, "empty_state") | 
					
						
							|  |  |  |                         summary_data["empty_state"][location.name] = time_taken | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         time_taken = self.location_test(location, all_state, "all_state") | 
					
						
							|  |  |  |                         summary_data["all_state"][location.name] = time_taken | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     total_empty_state = sum(summary_data["empty_state"].values()) | 
					
						
							|  |  |  |                     total_all_state = sum(summary_data["all_state"].values()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     logger.info(f"{game} took {total_empty_state/len(locations):.4f} " | 
					
						
							|  |  |  |                                 f"seconds per location in empty_state and {total_all_state/len(locations):.4f} " | 
					
						
							|  |  |  |                                 f"in all_state. (all times summed for {self.rule_iterations} runs.)") | 
					
						
							|  |  |  |                     logger.info(f"Top times in empty_state:\n" | 
					
						
							|  |  |  |                                 f"{self.format_times_from_counter(summary_data['empty_state'])}") | 
					
						
							|  |  |  |                     logger.info(f"Top times in all_state:\n" | 
					
						
							|  |  |  |                                 f"{self.format_times_from_counter(summary_data['all_state'])}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 except Exception as e: | 
					
						
							|  |  |  |                     logger.exception(e) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     runner = BenchmarkRunner() | 
					
						
							|  |  |  |     runner.main() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     from path_change import change_home | 
					
						
							|  |  |  |     change_home() | 
					
						
							|  |  |  |     run_locations_benchmark() |