mirror of
https://github.com/python/cpython.git
synced 2025-12-07 13:50:06 +00:00
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.
This commit is contained in:
parent
652c764a59
commit
ce79154176
3 changed files with 25 additions and 0 deletions
|
|
@ -0,0 +1 @@
|
|||
Improve multithreaded scaling of dataclasses on the free-threaded build.
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue