Conversation
Zaczero
force-pushed
the
pr/freelist-correctness
branch
from
September 20, 2026 10:55
0ee539e to
8807eaf
Compare
Merging this PR will regress 2 benchmarks
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | into_i128_zero |
817.2 ns | 982.4 ns | -16.82% |
| ❌ | test_empty_class_init |
28.5 µs | 31.7 µs | -10.05% |
| ⚡ | extract_u128_small |
1.9 µs | 1.6 µs | +13.4% |
| ⚡ | extract_u128_zero |
1.9 µs | 1.7 µs | +12.97% |
| ⚡ | into_u128_small |
987.2 ns | 874.7 ns | +12.86% |
| ⚡ | extract_u128_u32_max |
2.1 µs | 1.8 µs | +12.03% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing Zaczero:pr/freelist-correctness (1516200) with main (52a646f)
Footnotes
-
6 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
Complete all Rust base initializers before restoring GC tracking. Fresh allocations and native constructors which already track the instance retain their existing state. Add regression coverage for confirmed cache hits, cycle collection, inherited Rust payloads, dictionaries, and weak references.
Zaczero
force-pushed
the
pr/freelist-correctness
branch
2 times, most recently
from
September 20, 2026 13:09
67f77d1 to
2e1df45
Compare
Allow all N slots to retain allocations, including a one-entry freelist. Cover empty, full, overflow, refill, and LIFO behavior, and correct the reuse test which depended on the old N - 1 limit.
Zaczero
force-pushed
the
pr/freelist-correctness
branch
from
September 20, 2026 13:15
2e1df45 to
1516200
Compare
Zaczero
marked this pull request as ready for review
September 20, 2026 13:53
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Impact
Objects reused by
#[pyclass(freelist = N)]can remain untracked by CPython's cyclic garbage collector. If a reused object enters a reference cycle, dropping its external references and callinggc.collect()does not reclaim it. The cycle can retain Python objects and Rust-owned resources in long-running processes.The freelist also retains only
N - 1allocations. In particular,freelist = 1never caches an allocation. This PR fixes both correctness issues in separate commits, preserving the existing cache implementation.Approach
PyObject_Initrestores the object header and type reference, but does not restore GC tracking. Tracking is restored at the complete-instance initialization boundary, after every recursive Rust base initializer has finished. Fresh allocations and native-base instances that are already tracked retain that state.The insertion bound is corrected to admit all
Nentries. Capacity zero remains valid, and overflow returns the allocation to the caller.Regression coverage
freelist_reuse_is_tracked_and_collectibleprimes and confirms a cache hit, checks GC tracking, creates a self-cycle, and observes eventual destruction through the suite's bounded free-threading-aware collection helper. It failed on the original implementation.freelist_reuse_initializes_all_rust_basesdeterministically confirms reuse before covering recursive Rust initialization, instance-dictionary reset, weak references, and eventual cycle collection outside the attachment scope. It also failed before the lifecycle fix.exact_capacity_and_lifoexercises capacities 0, 1, 2, and 17 through empty, full, overflow, refill, and reverse-order removal.class_with_freelistretains its existing recycling and distinct-live-allocation checks, with the declared capacity changed to one.The GC suite and freelist unit tests cover 28 cases on CPython 3.14.7 and 28 on its free-threaded build. The no-std configuration covers 12 applicable cases. All completed successfully.
The shared-process GC suite also covers 27 cases on free-threaded CPython 3.15 development. That configuration exposed and now guards against assuming that one
gc.collect()call is a destruction barrier while collection is concurrent.The capacity unit test checks the insertion bound independently. Disabling only GC re-tracking, with the capacity fix retained, makes both GC regressions fail at their tracking assertions.
Validation commands
These lifecycle changes were also exercised across CPython 3.9–3.15, debug Python, and stable-ABI builds.
A release note is included.
Related to #6133: these defects were found while establishing a correct baseline for the allocation-cache performance investigation. This PR addresses correctness independently of the proposed optimization.
AI assistance was used for investigation, implementation, and test preparation.