From d3588a057c4a5ba317fa4ecbc3a47e990fef426d Mon Sep 17 00:00:00 2001 From: Mysteryem Date: Fri, 10 Oct 2025 16:19:52 +0100 Subject: [PATCH] Tests: gc.freeze() by default in the test\benchmark\locations.py (#5055) Without `gc.freeze()` and `gc.unfreeze()` afterward, the `gc.collect()` call within each benchmark often takes much longer than all 100_000 iterations of the location access rule, making it difficult to benchmark all but the slowest of access rules. This change enables using `gc.freeze()` by default. --- test/benchmark/locations.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/benchmark/locations.py b/test/benchmark/locations.py index 0e496cd3..245dcb60 100644 --- a/test/benchmark/locations.py +++ b/test/benchmark/locations.py @@ -1,4 +1,12 @@ -def run_locations_benchmark(): +def run_locations_benchmark(freeze_gc: bool = True) -> None: + """ + Run a benchmark of location access rule performance against an empty_state and an all_state. + + :param freeze_gc: Whether to freeze gc before benchmarking and unfreeze gc afterward. Freezing gc moves all objects + tracked by the garbage collector to a permanent generation, ignoring them in all future collections. Freezing + greatly reduces the duration of running gc.collect() within benchmarks, which otherwise often takes much longer + than running all iterations for the location rule being benchmarked. + """ import argparse import logging import gc @@ -34,6 +42,8 @@ def run_locations_benchmark(): 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: + if freeze_gc: + gc.freeze() 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): @@ -41,6 +51,8 @@ def run_locations_benchmark(): # if time is taken to disentangle complex ref chains, # this time should be attributed to the rule. gc.collect() + if freeze_gc: + gc.unfreeze() return t.dif def main(self): @@ -64,9 +76,13 @@ def run_locations_benchmark(): gc.collect() for step in self.gen_steps: + if freeze_gc: + gc.freeze() with TimeIt(f"{game} step {step}", logger): call_all(multiworld, step) gc.collect() + if freeze_gc: + gc.unfreeze() locations = sorted(multiworld.get_unfilled_locations()) if not locations: