Skip to content

gh-159437: Combine GC reference passes - #159599

Draft
mfalco-oai wants to merge 5 commits into
python:mainfrom
mfalco-oai:mfalco-oai/gh-159437-gc-ref-fusion-restricted
Draft

mfalco-oai wants to merge 5 commits into
python:mainfrom
mfalco-oai:mfalco-oai/gh-159437-gc-ref-fusion-restricted

Conversation

@mfalco-oai

@mfalco-oai mfalco-oai commented Oct 16, 2026 •

Copy link
Copy Markdown

With this optimization the GIL collector initializes temporary reference counts while subtracting references within the collection. Initializing each count on first encounter combines two walks over the objects into one.

The combined pass is limited to the initial reachability analysis of full collections. It requires an empty permanent generation and is disabled after any subinterpreter reservation or runtime reinitialization. These restrictions ensure every tracked mortal referent is part of the collection. Young collections and resurrection checks keep the existing passes.

Performance

Before is #159590 (0daa2029); after is this PR at 9fe33d48. Both builds use -falign-functions=64.

btree_gc_only times full collection of a 200,000-node B-tree.

CPU Before After Change (95% interval) Result
AMD 94.169 ms 85.092 ms −9.64% [−11.05%, −7.64%] Improvement
ARM 86.499 ms 77.442 ms −10.47% [−11.31%, −9.62%] Inconclusive (A/A failed)

Benchmarks: python/pyperformance#496

@bedevere-app bedevere-app Bot added the type-feature A feature request or enhancement label Oct 16, 2026
Comment thread Python/pystate.c
/* Monotonic guard for tracked objects that may outlive their interpreter.
* Kept outside _PyRuntime so reinitialization cannot clear it.
*/
static int may_have_foreign_gc_objects;

@pablogsal pablogsal Oct 17, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition is only used by GC. A simular guard I used in an earlier PR lived in gc.c and was updated from _PyGC_InitState, which avoided another accessor and declaration.

Is there a lifecycle case that needs this set before GC state initialization? Otherwise we could keep it local to GC, while still remembering previous interpreters after teardown and runtime restart.

'bar()\n')
# Full GIL collections may skip update_refs(). Stop at a visible phase
# after the collector marks collection active.
if support.Py_GIL_DISABLED:

@pablogsal pablogsal Oct 17, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use gc_collect_main regardless of the GIL? The collectors have it and the GDB helper already recognizes it. There's an example in an earlier PR, where I made the same breakpoint change. That would remove this branch.

@mfalco-oai
mfalco-oai force-pushed the mfalco-oai/gh-159437-gc-ref-fusion-restricted branch 2 times, most recently from 0c2699e to b676398 Compare October 19, 2026 22:23
Count the stable unreachable set while preparing callbacks and retain a conservative cleanup flag. Reuse the count only when late weakref clearing has no work, and force fresh cleanup and counting after resurrection analysis.

Add a regression for a callback-free weakref observed when clearing a young cycle releases an older finalizer.
@mfalco-oai
mfalco-oai force-pushed the mfalco-oai/gh-159437-gc-ref-fusion-restricted branch from b676398 to 9fe33d4 Compare October 21, 2026 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type-feature A feature request or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants