bpo-44850: Improve the performance of methodcaller. by anntzer · Pull Request #27782 · python/cpython
OK, I gave it a try, but I wasn't able to make it work :( If you want to give it a try, the patch is below; the printf() shows that methodcaller_vectorcall is never reached.
diff --git i/Modules/_operator.c w/Modules/_operator.c index de4613a6a8..cee6df24fe 100644 --- i/Modules/_operator.c +++ w/Modules/_operator.c @@ -1,5 +1,6 @@ #include "Python.h" #include "pycore_moduleobject.h" // _PyModule_GetState() +#include "structmember.h" #include "clinic/_operator.c.h" typedef struct { @@ -1480,8 +1481,44 @@ typedef struct { PyObject *kwds; PyObject **vectorcall_args; /* Borrowed references */ PyObject *vectorcall_kwnames; + vectorcallfunc vectorcall; } methodcallerobject; +static PyObject * +methodcaller_vectorcall( + methodcallerobject *mc, PyObject *const *args, size_t nargsf, PyObject* kwnames) +{ + printf("vectorcall\n"); + if (!_PyArg_NoKwnames("methodcaller", kwnames)) + return NULL; + if (PyVectorcall_NARGS(nargsf) != 1) { + PyErr_Format( + PyExc_TypeError, + "methodcaller expected 1 argument, got %zd", + PyVectorcall_NARGS(nargsf)); + return NULL; + } + mc->vectorcall_args[0] = args[0]; + return PyObject_VectorcallMethod( + mc->name, mc->vectorcall_args, + (1 + PyTuple_GET_SIZE(mc->args)) | PY_VECTORCALL_ARGUMENTS_OFFSET, + mc->vectorcall_kwnames); +} + +static PyObject * +methodcaller_call(methodcallerobject *mc, PyObject *args, PyObject *kw) +{ + if (!_PyArg_NoKeywords("methodcaller", kw)) + return NULL; + if (!_PyArg_CheckPositional("methodcaller", PyTuple_GET_SIZE(args), 1, 1)) + return NULL; + mc->vectorcall_args[0] = PyTuple_GET_ITEM(args, 0); + return PyObject_VectorcallMethod( + mc->name, mc->vectorcall_args, + (1 + PyTuple_GET_SIZE(mc->args)) | PY_VECTORCALL_ARGUMENTS_OFFSET, + mc->vectorcall_kwnames); +} + /* AC 3.5: variable number of arguments, not currently support by AC */ static PyObject * methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds) @@ -1548,6 +1585,7 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds) else { mc->vectorcall_kwnames = NULL; } + mc->vectorcall = (vectorcallfunc)methodcaller_vectorcall; PyObject_GC_Track(mc); return (PyObject *)mc; @@ -1584,20 +1622,6 @@ methodcaller_traverse(methodcallerobject *mc, visitproc visit, void *arg) return 0; } -static PyObject * -methodcaller_call(methodcallerobject *mc, PyObject *args, PyObject *kw) -{ - if (!_PyArg_NoKeywords("methodcaller", kw)) - return NULL; - if (!_PyArg_CheckPositional("methodcaller", PyTuple_GET_SIZE(args), 1, 1)) - return NULL; - mc->vectorcall_args[0] = PyTuple_GET_ITEM(args, 0); - return PyObject_VectorcallMethod( - mc->name, mc->vectorcall_args, - (1 + PyTuple_GET_SIZE(mc->args)) | PY_VECTORCALL_ARGUMENTS_OFFSET, - mc->vectorcall_kwnames); -} - static PyObject * methodcaller_repr(methodcallerobject *mc) { @@ -1722,6 +1746,12 @@ static PyMethodDef methodcaller_methods[] = { reduce_doc}, {NULL} }; + +static PyMemberDef methodcaller_members[] = { + {"__vectorcalloffset__", T_PYSSIZET, offsetof(methodcallerobject, vectorcall), READONLY}, + {NULL} +}; + PyDoc_STRVAR(methodcaller_doc, "methodcaller(name, ...) --> methodcaller object\n\ \n\ @@ -1737,6 +1767,7 @@ static PyType_Slot methodcaller_type_slots[] = { {Py_tp_traverse, methodcaller_traverse}, {Py_tp_clear, methodcaller_clear}, {Py_tp_methods, methodcaller_methods}, + {Py_tp_members, methodcaller_members}, {Py_tp_new, methodcaller_new}, {Py_tp_getattro, PyObject_GenericGetAttr}, {Py_tp_repr, methodcaller_repr},