Skip to content

Commit c22629d

Browse files
authored
Disable GC freeze hack in parallel workers (#21207)
Unfortunately, this hack causes severe performance regression on Python 3.14, where GC works differently. A single freeze still works, but repeated freezes are very slow. This PR makes parallel self-check ~5% slower on Python 3.12, but doesn't have any effect on `torch` (which however is not very parallelizeable). But I think it is a safer bet to not do any aggressive GC manipulation (beyond single freeze, which is kind of an established pattern). We can tune it more later, when we are done with more "algorithmic" improvements.
1 parent 5666c91 commit c22629d

File tree

2 files changed

+7
-16
lines changed

2 files changed

+7
-16
lines changed

mypy/build.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4383,9 +4383,7 @@ def process_stale_scc(
43834383
if (
43844384
not manager.options.test_env
43854385
and platform.python_implementation() == "CPython"
4386-
# Parallel workers perform loading in many smaller "pieces", so we
4387-
# should repeat the GC hack multiple times to actually benefit from it.
4388-
and (manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES or manager.parallel_worker)
4386+
and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES
43894387
):
43904388
# When deserializing cache we create huge amount of new objects, so even
43914389
# with our generous GC thresholds, GC is still doing a lot of pointless
@@ -4394,16 +4392,14 @@ def process_stale_scc(
43944392
# generation with the freeze()/unfreeze() trick below. This is arguably
43954393
# a hack, but it gives huge performance wins for large third-party
43964394
# libraries, like torch.
4397-
gc.collect(generation=1)
4398-
gc.collect(generation=0)
43994395
gc.disable()
44004396
for prev_scc in fresh_sccs_to_load:
44014397
manager.done_sccs.add(prev_scc.id)
44024398
process_fresh_modules(graph, sorted(prev_scc.mod_ids), manager)
44034399
if (
44044400
not manager.options.test_env
44054401
and platform.python_implementation() == "CPython"
4406-
and (manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES or manager.parallel_worker)
4402+
and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES
44074403
):
44084404
manager.gc_freeze_cycles += 1
44094405
gc.freeze()

mypy/build_worker/worker.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -179,22 +179,17 @@ def serve(server: IPCServer, ctx: ServerContext) -> None:
179179
manager.add_stats(scc_wait_time=t1 - t0, scc_receive_time=time.time() - t1)
180180
scc_id = scc_message.scc_id
181181
if scc_id is None:
182+
gc_stats = gc.get_stats()
183+
manager.add_stats(
184+
gc_collections_gen0=gc_stats[0]["collections"],
185+
gc_collections_gen1=gc_stats[1]["collections"],
186+
)
182187
manager.dump_stats()
183188
break
184189
scc = manager.scc_by_id[scc_id]
185190
t0 = time.time()
186191
try:
187-
if platform.python_implementation() == "CPython":
188-
# Since we are splitting the GC freeze hack into multiple smaller freezes,
189-
# we should collect young generations to not accumulate accidental garbage.
190-
gc.collect(generation=1)
191-
gc.collect(generation=0)
192-
gc.disable()
193192
load_states(scc, graph, manager, scc_message.import_errors, scc_message.mod_data)
194-
if platform.python_implementation() == "CPython":
195-
gc.freeze()
196-
gc.unfreeze()
197-
gc.enable()
198193
result = process_stale_scc(graph, scc, manager, from_cache=graph_data.from_cache)
199194
# We must commit after each SCC, otherwise we break --sqlite-cache.
200195
manager.commit()

0 commit comments

Comments
 (0)