gh-139103: Improve namedtuple scaling in free-threaded build (gh-144332)

Add `_Py_type_getattro_stackref`, a variant of type attribute lookup
that returns `_PyStackRef` instead of `PyObject*`. This allows returning
deferred references in the free-threaded build, reducing reference count
contention when accessing type attributes.

This significantly improves scaling of namedtuple instantiation across
multiple threads.

* Add blurb

* Rename PyObject_GetAttrStackRef to _PyObject_GetAttrStackRef

* Apply suggestion from @vstinner

Co-authored-by: Victor Stinner <vstinner@python.org>

* Apply suggestion from @vstinner

Co-authored-by: Victor Stinner <vstinner@python.org>

* format

* Update Include/internal/pycore_function.h

Co-authored-by: Victor Stinner <vstinner@python.org>

---------

Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
Sam Gross 2026-02-06 09:43:05 -05:00 committed by GitHub
parent 638d22c6e7
commit d891b2bbd1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 254 additions and 106 deletions

View file

@ -2392,10 +2392,9 @@ dummy_func(
}
else {
/* Classic, pushes one value. */
PyObject *attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
attr = _PyObject_GetAttrStackRef(PyStackRef_AsPyObjectBorrow(owner), name);
PyStackRef_CLOSE(owner);
ERROR_IF(attr_o == NULL);
attr = PyStackRef_FromPyObjectSteal(attr_o);
ERROR_IF(PyStackRef_IsNull(attr));
}
}

View file

@ -8703,19 +8703,19 @@
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
attr = _PyObject_GetAttrStackRef(PyStackRef_AsPyObjectBorrow(owner), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += -1;
stack_pointer[-1] = attr;
stack_pointer += (oparg&1);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(owner);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (attr_o == NULL) {
if (PyStackRef_IsNull(attr)) {
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
attr = PyStackRef_FromPyObjectSteal(attr_o);
stack_pointer += 1;
stack_pointer += -(oparg&1);
}
_tos_cache0 = PyStackRef_ZERO_BITS;
_tos_cache1 = PyStackRef_ZERO_BITS;

View file

@ -7924,18 +7924,18 @@
}
else {
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
attr = _PyObject_GetAttrStackRef(PyStackRef_AsPyObjectBorrow(owner), name);
stack_pointer = _PyFrame_GetStackPointer(frame);
stack_pointer += -1;
stack_pointer[-1] = attr;
stack_pointer += (oparg&1);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyStackRef_CLOSE(owner);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (attr_o == NULL) {
if (PyStackRef_IsNull(attr)) {
JUMP_TO_LABEL(error);
}
attr = PyStackRef_FromPyObjectSteal(attr_o);
stack_pointer += 1;
stack_pointer += -(oparg&1);
}
}
stack_pointer[-1] = attr;