You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Skip fetching the thread state for non-GC-tracked objects in _Py_Dealloc #154429
_Py_Dealloc() unconditionally fetches the current thread state and computes the C recursion margin on every deallocation:
PyThreadState*tstate=_PyThreadState_GET();
intptr_tmargin=_Py_RecursionLimit_GetMargin(tstate);
if (margin<2&&gc_flag) { /* trashcan deposit */ }
Both values are only consumed by the trashcan mechanism, which applies exclusively to GC-tracked types — only they can form the deep deallocation chains the trashcan guards against. For the common non-GC case (int, float, str and other atomic types), fetching the thread state and computing the margin is wasted work.
On most platforms _PyThreadState_GET() is a cheap thread-local load, but on macOS a _Thread_local access compiles to a function call to the TLV resolver (tlv_get_addr), so this is a real per-deallocation cost on Apple Silicon. Deallocation of non-GC atomic temporaries is one of the most frequent operations in typical workloads.
Gating the thread-state fetch and margin computation behind gc_flag skips them entirely for non-GC objects. The change is small and semantics-preserving.
Preliminary microbenchmark results (compute/allocation-heavy microbenchmarks, non-PGO build): ~1% faster on an Apple Silicon free-threaded build; neutral on Linux/x86-64 (where the thread-local read is already a fixed-offset load, so there is nothing to skip). Broader benchmarking (pyperformance, PGO/LTO) is still to be done.
Has this already been discussed elsewhere?
This is a minor feature or performance improvement
Activity
sergey-miryanov commentedon Jul 22, 2026
Duplicate of #154401
_Py_Deallocfor MacOS ARM #154401_Py_Deallocfor MacOS ARM #154401