From 99c092a531abf57a8f5c37356dc6971cdfed497a Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Fri, 3 Feb 2017 07:43:03 +0900 Subject: Issue #29263: LOAD_METHOD support for C methods Calling builtin method is at most 10% faster. --- Include/descrobject.h | 3 +++ Include/methodobject.h | 7 +++++++ Lib/test/test_gdb.py | 2 +- Objects/descrobject.c | 38 ++++++++++++++++++++++++++++++++++++ Objects/methodobject.c | 53 ++++++++++++++++++++++++++++---------------------- Objects/object.c | 4 ++-- Python/ceval.c | 12 ++++++------ Tools/gdb/libpython.py | 5 +---- 8 files changed, 88 insertions(+), 36 deletions(-) diff --git a/Include/descrobject.h b/Include/descrobject.h index 013d64521f..cb43174838 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -90,6 +90,9 @@ PyAPI_FUNC(PyObject *) PyDescr_NewMember(PyTypeObject *, PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *, struct PyGetSetDef *); #ifndef Py_LIMITED_API + +PyAPI_FUNC(PyObject *) _PyMethodDescr_FastCallKeywords( + PyObject *descrobj, PyObject **stack, Py_ssize_t nargs, PyObject *kwnames); PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *, struct wrapperbase *, void *); #define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL) diff --git a/Include/methodobject.h b/Include/methodobject.h index 7370419c39..b5c4e83730 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -102,6 +102,13 @@ PyAPI_FUNC(PyObject *) _PyMethodDef_RawFastCallDict( PyObject **args, Py_ssize_t nargs, PyObject *kwargs); + +PyAPI_FUNC(PyObject *) _PyMethodDef_RawFastCallKeywords( + PyMethodDef *method, + PyObject *self, + PyObject **args, + Py_ssize_t nargs, + PyObject *kwnames); #endif PyAPI_FUNC(int) PyCFunction_ClearFreeList(void); diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index b7554d698c..46736f62d5 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -846,7 +846,7 @@ id(42) breakpoint='time_gmtime', cmds_after_breakpoint=['py-bt-full'], ) - self.assertIn('#1 tp_name); + return NULL; + } + self = args[0]; + if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), + (PyObject *)PyDescr_TYPE(descr))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%V' " + "requires a '%.100s' object " + "but received a '%.100s'", + descr_name((PyDescrObject *)descr), "?", + PyDescr_TYPE(descr)->tp_name, + self->ob_type->tp_name); + return NULL; + } + + result = _PyMethodDef_RawFastCallKeywords(descr->d_method, self, + args+1, nargs-1, kwnames); + result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL); + return result; +} + static PyObject * classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 35a827e164..6618d78968 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -215,32 +215,24 @@ _PyCFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, } PyObject * -_PyCFunction_FastCallKeywords(PyObject *func_obj, PyObject **args, - Py_ssize_t nargs, PyObject *kwnames) +_PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, PyObject **args, + Py_ssize_t nargs, PyObject *kwnames) { - PyCFunctionObject *func; - PyCFunction meth; - PyObject *self, *result; - Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); - int flags; + /* _PyMethodDef_RawFastCallKeywords() must not be called with an exception set, + because it can clear it (directly or indirectly) and so the + caller loses its exception */ + assert(!PyErr_Occurred()); - assert(func_obj != NULL); - assert(PyCFunction_Check(func_obj)); + assert(method != NULL); assert(nargs >= 0); assert(kwnames == NULL || PyTuple_CheckExact(kwnames)); - assert((nargs == 0 && nkwargs == 0) || args != NULL); /* kwnames must only contains str strings, no subclass, and all keys must be unique */ - /* _PyCFunction_FastCallKeywords() must not be called with an exception - set, because it can clear it (directly or indirectly) and so the caller - loses its exception */ - assert(!PyErr_Occurred()); - - func = (PyCFunctionObject*)func_obj; - meth = PyCFunction_GET_FUNCTION(func); - self = PyCFunction_GET_SELF(func); - flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + PyCFunction meth = method->ml_meth; + int flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + Py_ssize_t nkwargs = kwnames == NULL ? 0 : PyTuple_Size(kwnames); + PyObject *result; switch (flags) { @@ -248,7 +240,7 @@ _PyCFunction_FastCallKeywords(PyObject *func_obj, PyObject **args, if (nargs != 0) { PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments (%zd given)", - func->m_ml->ml_name, nargs); + method->ml_name, nargs); return NULL; } @@ -263,7 +255,7 @@ _PyCFunction_FastCallKeywords(PyObject *func_obj, PyObject **args, if (nargs != 1) { PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%zd given)", - func->m_ml->ml_name, nargs); + method->ml_name, nargs); return NULL; } @@ -326,16 +318,31 @@ _PyCFunction_FastCallKeywords(PyObject *func_obj, PyObject **args, return NULL; } - result = _Py_CheckFunctionResult(func_obj, result, NULL); return result; no_keyword_error: PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - func->m_ml->ml_name); + method->ml_name); return NULL; } +PyObject * +_PyCFunction_FastCallKeywords(PyObject *func, PyObject **args, + Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *result; + + assert(func != NULL); + assert(PyCFunction_Check(func)); + + result = _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->m_ml, + PyCFunction_GET_SELF(func), + args, nargs, kwnames); + result = _Py_CheckFunctionResult(func, result, NULL); + return result; +} + /* Methods (the standard built-in methods, that is) */ static void diff --git a/Objects/object.c b/Objects/object.c index 93cdc1014f..5da6cffdb9 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1060,8 +1060,8 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) descr = _PyType_Lookup(tp, name); if (descr != NULL) { Py_INCREF(descr); - if (PyFunction_Check(descr)) { - /* A python method. */ + if (PyFunction_Check(descr) || + (Py_TYPE(descr) == &PyMethodDescr_Type)) { meth_found = 1; } else { f = descr->ob_type->tp_descr_get; diff --git a/Python/ceval.c b/Python/ceval.c index 298ad55b81..58007793bb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4832,17 +4832,19 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) PyObject *x, *w; Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); Py_ssize_t nargs = oparg - nkwargs; - PyObject **stack; + PyObject **stack = (*pp_stack) - nargs - nkwargs; /* Always dispatch PyCFunction first, because these are presumed to be the most frequent callable object. */ if (PyCFunction_Check(func)) { PyThreadState *tstate = PyThreadState_GET(); - - stack = (*pp_stack) - nargs - nkwargs; C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames)); } + else if (Py_TYPE(func) == &PyMethodDescr_Type) { + PyThreadState *tstate = PyThreadState_GET(); + C_TRACE(x, _PyMethodDescr_FastCallKeywords(func, stack, nargs, kwnames)); + } else { if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { /* Optimize access to bound methods. Reuse the Python stack @@ -4856,20 +4858,18 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) Py_INCREF(func); Py_SETREF(*pfunc, self); nargs++; + stack--; } else { Py_INCREF(func); } - stack = (*pp_stack) - nargs - nkwargs; - if (PyFunction_Check(func)) { x = fast_function(func, stack, nargs, kwnames); } else { x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames); } - Py_DECREF(func); } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index cca9741ced..4f45256188 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1540,10 +1540,7 @@ class Frame(object): if caller in ('_PyCFunction_FastCallDict', '_PyCFunction_FastCallKeywords'): - if caller == '_PyCFunction_FastCallKeywords': - arg_name = 'func_obj' - else: - arg_name = 'func' + arg_name = 'func' # Within that frame: # "func" is the local containing the PyObject* of the # PyCFunctionObject instance -- cgit v1.2.1