Skip to content

Commit 12c5838

Browse files
committed
Fix PyCFunction_Call() performance issue
Issue #29259, #29465: PyCFunction_Call() doesn't create anymore a redundant tuple to pass positional arguments for METH_VARARGS. Add a new cfunction_call() subfunction.
1 parent 3722f1f commit 12c5838

File tree

1 file changed

+50
-4
lines changed

1 file changed

+50
-4
lines changed

Diff for: Objects/methodobject.c

+50-4
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,59 @@ PyCFunction_GetFlags(PyObject *op)
7777
return PyCFunction_GET_FLAGS(op);
7878
}
7979

80+
static PyObject *
81+
cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs)
82+
{
83+
assert(!PyErr_Occurred());
84+
85+
PyCFunction meth = PyCFunction_GET_FUNCTION(func);
86+
PyObject *self = PyCFunction_GET_SELF(func);
87+
PyObject *result;
88+
89+
if (PyCFunction_GET_FLAGS(func) & METH_KEYWORDS) {
90+
if (Py_EnterRecursiveCall(" while calling a Python object")) {
91+
return NULL;
92+
}
93+
94+
result = (*(PyCFunctionWithKeywords)meth)(self, args, kwargs);
95+
96+
Py_LeaveRecursiveCall();
97+
}
98+
else {
99+
if (kwargs != NULL && PyDict_Size(kwargs) != 0) {
100+
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
101+
((PyCFunctionObject*)func)->m_ml->ml_name);
102+
return NULL;
103+
}
104+
105+
if (Py_EnterRecursiveCall(" while calling a Python object")) {
106+
return NULL;
107+
}
108+
109+
result = (*meth)(self, args);
110+
111+
Py_LeaveRecursiveCall();
112+
}
113+
114+
return _Py_CheckFunctionResult(func, result, NULL);
115+
}
116+
117+
80118
PyObject *
81119
PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwargs)
82120
{
83-
return _PyCFunction_FastCallDict(func,
84-
&PyTuple_GET_ITEM(args, 0),
85-
PyTuple_GET_SIZE(args),
86-
kwargs);
121+
/* first try METH_VARARGS to pass directly args tuple unchanged.
122+
_PyMethodDef_RawFastCallDict() creates a new temporary tuple
123+
for METH_VARARGS. */
124+
if (PyCFunction_GET_FLAGS(func) & METH_VARARGS) {
125+
return cfunction_call_varargs(func, args, kwargs);
126+
}
127+
else {
128+
return _PyCFunction_FastCallDict(func,
129+
&PyTuple_GET_ITEM(args, 0),
130+
PyTuple_GET_SIZE(args),
131+
kwargs);
132+
}
87133
}
88134

89135
PyObject *

0 commit comments

Comments
 (0)