From 7d388eb0f89b92a51dc0d5a562920279879126a9 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 14 May 2025 12:12:12 +0530 Subject: [PATCH 1/7] add _PyObject_GetMethodStackRef --- Include/internal/pycore_object.h | 3 + Objects/call.c | 10 +-- Objects/object.c | 112 +++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index b7e162c8abcabf..51aa10806c129e 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -897,6 +897,9 @@ extern PyObject *_PyType_LookupRefAndVersion(PyTypeObject *, PyObject *, extern unsigned int _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef *out); +extern int _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, + PyObject *name, _PyStackRef *method); + // Cache the provided init method in the specialization cache of type if the // provided type version matches the current version of the type. // diff --git a/Objects/call.c b/Objects/call.c index b1610dababd466..551bcb71ecba46 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -834,12 +834,14 @@ PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, assert(PyVectorcall_NARGS(nargsf) >= 1); PyThreadState *tstate = _PyThreadState_GET(); - PyObject *callable = NULL; + _PyCStackRef method; + _PyThreadState_PushCStackRef(tstate, &method); /* Use args[0] as "self" argument */ - int unbound = _PyObject_GetMethod(args[0], name, &callable); - if (callable == NULL) { + int unbound = _PyObject_GetMethodStackRef(tstate, args[0], name, &method.ref); + if (PyStackRef_IsNull(method.ref)) { return NULL; } + PyObject *callable = PyStackRef_AsPyObjectBorrow(method.ref); if (unbound) { /* We must remove PY_VECTORCALL_ARGUMENTS_OFFSET since @@ -855,7 +857,7 @@ PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_METHOD, callable); PyObject *result = _PyObject_VectorcallTstate(tstate, callable, args, nargsf, kwnames); - Py_DECREF(callable); + _PyThreadState_PopCStackRef(tstate, &method); return result; } diff --git a/Objects/object.c b/Objects/object.c index 723b0427e69251..96d5bbd13aef4b 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1664,6 +1664,118 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) return 0; } +int +_PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, + PyObject *name, _PyStackRef *method) +{ + int meth_found = 0; + + int ret = 0; + *method = PyStackRef_NULL; + + PyTypeObject *tp = Py_TYPE(obj); + if (!_PyType_IsReady(tp)) { + if (PyType_Ready(tp) < 0) { + return 0; + } + } + + if (tp->tp_getattro != PyObject_GenericGetAttr || !PyUnicode_CheckExact(name)) { + PyObject *res = PyObject_GetAttr(obj, name); + if (res != NULL) { + *method = PyStackRef_FromPyObjectSteal(res); + } + return 0; + } + + _PyType_LookupStackRefAndVersion(tp, name, method); + PyObject *descr = PyStackRef_AsPyObjectBorrow(*method); + descrgetfunc f = NULL; + if (descr != NULL) { + if (_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) { + meth_found = 1; + } + else { + f = Py_TYPE(descr)->tp_descr_get; + if (f != NULL && PyDescr_IsData(descr)) { + PyObject *value = f(descr, obj, (PyObject *)Py_TYPE(obj)); + PyStackRef_CLEAR(*method); + if (value != NULL) { + *method = PyStackRef_FromPyObjectSteal(value); + } + goto exit; + } + } + } + PyObject *dict, *attr; + if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && + _PyObject_TryGetInstanceAttribute(obj, name, &attr)) { + if (attr != NULL) { + PyStackRef_CLEAR(*method); + *method = PyStackRef_FromPyObjectSteal(attr); + goto exit; + } + dict = NULL; + } + else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + dict = (PyObject *)_PyObject_GetManagedDict(obj); + } + else { + PyObject **dictptr = _PyObject_ComputedDictPointer(obj); + if (dictptr != NULL) { + dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*dictptr); + } + else { + dict = NULL; + } + } + if (dict != NULL) { + Py_INCREF(dict); + PyObject *value; + if (PyDict_GetItemRef(dict, name, &value) != 0) { + // found or error + Py_DECREF(dict); + PyStackRef_CLEAR(*method); + if (value != NULL) { + *method = PyStackRef_FromPyObjectSteal(value); + } + goto exit; + } + // not found + Py_DECREF(dict); + } + + if (meth_found) { + ret = 1; + assert(!PyStackRef_IsNull(*method)); + goto exit; + } + + if (f != NULL) { + PyObject *value = f(descr, obj, (PyObject *)Py_TYPE(obj)); + PyStackRef_CLEAR(*method); + if (value) { + *method = PyStackRef_FromPyObjectSteal(value); + } + goto exit; + } + + if (descr != NULL) { + assert(!PyStackRef_IsNull(*method)); + goto exit; + } + + PyErr_Format(PyExc_AttributeError, + "'%.100s' object has no attribute '%U'", + tp->tp_name, name); + + _PyObject_SetAttributeErrorContext(obj, name); + PyStackRef_CLEAR(*method); +exit: + return ret; +} + + /* Generic GetAttr functions - put these in your tp_[gs]etattro slot. */ PyObject * From b09f23f4af9f5177436fc4010346695088d43908 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 15 May 2025 16:52:30 +0530 Subject: [PATCH 2/7] fix assert --- Objects/object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/object.c b/Objects/object.c index 96d5bbd13aef4b..443bb0af947c57 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1671,7 +1671,7 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, int meth_found = 0; int ret = 0; - *method = PyStackRef_NULL; + assert(PyStackRef_IsNull(*method)); PyTypeObject *tp = Py_TYPE(obj); if (!_PyType_IsReady(tp)) { From 40da794baac3c8cace67c215ea285308074da59e Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 15 May 2025 17:01:13 +0530 Subject: [PATCH 3/7] add todo --- Objects/object.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/object.c b/Objects/object.c index 443bb0af947c57..be062d2b6b7ea2 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1730,6 +1730,7 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, } } if (dict != NULL) { + // TODO: use _Py_dict_lookup_threadsafe_stackref Py_INCREF(dict); PyObject *value; if (PyDict_GetItemRef(dict, name, &value) != 0) { From de82e900a8aee093967168c67b332a68dec9ab6e Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 15 May 2025 17:35:58 +0530 Subject: [PATCH 4/7] pop stackref when error --- Objects/call.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/call.c b/Objects/call.c index 551bcb71ecba46..0f0dd0a309f87a 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -839,6 +839,7 @@ PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, /* Use args[0] as "self" argument */ int unbound = _PyObject_GetMethodStackRef(tstate, args[0], name, &method.ref); if (PyStackRef_IsNull(method.ref)) { + _PyThreadState_PopCStackRef(tstate, &method); return NULL; } PyObject *callable = PyStackRef_AsPyObjectBorrow(method.ref); From f9d2f2842c3e68de4feb7d94eb723cf1f667f532 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 23 May 2025 16:29:09 +0530 Subject: [PATCH 5/7] cleanup --- Objects/call.c | 24 +++++++++++++++--------- Objects/object.c | 19 ++++++++----------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Objects/call.c b/Objects/call.c index 0f0dd0a309f87a..c9a18bcc3da60b 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -871,11 +871,14 @@ PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...) return null_error(tstate); } - PyObject *callable = NULL; - int is_method = _PyObject_GetMethod(obj, name, &callable); - if (callable == NULL) { + _PyCStackRef method; + _PyThreadState_PushCStackRef(tstate, &method); + int is_method = _PyObject_GetMethodStackRef(tstate, obj, name, &method.ref); + if (PyStackRef_IsNull(method.ref)) { + _PyThreadState_PopCStackRef(tstate, &method); return NULL; } + PyObject *callable = PyStackRef_AsPyObjectBorrow(method.ref); obj = is_method ? obj : NULL; va_list vargs; @@ -883,7 +886,7 @@ PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...) PyObject *result = object_vacall(tstate, obj, callable, vargs); va_end(vargs); - Py_DECREF(callable); + _PyThreadState_PopCStackRef(tstate, &method); return result; } @@ -900,12 +903,15 @@ _PyObject_CallMethodIdObjArgs(PyObject *obj, _Py_Identifier *name, ...) if (!oname) { return NULL; } - - PyObject *callable = NULL; - int is_method = _PyObject_GetMethod(obj, oname, &callable); - if (callable == NULL) { + _PyCStackRef method; + _PyThreadState_PushCStackRef(tstate, &method); + int is_method = _PyObject_GetMethodStackRef(tstate, obj, oname, &method.ref); + if (PyStackRef_IsNull(method.ref)) { + _PyThreadState_PopCStackRef(tstate, &method); return NULL; } + PyObject *callable = PyStackRef_AsPyObjectBorrow(method.ref); + obj = is_method ? obj : NULL; va_list vargs; @@ -913,7 +919,7 @@ _PyObject_CallMethodIdObjArgs(PyObject *obj, _Py_Identifier *name, ...) PyObject *result = object_vacall(tstate, obj, callable, vargs); va_end(vargs); - Py_DECREF(callable); + _PyThreadState_PopCStackRef(tstate, &method); return result; } diff --git a/Objects/object.c b/Objects/object.c index 342c83986aa0d9..68c8bfeae33e33 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1670,7 +1670,6 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, { int meth_found = 0; - int ret = 0; assert(PyStackRef_IsNull(*method)); PyTypeObject *tp = Py_TYPE(obj); @@ -1703,7 +1702,7 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, if (value != NULL) { *method = PyStackRef_FromPyObjectSteal(value); } - goto exit; + return 0; } } } @@ -1713,7 +1712,7 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, if (attr != NULL) { PyStackRef_CLEAR(*method); *method = PyStackRef_FromPyObjectSteal(attr); - goto exit; + return 0; } dict = NULL; } @@ -1740,16 +1739,15 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, if (value != NULL) { *method = PyStackRef_FromPyObjectSteal(value); } - goto exit; + return 0; } // not found Py_DECREF(dict); } if (meth_found) { - ret = 1; assert(!PyStackRef_IsNull(*method)); - goto exit; + return 1; } if (f != NULL) { @@ -1758,12 +1756,12 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, if (value) { *method = PyStackRef_FromPyObjectSteal(value); } - goto exit; + return 0; } if (descr != NULL) { assert(!PyStackRef_IsNull(*method)); - goto exit; + return 0; } PyErr_Format(PyExc_AttributeError, @@ -1771,9 +1769,8 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, tp->tp_name, name); _PyObject_SetAttributeErrorContext(obj, name); - PyStackRef_CLEAR(*method); -exit: - return ret; + assert(PyStackRef_IsNull(*method)); + return 0; } From 1d0a0b904837e5d30cb651ed357062a9f068561d Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 23 May 2025 17:05:27 +0530 Subject: [PATCH 6/7] add method_caller benchmark to ftscalingbench.py --- Tools/ftscalingbench/ftscalingbench.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Tools/ftscalingbench/ftscalingbench.py b/Tools/ftscalingbench/ftscalingbench.py index 926bc66b944c6f..1a59e25189d5dd 100644 --- a/Tools/ftscalingbench/ftscalingbench.py +++ b/Tools/ftscalingbench/ftscalingbench.py @@ -27,6 +27,7 @@ import sys import threading import time +from operator import methodcaller # The iterations in individual benchmarks are scaled by this factor. WORK_SCALE = 100 @@ -188,6 +189,18 @@ def thread_local_read(): _ = tmp.x _ = tmp.x +class MyClass: + __slots__ = () + + def func(self): + pass + +@register_benchmark +def method_caller(): + mc = methodcaller("func") + obj = MyClass() + for i in range(1000 * WORK_SCALE): + mc(obj) def bench_one_thread(func): t0 = time.perf_counter_ns() From 2b3c56524421ea95ab006a71af61850c59541c02 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 23 May 2025 17:06:37 +0530 Subject: [PATCH 7/7] fmt --- Include/internal/pycore_object.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 51aa10806c129e..3aaee7d008155a 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -898,7 +898,7 @@ extern unsigned int _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef *out); extern int _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, - PyObject *name, _PyStackRef *method); + PyObject *name, _PyStackRef *method); // Cache the provided init method in the specialization cache of type if the // provided type version matches the current version of the type. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy