From 83193b5e32f4b066eb396814e94528c68a2d484a Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 23 Apr 2026 19:07:32 +0530 Subject: [PATCH 1/2] fix scaling of descriptors on free-threading --- Objects/typeobject.c | 17 ++++++++++++----- Tools/ftscalingbench/ftscalingbench.py | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index fb3c7101410683..98e93a4567a7a9 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6289,6 +6289,10 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef PyObject *res_obj = PyStackRef_AsPyObjectBorrow(*out); #if Py_GIL_DISABLED + // Optimistically enable deferred refcounting for the result if it's not already enabled. + if (res_obj != NULL && PyType_IS_GC(Py_TYPE(res_obj)) && !_PyObject_HasDeferredRefcount(res_obj)) { + PyUnstable_Object_EnableDeferredRefcount(res_obj); + } update_cache_gil_disabled(entry, name, version_tag, res_obj); #else PyObject *old_value = update_cache(entry, name, version_tag, res_obj); @@ -10995,10 +10999,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) @@ -11010,9 +11016,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)} From dabfd091d7f67b014862902f96c47c9e181e4b15 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 23 Apr 2026 19:17:23 +0530 Subject: [PATCH 2/2] drc only for descriptors --- Objects/typeobject.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 98e93a4567a7a9..636c07f9213575 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6289,8 +6289,11 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef PyObject *res_obj = PyStackRef_AsPyObjectBorrow(*out); #if Py_GIL_DISABLED - // Optimistically enable deferred refcounting for the result if it's not already enabled. - if (res_obj != NULL && PyType_IS_GC(Py_TYPE(res_obj)) && !_PyObject_HasDeferredRefcount(res_obj)) { + // Optimistically enable deferred refcounting on descriptors + if (res_obj != NULL && + PyType_IS_GC(Py_TYPE(res_obj)) && + !_PyObject_HasDeferredRefcount(res_obj) && + Py_TYPE(res_obj)->tp_descr_get != NULL) { PyUnstable_Object_EnableDeferredRefcount(res_obj); } update_cache_gil_disabled(entry, name, version_tag, res_obj);