From 9ca166c171423184e28f74a782dec2bf46b25760 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 15 Nov 2025 22:03:19 +0100 Subject: [PATCH 1/8] Use _PyObject_GetMethodStackRef for slot_tp_new --- Objects/typeobject.c | 13 ++++++++++--- Tools/ftscalingbench/ftscalingbench.py | 24 +++++++++++++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 5841deb454da1f..26d18c9312024f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -10852,13 +10852,20 @@ 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); + int meth_found = _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); + //Py_DECREF(func); + _PyThreadState_PopCStackRef(tstate, &c_stackref); return result; } diff --git a/Tools/ftscalingbench/ftscalingbench.py b/Tools/ftscalingbench/ftscalingbench.py index 1a59e25189d5dd..88eb7b2ce529c4 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,31 @@ 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 namedtuple(): + for i in range(1000 * WORK_SCALE): + _ = Foo(x=1) + @register_benchmark def object_cfunction(): accu = 0 From e684ed97b77cf3c79dd0bfd63d85fbef3983be1c Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 15 Nov 2025 22:32:39 +0100 Subject: [PATCH 2/8] remove double definition --- Programs/test_frozenmain.h | 12 ++++++------ Tools/ftscalingbench/ftscalingbench.py | 5 ----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index dbeedb7ffe0ce6..b9bf4134b59718 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -13,10 +13,10 @@ unsigned char M_test_frozenmain[] = { 82,5,93,6,12,0,82,6,93,5,93,6,44,26,0,0, 0,0,0,0,0,0,0,0,12,0,50,4,52,1,0,0, 0,0,0,0,31,0,75,26,0,0,9,0,30,0,82,1, - 35,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122, - 101,110,32,72,101,108,108,111,32,87,111,114,108,100,122,8, + 35,0,41,8,233,0,0,0,0,78,218,18,70,114,111,122, + 101,110,32,72,101,108,108,111,32,87,111,114,108,100,218,8, 115,121,115,46,97,114,103,118,218,6,99,111,110,102,105,103, - 122,7,99,111,110,102,105,103,32,122,2,58,32,41,5,218, + 218,7,99,111,110,102,105,103,32,218,2,58,32,41,5,218, 12,112,114,111,103,114,97,109,95,110,97,109,101,218,10,101, 120,101,99,117,116,97,98,108,101,218,15,117,115,101,95,101, 110,118,105,114,111,110,109,101,110,116,218,17,99,111,110,102, @@ -25,15 +25,15 @@ unsigned char M_test_frozenmain[] = { 3,115,121,115,218,17,95,116,101,115,116,105,110,116,101,114, 110,97,108,99,97,112,105,218,5,112,114,105,110,116,218,4, 97,114,103,118,218,11,103,101,116,95,99,111,110,102,105,103, - 115,114,3,0,0,0,218,3,107,101,121,169,0,243,0,0, + 115,114,5,0,0,0,218,3,107,101,121,169,0,243,0,0, 0,0,218,18,116,101,115,116,95,102,114,111,122,101,110,109, 97,105,110,46,112,121,218,8,60,109,111,100,117,108,101,62, - 114,18,0,0,0,1,0,0,0,115,94,0,0,0,240,3, + 114,22,0,0,0,1,0,0,0,115,94,0,0,0,240,3, 1,1,1,243,8,0,1,11,219,0,24,225,0,5,208,6, 26,212,0,27,217,0,5,128,106,144,35,151,40,145,40,212, 0,27,216,9,26,215,9,38,210,9,38,211,9,40,168,24, 213,9,50,128,6,243,2,6,12,2,128,67,241,14,0,5, 10,136,71,144,67,144,53,152,2,152,54,160,35,157,59,152, - 45,208,10,40,214,4,41,243,15,6,12,2,114,16,0,0, + 45,208,10,40,214,4,41,243,15,6,12,2,114,20,0,0, 0, }; diff --git a/Tools/ftscalingbench/ftscalingbench.py b/Tools/ftscalingbench/ftscalingbench.py index 88eb7b2ce529c4..a5cc7e0625d6ed 100644 --- a/Tools/ftscalingbench/ftscalingbench.py +++ b/Tools/ftscalingbench/ftscalingbench.py @@ -60,11 +60,6 @@ def namedtuple(): for i in range(1000 * WORK_SCALE): _ = Bar(x=1) -@register_benchmark -def namedtuple(): - for i in range(1000 * WORK_SCALE): - _ = Foo(x=1) - @register_benchmark def object_cfunction(): accu = 0 From 28bc3511c56ae15e57cc3c859faac917776f8cd1 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 15 Nov 2025 22:35:42 +0100 Subject: [PATCH 3/8] remove dead code --- Objects/typeobject.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 26d18c9312024f..b59abd980f8794 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -10862,9 +10862,7 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } 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; } From 16198642e1e48118d5785d120bcaffa3c94c8f87 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 15 Nov 2025 23:07:41 +0100 Subject: [PATCH 4/8] add assert --- Objects/typeobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index b59abd980f8794..02a9a2f3fba73d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -10860,6 +10860,7 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) _PyThreadState_PopCStackRef(tstate, &c_stackref); return NULL; } + assert(meth_found); func = PyStackRef_AsPyObjectBorrow(c_stackref.ref); assert(func != NULL); result = _PyObject_Call_Prepend(tstate, func, (PyObject *)type, args, kwds); From b1b908581d67be2b92f80d376d649ca15e83cee3 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 16 Nov 2025 00:13:34 +0100 Subject: [PATCH 5/8] Apply suggestions from code review --- Objects/typeobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 02a9a2f3fba73d..a320deba593a6c 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -10854,13 +10854,12 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) _PyCStackRef c_stackref; _PyThreadState_PushCStackRef(tstate, &c_stackref); - int meth_found = _PyObject_GetMethodStackRef(tstate, (PyObject *)type, &_Py_ID(__new__), &c_stackref.ref); + _PyObject_GetMethodStackRef(tstate, (PyObject *)type, &_Py_ID(__new__), &c_stackref.ref); if (PyStackRef_IsNull(c_stackref.ref)) { _PyThreadState_PopCStackRef(tstate, &c_stackref); return NULL; } - assert(meth_found); func = PyStackRef_AsPyObjectBorrow(c_stackref.ref); assert(func != NULL); result = _PyObject_Call_Prepend(tstate, func, (PyObject *)type, args, kwds); From 1d2ef1135efc12c0e4cc2492dbd659b80a3c1f58 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 16 Nov 2025 20:28:56 +0000 Subject: [PATCH 6/8] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-11-16-20-28-48.gh-issue-139103.gZNQMz.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-11-16-20-28-48.gh-issue-139103.gZNQMz.rst 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..7bd6b15e3d1901 --- /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`. From b141d27677b98535a5a7c06e9b57f23b661e2169 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 16 Nov 2025 21:30:18 +0100 Subject: [PATCH 7/8] Apply suggestions from code review --- .../2025-11-16-20-28-48.gh-issue-139103.gZNQMz.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 7bd6b15e3d1901..7c2d3c18f364d5 100644 --- 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 @@ -1 +1 @@ -Improve performance of concurrent object construction by using `_PyObject_GetMethodStackRef` in `slot_tp_new`. +Improve performance of concurrent object construction by using ``_PyObject_GetMethodStackRef`` in ``slot_tp_new``. From d8930e063533d84bddb310f9c602b205f635179a Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 16 Nov 2025 21:31:02 +0100 Subject: [PATCH 8/8] revert changes to auto-generated file --- Programs/test_frozenmain.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index b9bf4134b59718..dbeedb7ffe0ce6 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -13,10 +13,10 @@ unsigned char M_test_frozenmain[] = { 82,5,93,6,12,0,82,6,93,5,93,6,44,26,0,0, 0,0,0,0,0,0,0,0,12,0,50,4,52,1,0,0, 0,0,0,0,31,0,75,26,0,0,9,0,30,0,82,1, - 35,0,41,8,233,0,0,0,0,78,218,18,70,114,111,122, - 101,110,32,72,101,108,108,111,32,87,111,114,108,100,218,8, + 35,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122, + 101,110,32,72,101,108,108,111,32,87,111,114,108,100,122,8, 115,121,115,46,97,114,103,118,218,6,99,111,110,102,105,103, - 218,7,99,111,110,102,105,103,32,218,2,58,32,41,5,218, + 122,7,99,111,110,102,105,103,32,122,2,58,32,41,5,218, 12,112,114,111,103,114,97,109,95,110,97,109,101,218,10,101, 120,101,99,117,116,97,98,108,101,218,15,117,115,101,95,101, 110,118,105,114,111,110,109,101,110,116,218,17,99,111,110,102, @@ -25,15 +25,15 @@ unsigned char M_test_frozenmain[] = { 3,115,121,115,218,17,95,116,101,115,116,105,110,116,101,114, 110,97,108,99,97,112,105,218,5,112,114,105,110,116,218,4, 97,114,103,118,218,11,103,101,116,95,99,111,110,102,105,103, - 115,114,5,0,0,0,218,3,107,101,121,169,0,243,0,0, + 115,114,3,0,0,0,218,3,107,101,121,169,0,243,0,0, 0,0,218,18,116,101,115,116,95,102,114,111,122,101,110,109, 97,105,110,46,112,121,218,8,60,109,111,100,117,108,101,62, - 114,22,0,0,0,1,0,0,0,115,94,0,0,0,240,3, + 114,18,0,0,0,1,0,0,0,115,94,0,0,0,240,3, 1,1,1,243,8,0,1,11,219,0,24,225,0,5,208,6, 26,212,0,27,217,0,5,128,106,144,35,151,40,145,40,212, 0,27,216,9,26,215,9,38,210,9,38,211,9,40,168,24, 213,9,50,128,6,243,2,6,12,2,128,67,241,14,0,5, 10,136,71,144,67,144,53,152,2,152,54,160,35,157,59,152, - 45,208,10,40,214,4,41,243,15,6,12,2,114,20,0,0, + 45,208,10,40,214,4,41,243,15,6,12,2,114,16,0,0, 0, };