https://github.com/python/cpython/commit/166c56b31162924ddbb594b67169004882dd1c07
commit: 166c56b31162924ddbb594b67169004882dd1c07
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-05-13T18:09:33Z
summary:

[3.15] gh-148906: fix performance scaling of descriptors on free-threading 
(GH-148915) (#149798)

gh-148906: fix performance scaling of descriptors on free-threading (GH-148915)
(cherry picked from commit 94bca40ff09c20f6168d6a27e3aa42bf8a8077b8)

Co-authored-by: Kumar Aditya <[email protected]>

files:
M Objects/typeobject.c
M Tools/ftscalingbench/ftscalingbench.py

diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 9a18ca72516da7..7cca137f74be58 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -4841,6 +4841,18 @@ type_new_set_attrs(const type_new_ctx *ctx, PyTypeObject 
*type)
     if (type_new_set_classdictcell(dict) < 0) {
         return -1;
     }
+
+#ifdef Py_GIL_DISABLED
+    // enable deferred reference counting on functions and descriptors
+    Py_ssize_t pos = 0;
+    PyObject *key, *value;
+    while (PyDict_Next(dict, &pos, &key, &value)) {
+        if (PyFunction_Check(value) || Py_TYPE(value)->tp_descr_get != NULL) {
+            PyUnstable_Object_EnableDeferredRefcount(value);
+        }
+    }
+#endif
+
     return 0;
 }
 
@@ -6746,12 +6758,11 @@ type_setattro(PyObject *self, PyObject *name, PyObject 
*value)
     assert(!_PyType_HasFeature(metatype, Py_TPFLAGS_MANAGED_DICT));
 
 #ifdef Py_GIL_DISABLED
-    // gh-139103: Enable deferred refcounting for functions assigned
-    // to type objects.  This is important for `dataclass.__init__`,
-    // which is generated dynamically.
-    if (value != NULL &&
-        PyFunction_Check(value) &&
-        !_PyObject_HasDeferredRefcount(value))
+    // gh-139103: Enable deferred refcounting for functions and descriptors
+    // assigned to type objects.  This is important for `dataclass.__init__`,
+    // which is generated dynamically, and for descriptor scaling on
+    // free-threaded builds.
+    if (value != NULL && (PyFunction_Check(value) || 
Py_TYPE(value)->tp_descr_get != NULL))
     {
         PyUnstable_Object_EnableDeferredRefcount(value);
     }
@@ -11089,10 +11100,12 @@ static PyObject *
 slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
 {
     PyTypeObject *tp = Py_TYPE(self);
-    PyObject *get;
-
-    get = _PyType_LookupRef(tp, &_Py_ID(__get__));
-    if (get == NULL) {
+    PyThreadState *tstate = _PyThreadState_GET();
+    _PyCStackRef cref;
+    _PyThreadState_PushCStackRef(tstate, &cref);
+    _PyType_LookupStackRefAndVersion(tp, &_Py_ID(__get__), &cref.ref);
+    if (PyStackRef_IsNull(cref.ref)) {
+        _PyThreadState_PopCStackRef(tstate, &cref);
 #ifndef Py_GIL_DISABLED
         /* Avoid further slowdowns */
         if (tp->tp_descr_get == slot_tp_descr_get)
@@ -11104,9 +11117,10 @@ slot_tp_descr_get(PyObject *self, PyObject *obj, 
PyObject *type)
         obj = Py_None;
     if (type == NULL)
         type = Py_None;
+    PyObject *get = PyStackRef_AsPyObjectBorrow(cref.ref);
     PyObject *stack[3] = {self, obj, type};
     PyObject *res = PyObject_Vectorcall(get, stack, 3, NULL);
-    Py_DECREF(get);
+    _PyThreadState_PopCStackRef(tstate, &cref);
     return res;
 }
 
diff --git a/Tools/ftscalingbench/ftscalingbench.py 
b/Tools/ftscalingbench/ftscalingbench.py
index 60f43b99c0f69d..c8a914c22a9e13 100644
--- a/Tools/ftscalingbench/ftscalingbench.py
+++ b/Tools/ftscalingbench/ftscalingbench.py
@@ -279,6 +279,23 @@ def staticmethod_call():
     for _ in range(1000 * WORK_SCALE):
         obj.my_staticmethod()
 
+
+class MyDescriptor:
+    def __get__(self, obj, objtype=None):
+        return 42
+
+    def __set__(self, obj, value):
+        pass
+
+class MyClassWithDescriptor:
+    attr = MyDescriptor()
+
+@register_benchmark
+def descriptor():
+    obj = MyClassWithDescriptor()
+    for _ in range(1000 * WORK_SCALE):
+        obj.attr
+
 @register_benchmark
 def deepcopy():
     x = {'list': [1, 2], 'tuple': (1, None)}

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to