https://github.com/python/cpython/commit/ce791541769a41beabec0f515cd62e504d46ff1c
commit: ce791541769a41beabec0f515cd62e504d46ff1c
branch: main
author: Edward Xu <[email protected]>
committer: colesbury <[email protected]>
date: 2025-11-19T00:57:59Z
summary:

gh-139103: fix free-threading `dataclass.__init__` perf issue (gh-141596)

The dataclasses `__init__` function is generated dynamically by a call to 
`exec()` and so doesn't have deferred reference counting enabled. Enable 
deferred reference counting on functions when assigned as an attribute to type 
objects to avoid reference count contention when creating dataclass instances.

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-23-58-23.gh-issue-139103.9cVYJ0.rst
M Objects/typeobject.c
M Tools/ftscalingbench/ftscalingbench.py

diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-23-58-23.gh-issue-139103.9cVYJ0.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-23-58-23.gh-issue-139103.9cVYJ0.rst
new file mode 100644
index 00000000000000..c038dc742ccec9
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-23-58-23.gh-issue-139103.9cVYJ0.rst
@@ -0,0 +1 @@
+Improve multithreaded scaling of dataclasses on the free-threaded build.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 61bcc21ce13d47..c99c6b3f6377b6 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -6546,6 +6546,18 @@ type_setattro(PyObject *self, PyObject *name, PyObject 
*value)
     assert(!_PyType_HasFeature(metatype, Py_TPFLAGS_INLINE_VALUES));
     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))
+    {
+        PyUnstable_Object_EnableDeferredRefcount(value);
+    }
+#endif
+
     PyObject *old_value = NULL;
     PyObject *descr = _PyType_LookupRef(metatype, name);
     if (descr != NULL) {
diff --git a/Tools/ftscalingbench/ftscalingbench.py 
b/Tools/ftscalingbench/ftscalingbench.py
index 1a59e25189d5dd..097a065f368f30 100644
--- a/Tools/ftscalingbench/ftscalingbench.py
+++ b/Tools/ftscalingbench/ftscalingbench.py
@@ -27,6 +27,7 @@
 import sys
 import threading
 import time
+from dataclasses import dataclass
 from operator import methodcaller
 
 # The iterations in individual benchmarks are scaled by this factor.
@@ -202,6 +203,17 @@ def method_caller():
     for i in range(1000 * WORK_SCALE):
         mc(obj)
 
+@dataclass
+class MyDataClass:
+    x: int
+    y: int
+    z: int
+
+@register_benchmark
+def instantiate_dataclass():
+    for _ in range(1000 * WORK_SCALE):
+        obj = MyDataClass(x=1, y=2, z=3)
+
 def bench_one_thread(func):
     t0 = time.perf_counter_ns()
     func()

_______________________________________________
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