mirror of https://github.com/python/cpython
Add _PyObject_FastCallKeywords()
Issue #27830: Add _PyObject_FastCallKeywords(): avoid the creation of a temporary dictionary for keyword arguments. Other changes: * Cleanup call_function() and fast_function() (ex: rename nk to nkwargs) * Remove now useless do_call(), replaced with _PyObject_FastCallKeywords()
This commit is contained in:
parent
84f6a8f725
commit
d873572095
|
@ -271,8 +271,8 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
|
||||||
Py_ssize_t nargs);
|
Py_ssize_t nargs);
|
||||||
|
|
||||||
/* Call the callable object func with the "fast call" calling convention:
|
/* Call the callable object func with the "fast call" calling convention:
|
||||||
args is a C array for positional parameters (nargs is the number of
|
args is a C array for positional arguments (nargs is the number of
|
||||||
positional paramater), kwargs is a dictionary for keyword parameters.
|
positional arguments), kwargs is a dictionary for keyword arguments.
|
||||||
|
|
||||||
If nargs is equal to zero, args can be NULL. kwargs can be NULL.
|
If nargs is equal to zero, args can be NULL. kwargs can be NULL.
|
||||||
nargs must be greater or equal to zero.
|
nargs must be greater or equal to zero.
|
||||||
|
@ -283,6 +283,24 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
|
||||||
PyObject **args, Py_ssize_t nargs,
|
PyObject **args, Py_ssize_t nargs,
|
||||||
PyObject *kwargs);
|
PyObject *kwargs);
|
||||||
|
|
||||||
|
/* Call the callable object func with the "fast call" calling convention:
|
||||||
|
args is a C array for positional arguments followed by values of
|
||||||
|
keyword arguments. Keys of keyword arguments are stored as a tuple
|
||||||
|
of strings in kwnames. nargs is the number of positional parameters at
|
||||||
|
the beginning of stack. The size of kwnames gives the number of keyword
|
||||||
|
values in the stack after positional arguments.
|
||||||
|
|
||||||
|
If nargs is equal to zero and there is no keyword argument (kwnames is
|
||||||
|
NULL or its size is zero), args can be NULL.
|
||||||
|
|
||||||
|
Return the result on success. Raise an exception and return NULL on
|
||||||
|
error. */
|
||||||
|
PyAPI_FUNC(PyObject *) _PyObject_FastCallKeywords
|
||||||
|
(PyObject *func,
|
||||||
|
PyObject **args,
|
||||||
|
Py_ssize_t nargs,
|
||||||
|
PyObject *kwnames);
|
||||||
|
|
||||||
#define _PyObject_FastCall(func, args, nargs) \
|
#define _PyObject_FastCall(func, args, nargs) \
|
||||||
_PyObject_FastCallDict((func), (args), (nargs), NULL)
|
_PyObject_FastCallDict((func), (args), (nargs), NULL)
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,12 @@ PyAPI_FUNC(PyObject *) _PyFunction_FastCallDict(
|
||||||
PyObject **args,
|
PyObject **args,
|
||||||
Py_ssize_t nargs,
|
Py_ssize_t nargs,
|
||||||
PyObject *kwargs);
|
PyObject *kwargs);
|
||||||
|
|
||||||
|
PyAPI_FUNC(PyObject *) _PyFunction_FastCallKeywords(
|
||||||
|
PyObject *func,
|
||||||
|
PyObject **stack,
|
||||||
|
Py_ssize_t nargs,
|
||||||
|
PyObject *kwnames);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Macros for direct access to these values. Type checks are *not*
|
/* Macros for direct access to these values. Type checks are *not*
|
||||||
|
|
|
@ -2366,6 +2366,74 @@ _PyObject_Call_Prepend(PyObject *func,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_PyStack_AsDict(PyObject **values, Py_ssize_t nkwargs, PyObject *kwnames,
|
||||||
|
PyObject *func)
|
||||||
|
{
|
||||||
|
PyObject *kwdict;
|
||||||
|
Py_ssize_t i;
|
||||||
|
|
||||||
|
kwdict = PyDict_New();
|
||||||
|
if (kwdict == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i < nkwargs; i++) {
|
||||||
|
int err;
|
||||||
|
PyObject *key = PyTuple_GET_ITEM(kwnames, i);
|
||||||
|
PyObject *value = *values++;
|
||||||
|
|
||||||
|
if (PyDict_GetItem(kwdict, key) != NULL) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"%.200s%s got multiple values "
|
||||||
|
"for keyword argument '%U'",
|
||||||
|
PyEval_GetFuncName(func),
|
||||||
|
PyEval_GetFuncDesc(func),
|
||||||
|
key);
|
||||||
|
Py_DECREF(kwdict);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = PyDict_SetItem(kwdict, key, value);
|
||||||
|
if (err) {
|
||||||
|
Py_DECREF(kwdict);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kwdict;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
_PyObject_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs,
|
||||||
|
PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *kwdict, *result;
|
||||||
|
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
|
||||||
|
|
||||||
|
assert(nargs >= 0);
|
||||||
|
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
|
||||||
|
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
|
||||||
|
|
||||||
|
if (PyFunction_Check(func)) {
|
||||||
|
/* Fast-path: avoid temporary tuple or dict */
|
||||||
|
return _PyFunction_FastCallKeywords(func, stack, nargs, kwnames);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nkwargs > 0) {
|
||||||
|
kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func);
|
||||||
|
if (kwdict == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
kwdict = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _PyObject_FastCallDict(func, stack, nargs, kwdict);
|
||||||
|
Py_XDECREF(kwdict);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
call_function_tail(PyObject *callable, PyObject *args)
|
call_function_tail(PyObject *callable, PyObject *args)
|
||||||
{
|
{
|
||||||
|
|
|
@ -113,8 +113,7 @@ static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *, uint64*, u
|
||||||
#else
|
#else
|
||||||
static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *);
|
static PyObject * call_function(PyObject ***, Py_ssize_t, PyObject *);
|
||||||
#endif
|
#endif
|
||||||
static PyObject * fast_function(PyObject *, PyObject ***, Py_ssize_t, PyObject *);
|
static PyObject * fast_function(PyObject *, PyObject **, Py_ssize_t, PyObject *);
|
||||||
static PyObject * do_call(PyObject *, PyObject ***, Py_ssize_t, PyObject *);
|
|
||||||
static PyObject * do_call_core(PyObject *, PyObject *, PyObject *);
|
static PyObject * do_call_core(PyObject *, PyObject *, PyObject *);
|
||||||
static PyObject * create_keyword_args(PyObject *, PyObject ***, PyObject *);
|
static PyObject * create_keyword_args(PyObject *, PyObject ***, PyObject *);
|
||||||
static PyObject * load_args(PyObject ***, Py_ssize_t);
|
static PyObject * load_args(PyObject ***, Py_ssize_t);
|
||||||
|
@ -4940,7 +4939,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names
|
call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames
|
||||||
#ifdef WITH_TSC
|
#ifdef WITH_TSC
|
||||||
, uint64* pintr0, uint64* pintr1
|
, uint64* pintr0, uint64* pintr1
|
||||||
#endif
|
#endif
|
||||||
|
@ -4949,8 +4948,8 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names
|
||||||
PyObject **pfunc = (*pp_stack) - oparg - 1;
|
PyObject **pfunc = (*pp_stack) - oparg - 1;
|
||||||
PyObject *func = *pfunc;
|
PyObject *func = *pfunc;
|
||||||
PyObject *x, *w;
|
PyObject *x, *w;
|
||||||
Py_ssize_t nk = names == NULL ? 0 : PyTuple_GET_SIZE(names);
|
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
|
||||||
Py_ssize_t nargs = oparg - nk;
|
Py_ssize_t nargs = oparg - nkwargs;
|
||||||
|
|
||||||
/* Always dispatch PyCFunction first, because these are
|
/* Always dispatch PyCFunction first, because these are
|
||||||
presumed to be the most frequent callable object.
|
presumed to be the most frequent callable object.
|
||||||
|
@ -4960,7 +4959,7 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names
|
||||||
PyThreadState *tstate = PyThreadState_GET();
|
PyThreadState *tstate = PyThreadState_GET();
|
||||||
|
|
||||||
PCALL(PCALL_CFUNCTION);
|
PCALL(PCALL_CFUNCTION);
|
||||||
if (names == NULL && flags & (METH_NOARGS | METH_O)) {
|
if (kwnames == NULL && flags & (METH_NOARGS | METH_O)) {
|
||||||
PyCFunction meth = PyCFunction_GET_FUNCTION(func);
|
PyCFunction meth = PyCFunction_GET_FUNCTION(func);
|
||||||
PyObject *self = PyCFunction_GET_SELF(func);
|
PyObject *self = PyCFunction_GET_SELF(func);
|
||||||
if (flags & METH_NOARGS && nargs == 0) {
|
if (flags & METH_NOARGS && nargs == 0) {
|
||||||
|
@ -4982,8 +4981,8 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyObject *callargs, *kwdict = NULL;
|
PyObject *callargs, *kwdict = NULL;
|
||||||
if (names != NULL) {
|
if (kwnames != NULL) {
|
||||||
kwdict = create_keyword_args(names, pp_stack, func);
|
kwdict = create_keyword_args(kwnames, pp_stack, func);
|
||||||
if (kwdict == NULL) {
|
if (kwdict == NULL) {
|
||||||
x = NULL;
|
x = NULL;
|
||||||
goto cfuncerror;
|
goto cfuncerror;
|
||||||
|
@ -5003,6 +5002,9 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
|
||||||
|
PyObject **stack;
|
||||||
|
|
||||||
if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
|
if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
|
||||||
/* optimize access to bound methods */
|
/* optimize access to bound methods */
|
||||||
PyObject *self = PyMethod_GET_SELF(func);
|
PyObject *self = PyMethod_GET_SELF(func);
|
||||||
|
@ -5018,11 +5020,14 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *names
|
||||||
Py_INCREF(func);
|
Py_INCREF(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stack = (*pp_stack) - nargs - nkwargs;
|
||||||
|
|
||||||
READ_TIMESTAMP(*pintr0);
|
READ_TIMESTAMP(*pintr0);
|
||||||
if (PyFunction_Check(func)) {
|
if (PyFunction_Check(func)) {
|
||||||
x = fast_function(func, pp_stack, nargs, names);
|
x = fast_function(func, stack, nargs, kwnames);
|
||||||
} else {
|
}
|
||||||
x = do_call(func, pp_stack, nargs, names);
|
else {
|
||||||
|
x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
|
||||||
}
|
}
|
||||||
READ_TIMESTAMP(*pintr1);
|
READ_TIMESTAMP(*pintr1);
|
||||||
|
|
||||||
|
@ -5055,7 +5060,7 @@ cfuncerror:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t nargs,
|
_PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs,
|
||||||
PyObject *globals)
|
PyObject *globals)
|
||||||
{
|
{
|
||||||
PyFrameObject *f;
|
PyFrameObject *f;
|
||||||
|
@ -5091,19 +5096,19 @@ _PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t nargs,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Similar to _PyFunction_FastCall() but keywords are passed a (key, value)
|
|
||||||
pairs in stack */
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
fast_function(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject *names)
|
fast_function(PyObject *func, PyObject **stack,
|
||||||
|
Py_ssize_t nargs, PyObject *kwnames)
|
||||||
{
|
{
|
||||||
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
|
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
|
||||||
PyObject *globals = PyFunction_GET_GLOBALS(func);
|
PyObject *globals = PyFunction_GET_GLOBALS(func);
|
||||||
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
|
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
|
||||||
PyObject *kwdefs, *closure, *name, *qualname;
|
PyObject *kwdefs, *closure, *name, *qualname;
|
||||||
PyObject **d;
|
PyObject **d;
|
||||||
Py_ssize_t nkwargs = names == NULL ? 0 : PyTuple_GET_SIZE(names);
|
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
|
||||||
Py_ssize_t nd;
|
Py_ssize_t nd;
|
||||||
PyObject **stack = (*pp_stack)-nargs-nkwargs;
|
|
||||||
|
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
|
||||||
|
|
||||||
PCALL(PCALL_FUNCTION);
|
PCALL(PCALL_FUNCTION);
|
||||||
PCALL(PCALL_FAST_FUNCTION);
|
PCALL(PCALL_FAST_FUNCTION);
|
||||||
|
@ -5112,15 +5117,14 @@ fast_function(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject *
|
||||||
co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
|
co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
|
||||||
{
|
{
|
||||||
if (argdefs == NULL && co->co_argcount == nargs) {
|
if (argdefs == NULL && co->co_argcount == nargs) {
|
||||||
return _PyFunction_FastCallNoKw(co, stack, nargs, globals);
|
return _PyFunction_FastCall(co, stack, nargs, globals);
|
||||||
}
|
}
|
||||||
else if (nargs == 0 && argdefs != NULL
|
else if (nargs == 0 && argdefs != NULL
|
||||||
&& co->co_argcount == Py_SIZE(argdefs)) {
|
&& co->co_argcount == Py_SIZE(argdefs)) {
|
||||||
/* function called with no arguments, but all parameters have
|
/* function called with no arguments, but all parameters have
|
||||||
a default value: use default values as arguments .*/
|
a default value: use default values as arguments .*/
|
||||||
stack = &PyTuple_GET_ITEM(argdefs, 0);
|
stack = &PyTuple_GET_ITEM(argdefs, 0);
|
||||||
return _PyFunction_FastCallNoKw(co, stack, Py_SIZE(argdefs),
|
return _PyFunction_FastCall(co, stack, Py_SIZE(argdefs), globals);
|
||||||
globals);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5140,11 +5144,18 @@ fast_function(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject *
|
||||||
return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
|
return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
|
||||||
stack, nargs,
|
stack, nargs,
|
||||||
NULL, 0,
|
NULL, 0,
|
||||||
names, stack + nargs,
|
kwnames, stack + nargs,
|
||||||
d, (int)nd, kwdefs,
|
d, (int)nd, kwdefs,
|
||||||
closure, name, qualname);
|
closure, name, qualname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
_PyFunction_FastCallKeywords(PyObject *func, PyObject **stack,
|
||||||
|
Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
return fast_function(func, stack, nargs, kwnames);
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
|
_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
|
||||||
PyObject *kwargs)
|
PyObject *kwargs)
|
||||||
|
@ -5172,15 +5183,14 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
|
||||||
{
|
{
|
||||||
/* Fast paths */
|
/* Fast paths */
|
||||||
if (argdefs == NULL && co->co_argcount == nargs) {
|
if (argdefs == NULL && co->co_argcount == nargs) {
|
||||||
return _PyFunction_FastCallNoKw(co, args, nargs, globals);
|
return _PyFunction_FastCall(co, args, nargs, globals);
|
||||||
}
|
}
|
||||||
else if (nargs == 0 && argdefs != NULL
|
else if (nargs == 0 && argdefs != NULL
|
||||||
&& co->co_argcount == Py_SIZE(argdefs)) {
|
&& co->co_argcount == Py_SIZE(argdefs)) {
|
||||||
/* function called with no arguments, but all parameters have
|
/* function called with no arguments, but all parameters have
|
||||||
a default value: use default values as arguments .*/
|
a default value: use default values as arguments .*/
|
||||||
args = &PyTuple_GET_ITEM(argdefs, 0);
|
args = &PyTuple_GET_ITEM(argdefs, 0);
|
||||||
return _PyFunction_FastCallNoKw(co, args, Py_SIZE(argdefs),
|
return _PyFunction_FastCall(co, args, Py_SIZE(argdefs), globals);
|
||||||
globals);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5242,8 +5252,8 @@ create_keyword_args(PyObject *names, PyObject ***pp_stack,
|
||||||
return NULL;
|
return NULL;
|
||||||
while (--nk >= 0) {
|
while (--nk >= 0) {
|
||||||
int err;
|
int err;
|
||||||
PyObject *value = EXT_POP(*pp_stack);
|
|
||||||
PyObject *key = PyTuple_GET_ITEM(names, nk);
|
PyObject *key = PyTuple_GET_ITEM(names, nk);
|
||||||
|
PyObject *value = EXT_POP(*pp_stack);
|
||||||
if (PyDict_GetItem(kwdict, key) != NULL) {
|
if (PyDict_GetItem(kwdict, key) != NULL) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"%.200s%s got multiple values "
|
"%.200s%s got multiple values "
|
||||||
|
@ -5281,33 +5291,6 @@ load_args(PyObject ***pp_stack, Py_ssize_t nargs)
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
do_call(PyObject *func, PyObject ***pp_stack, Py_ssize_t nargs, PyObject *kwnames)
|
|
||||||
{
|
|
||||||
PyObject *callargs, *kwdict, *result;
|
|
||||||
|
|
||||||
if (kwnames != NULL) {
|
|
||||||
kwdict = create_keyword_args(kwnames, pp_stack, func);
|
|
||||||
if (kwdict == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
kwdict = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
callargs = load_args(pp_stack, nargs);
|
|
||||||
if (callargs == NULL) {
|
|
||||||
Py_XDECREF(kwdict);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = do_call_core(func, callargs, kwdict);
|
|
||||||
Py_XDECREF(callargs);
|
|
||||||
Py_XDECREF(kwdict);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict)
|
do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue