Skip to content

Commit c525723

Browse files
committed
Optimize methoddescr_call(): avoid temporary PyCFunction
Issue #29259, #29263. methoddescr_call() creates a PyCFunction object, call it and the destroy it. Add a new _PyMethodDef_RawFastCallDict() method to avoid the temporary PyCFunction object.
1 parent 35ecebe commit c525723

File tree

3 files changed

+40
-26
lines changed

3 files changed

+40
-26
lines changed

Diff for: Include/methodobject.h

+7
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ typedef struct {
9595
PyObject *m_module; /* The __module__ attribute, can be anything */
9696
PyObject *m_weakreflist; /* List of weak references */
9797
} PyCFunctionObject;
98+
99+
PyAPI_FUNC(PyObject *) _PyMethodDef_RawFastCallDict(
100+
PyMethodDef *method,
101+
PyObject *self,
102+
PyObject **args,
103+
Py_ssize_t nargs,
104+
PyObject *kwargs);
98105
#endif
99106

100107
PyAPI_FUNC(int) PyCFunction_ClearFreeList(void);

Diff for: Objects/descrobject.c

+9-11
Original file line numberDiff line numberDiff line change
@@ -210,15 +210,15 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value)
210210
}
211211

212212
static PyObject *
213-
methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds)
213+
methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs)
214214
{
215-
Py_ssize_t argc;
216-
PyObject *self, *func, *result, **stack;
215+
Py_ssize_t nargs;
216+
PyObject *self, *result;
217217

218218
/* Make sure that the first argument is acceptable as 'self' */
219219
assert(PyTuple_Check(args));
220-
argc = PyTuple_GET_SIZE(args);
221-
if (argc < 1) {
220+
nargs = PyTuple_GET_SIZE(args);
221+
if (nargs < 1) {
222222
PyErr_Format(PyExc_TypeError,
223223
"descriptor '%V' of '%.100s' "
224224
"object needs an argument",
@@ -239,12 +239,10 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds)
239239
return NULL;
240240
}
241241

242-
func = PyCFunction_NewEx(descr->d_method, self, NULL);
243-
if (func == NULL)
244-
return NULL;
245-
stack = &PyTuple_GET_ITEM(args, 1);
246-
result = _PyObject_FastCallDict(func, stack, argc - 1, kwds);
247-
Py_DECREF(func);
242+
result = _PyMethodDef_RawFastCallDict(descr->d_method, self,
243+
&PyTuple_GET_ITEM(args, 1), nargs - 1,
244+
kwargs);
245+
result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL);
248246
return result;
249247
}
250248

Diff for: Objects/methodobject.c

+24-15
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,14 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds)
152152
}
153153

154154
PyObject *
155-
_PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
156-
PyObject *kwargs)
155+
_PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **args,
156+
Py_ssize_t nargs, PyObject *kwargs)
157157
{
158-
PyCFunctionObject *func;
159158
PyCFunction meth;
160-
PyObject *self;
161159
PyObject *result;
162160
int flags;
163161

164-
assert(func_obj != NULL);
165-
assert(PyCFunction_Check(func_obj));
162+
assert(method != NULL);
166163
assert(nargs >= 0);
167164
assert(nargs == 0 || args != NULL);
168165
assert(kwargs == NULL || PyDict_Check(kwargs));
@@ -172,10 +169,8 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
172169
caller loses its exception */
173170
assert(!PyErr_Occurred());
174171

175-
func = (PyCFunctionObject*)func_obj;
176-
meth = PyCFunction_GET_FUNCTION(func);
177-
self = PyCFunction_GET_SELF(func);
178-
flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
172+
meth = method->ml_meth;
173+
flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
179174

180175
switch (flags)
181176
{
@@ -186,7 +181,7 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
186181

187182
if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) {
188183
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
189-
func->m_ml->ml_name);
184+
method->ml_name);
190185
return NULL;
191186
}
192187

@@ -197,7 +192,7 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
197192
if (nargs != 1) {
198193
PyErr_Format(PyExc_TypeError,
199194
"%.200s() takes exactly one argument (%zd given)",
200-
func->m_ml->ml_name, nargs);
195+
method->ml_name, nargs);
201196
return NULL;
202197
}
203198

@@ -259,17 +254,31 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
259254
return NULL;
260255
}
261256

262-
result = _Py_CheckFunctionResult(func_obj, result, NULL);
263-
264257
return result;
265258

266259
no_keyword_error:
267260
PyErr_Format(PyExc_TypeError,
268261
"%.200s() takes no arguments (%zd given)",
269-
func->m_ml->ml_name, nargs);
262+
method->ml_name, nargs);
270263
return NULL;
271264
}
272265

266+
PyObject *
267+
_PyCFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
268+
PyObject *kwargs)
269+
{
270+
PyObject *result;
271+
272+
assert(func != NULL);
273+
assert(PyCFunction_Check(func));
274+
275+
result = _PyMethodDef_RawFastCallDict(((PyCFunctionObject*)func)->m_ml,
276+
PyCFunction_GET_SELF(func),
277+
args, nargs, kwargs);
278+
result = _Py_CheckFunctionResult(func, result, NULL);
279+
return result;
280+
}
281+
273282
PyObject *
274283
_PyCFunction_FastCallKeywords(PyObject *func_obj, PyObject **args,
275284
Py_ssize_t nargs, PyObject *kwnames)

0 commit comments

Comments
 (0)