Skip to content

Commit 8529e2f

Browse files
committed
gh-139103: Improve enum free-threaded scaling
1 parent 16ccdbc commit 8529e2f

File tree

6 files changed

+88
-4
lines changed

6 files changed

+88
-4
lines changed

Lib/enum.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ class property(DynamicClassAttribute):
185185
_attr_type = None
186186
_cls_type = None
187187

188+
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
189+
sys._enable_deferred_refcount(self)
190+
super().__init__(fget, fset, fdel, doc)
191+
188192
def __get__(self, instance, ownerclass=None):
189193
if instance is None:
190194
if self.member is not None:
@@ -1067,6 +1071,7 @@ def _find_new_(mcls, classdict, member_type, first_enum):
10671071
return __new__, save_new, use_args
10681072

10691073
def _add_member_(cls, name, member):
1074+
sys._enable_deferred_refcount(member)
10701075
# _value_ structures are not updated
10711076
if name in cls._member_map_:
10721077
if cls._member_map_[name] is not member:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :func:`sys._enable_deferred_refcount` and use it in :mod:`enum` to
2+
improve free-threaded scaling.

Objects/typeobject.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10925,15 +10925,19 @@ static PyObject *
1092510925
slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
1092610926
{
1092710927
PyTypeObject *tp = Py_TYPE(self);
10928-
PyObject *get;
10928+
PyThreadState *tstate = _PyThreadState_GET();
10929+
_PyCStackRef cref;
10930+
_PyThreadState_PushCStackRef(tstate, &cref);
1092910931

10930-
get = _PyType_LookupRef(tp, &_Py_ID(__get__));
10932+
_PyType_LookupStackRefAndVersion(tp, &_Py_ID(__get__), &cref.ref);
10933+
PyObject *get = PyStackRef_AsPyObjectBorrow(cref.ref);
1093110934
if (get == NULL) {
1093210935
#ifndef Py_GIL_DISABLED
1093310936
/* Avoid further slowdowns */
1093410937
if (tp->tp_descr_get == slot_tp_descr_get)
1093510938
tp->tp_descr_get = NULL;
1093610939
#endif
10940+
_PyThreadState_PopCStackRef(tstate, &cref);
1093710941
return Py_NewRef(self);
1093810942
}
1093910943
if (obj == NULL)
@@ -10942,7 +10946,7 @@ slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
1094210946
type = Py_None;
1094310947
PyObject *stack[3] = {self, obj, type};
1094410948
PyObject *res = PyObject_Vectorcall(get, stack, 3, NULL);
10945-
Py_DECREF(get);
10949+
_PyThreadState_PopCStackRef(tstate, &cref);
1094610950
return res;
1094710951
}
1094810952

Python/clinic/sysmodule.c.h

Lines changed: 33 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/sysmodule.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,26 @@ sys__is_immortal_impl(PyObject *module, PyObject *op)
10521052
return PyUnstable_IsImmortal(op);
10531053
}
10541054

1055+
/*[clinic input]
1056+
sys._enable_deferred_refcount -> bool
1057+
1058+
op: object
1059+
/
1060+
1061+
Enable deferred reference counting on the object.
1062+
1063+
Return True if deferred reference counting was successfully enabled, and
1064+
False otherwise. This is primarily useful for avoiding reference count
1065+
contention on objects that are shared between multiple threads.
1066+
[clinic start generated code]*/
1067+
1068+
static int
1069+
sys__enable_deferred_refcount_impl(PyObject *module, PyObject *op)
1070+
/*[clinic end generated code: output=d19c0f74be9da2a8 input=92d197248dcfb1f7]*/
1071+
{
1072+
return PyUnstable_Object_EnableDeferredRefcount(op);
1073+
}
1074+
10551075
/*
10561076
* Cached interned string objects used for calling the profile and
10571077
* trace functions.
@@ -2942,6 +2962,7 @@ static PyMethodDef sys_methods[] = {
29422962
SYS_GETWINDOWSVERSION_METHODDEF
29432963
SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF
29442964
SYS__IS_IMMORTAL_METHODDEF
2965+
SYS__ENABLE_DEFERRED_REFCOUNT_METHODDEF
29452966
SYS_INTERN_METHODDEF
29462967
SYS__IS_INTERNED_METHODDEF
29472968
SYS_IS_FINALIZING_METHODDEF

Tools/ftscalingbench/ftscalingbench.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import time
3131
from collections import namedtuple
3232
from dataclasses import dataclass
33+
from enum import Enum
3334
from operator import methodcaller
3435
from typing import NamedTuple
3536

@@ -236,6 +237,25 @@ def instantiate_typing_namedtuple():
236237
obj = MyTypingNamedTuple(x=1, y=2, z=3)
237238

238239

240+
class MyEnum(Enum):
241+
X = 1
242+
Y = 2
243+
Z = 3
244+
245+
@register_benchmark
246+
def enum_attr():
247+
for _ in range(1000 * WORK_SCALE):
248+
MyEnum.X
249+
MyEnum.Y
250+
MyEnum.Z
251+
252+
@register_benchmark
253+
def enum_value():
254+
for _ in range(1000 * WORK_SCALE):
255+
MyEnum.X.value
256+
MyEnum.Y.value
257+
MyEnum.Z.value
258+
239259
@register_benchmark
240260
def deepcopy():
241261
x = {'list': [1, 2], 'tuple': (1, None)}

0 commit comments

Comments
 (0)