Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ class property(DynamicClassAttribute):
_attr_type = None
_cls_type = None

def __init__(self, fget=None, fset=None, fdel=None, doc=None):
sys._enable_deferred_refcount(self)
super().__init__(fget, fset, fdel, doc)

def __get__(self, instance, ownerclass=None):
if instance is None:
if self.member is not None:
Expand Down Expand Up @@ -1067,6 +1071,7 @@ def _find_new_(mcls, classdict, member_type, first_enum):
return __new__, save_new, use_args

def _add_member_(cls, name, member):
sys._enable_deferred_refcount(member)
# _value_ structures are not updated
if name in cls._member_map_:
if cls._member_map_[name] is not member:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :func:`sys._enable_deferred_refcount` and use it in :mod:`enum` to
improve free-threaded scaling.
10 changes: 7 additions & 3 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -10925,15 +10925,19 @@ static PyObject *
slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
PyTypeObject *tp = Py_TYPE(self);
PyObject *get;
PyThreadState *tstate = _PyThreadState_GET();
_PyCStackRef cref;
_PyThreadState_PushCStackRef(tstate, &cref);

get = _PyType_LookupRef(tp, &_Py_ID(__get__));
_PyType_LookupStackRefAndVersion(tp, &_Py_ID(__get__), &cref.ref);
PyObject *get = PyStackRef_AsPyObjectBorrow(cref.ref);
if (get == NULL) {
#ifndef Py_GIL_DISABLED
/* Avoid further slowdowns */
if (tp->tp_descr_get == slot_tp_descr_get)
tp->tp_descr_get = NULL;
#endif
_PyThreadState_PopCStackRef(tstate, &cref);
return Py_NewRef(self);
}
if (obj == NULL)
Expand All @@ -10942,7 +10946,7 @@ slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
type = Py_None;
PyObject *stack[3] = {self, obj, type};
PyObject *res = PyObject_Vectorcall(get, stack, 3, NULL);
Py_DECREF(get);
_PyThreadState_PopCStackRef(tstate, &cref);
return res;
}

Expand Down
34 changes: 33 additions & 1 deletion Python/clinic/sysmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,26 @@ sys__is_immortal_impl(PyObject *module, PyObject *op)
return PyUnstable_IsImmortal(op);
}

/*[clinic input]
sys._enable_deferred_refcount -> bool

op: object
/

Enable deferred reference counting on the object.

Return True if deferred reference counting was successfully enabled, and
False otherwise. This is primarily useful for avoiding reference count
contention on objects that are shared between multiple threads.
[clinic start generated code]*/

static int
sys__enable_deferred_refcount_impl(PyObject *module, PyObject *op)
/*[clinic end generated code: output=d19c0f74be9da2a8 input=92d197248dcfb1f7]*/
{
return PyUnstable_Object_EnableDeferredRefcount(op);
}

/*
* Cached interned string objects used for calling the profile and
* trace functions.
Expand Down Expand Up @@ -2942,6 +2962,7 @@ static PyMethodDef sys_methods[] = {
SYS_GETWINDOWSVERSION_METHODDEF
SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF
SYS__IS_IMMORTAL_METHODDEF
SYS__ENABLE_DEFERRED_REFCOUNT_METHODDEF
SYS_INTERN_METHODDEF
SYS__IS_INTERNED_METHODDEF
SYS_IS_FINALIZING_METHODDEF
Expand Down
20 changes: 20 additions & 0 deletions Tools/ftscalingbench/ftscalingbench.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import time
from collections import namedtuple
from dataclasses import dataclass
from enum import Enum
from operator import methodcaller
from typing import NamedTuple

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


class MyEnum(Enum):
X = 1
Y = 2
Z = 3

@register_benchmark
def enum_attr():
for _ in range(1000 * WORK_SCALE):
MyEnum.X
MyEnum.Y
MyEnum.Z

@register_benchmark
def enum_value():
for _ in range(1000 * WORK_SCALE):
MyEnum.X.value
MyEnum.Y.value
MyEnum.Z.value

@register_benchmark
def deepcopy():
x = {'list': [1, 2], 'tuple': (1, None)}
Expand Down
Loading