diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2017-02-09 01:01:37 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2017-02-09 01:01:37 (GMT) |
commit | 12c5838dae390793009d73ba486b6b2be18cfd43 (patch) | |
tree | 382a22051a5ad49eafbc310149554c61d78b9620 /Objects/methodobject.c | |
parent | 3722f1f483065338e83ec1e28cd8dc8412ef39d7 (diff) | |
download | cpython-12c5838dae390793009d73ba486b6b2be18cfd43.zip cpython-12c5838dae390793009d73ba486b6b2be18cfd43.tar.gz cpython-12c5838dae390793009d73ba486b6b2be18cfd43.tar.bz2 |
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.
Diffstat (limited to 'Objects/methodobject.c')
-rw-r--r-- | Objects/methodobject.c | 54 |
1 files changed, 50 insertions, 4 deletions
diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 0782777..d0fbefd 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -77,13 +77,59 @@ PyCFunction_GetFlags(PyObject *op) return PyCFunction_GET_FLAGS(op); } +static PyObject * +cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs) +{ + assert(!PyErr_Occurred()); + + PyCFunction meth = PyCFunction_GET_FUNCTION(func); + PyObject *self = PyCFunction_GET_SELF(func); + PyObject *result; + + if (PyCFunction_GET_FLAGS(func) & METH_KEYWORDS) { + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } + + result = (*(PyCFunctionWithKeywords)meth)(self, args, kwargs); + + Py_LeaveRecursiveCall(); + } + else { + if (kwargs != NULL && PyDict_Size(kwargs) != 0) { + PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", + ((PyCFunctionObject*)func)->m_ml->ml_name); + return NULL; + } + + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } + + result = (*meth)(self, args); + + Py_LeaveRecursiveCall(); + } + + return _Py_CheckFunctionResult(func, result, NULL); +} + + PyObject * PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwargs) { - return _PyCFunction_FastCallDict(func, - &PyTuple_GET_ITEM(args, 0), - PyTuple_GET_SIZE(args), - kwargs); + /* first try METH_VARARGS to pass directly args tuple unchanged. + _PyMethodDef_RawFastCallDict() creates a new temporary tuple + for METH_VARARGS. */ + if (PyCFunction_GET_FLAGS(func) & METH_VARARGS) { + return cfunction_call_varargs(func, args, kwargs); + } + else { + return _PyCFunction_FastCallDict(func, + &PyTuple_GET_ITEM(args, 0), + PyTuple_GET_SIZE(args), + kwargs); + } } PyObject * |