diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-16-20-28-48.gh-issue-139103.gZNQMz.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-16-20-28-48.gh-issue-139103.gZNQMz.rst new file mode 100644 index 00000000000000..7c2d3c18f364d5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-16-20-28-48.gh-issue-139103.gZNQMz.rst @@ -0,0 +1 @@ +Improve performance of concurrent object construction by using ``_PyObject_GetMethodStackRef`` in ``slot_tp_new``. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 61bcc21ce13d47..2237212bbd89d7 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -10888,13 +10888,18 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyThreadState *tstate = _PyThreadState_GET(); PyObject *func, *result; - func = PyObject_GetAttr((PyObject *)type, &_Py_ID(__new__)); - if (func == NULL) { + _PyCStackRef c_stackref; + _PyThreadState_PushCStackRef(tstate, &c_stackref); + _PyObject_GetMethodStackRef(tstate, (PyObject *)type, &_Py_ID(__new__), &c_stackref.ref); + + if (PyStackRef_IsNull(c_stackref.ref)) { + _PyThreadState_PopCStackRef(tstate, &c_stackref); return NULL; } - + func = PyStackRef_AsPyObjectBorrow(c_stackref.ref); + assert(func != NULL); result = _PyObject_Call_Prepend(tstate, func, (PyObject *)type, args, kwds); - Py_DECREF(func); + _PyThreadState_PopCStackRef(tstate, &c_stackref); return result; } diff --git a/Tools/ftscalingbench/ftscalingbench.py b/Tools/ftscalingbench/ftscalingbench.py index 1a59e25189d5dd..a5cc7e0625d6ed 100644 --- a/Tools/ftscalingbench/ftscalingbench.py +++ b/Tools/ftscalingbench/ftscalingbench.py @@ -28,6 +28,8 @@ import threading import time from operator import methodcaller +from typing import NamedTuple +from collections import namedtuple # The iterations in individual benchmarks are scaled by this factor. WORK_SCALE = 100 @@ -38,11 +40,26 @@ in_queues = [] out_queues = [] - def register_benchmark(func): ALL_BENCHMARKS[func.__name__] = func return func +class Foo(NamedTuple): + x: int + + +Bar = namedtuple('Bar', ['x']) + +@register_benchmark +def typing_namedtuple(): + for i in range(1000 * WORK_SCALE): + _ = Foo(x=1) + +@register_benchmark +def namedtuple(): + for i in range(1000 * WORK_SCALE): + _ = Bar(x=1) + @register_benchmark def object_cfunction(): accu = 0