diff -r 71876e4abce4 Include/abstract.h --- a/Include/abstract.h Thu Dec 15 12:40:53 2016 +0100 +++ b/Include/abstract.h Thu Dec 15 14:23:30 2016 +0100 @@ -311,8 +311,8 @@ PyAPI_FUNC(PyObject **) _PyStack_UnpackD The size is chosen to not abuse the C stack and so limit the risk of stack overflow. The size is also chosen to allow using the small stack for most function calls of the Python standard library. On 64-bit CPU, it allocates - 40 bytes on the stack. */ -#define _PY_FASTCALL_SMALL_STACK 5 + 24 bytes (3*8) on the stack. */ +#define _PY_FASTCALL_SMALL_STACK 3 /* Call the callable object 'callable' with the "fast call" calling convention: args is a C array for positional arguments (nargs is the number of diff -r 71876e4abce4 Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Thu Dec 15 12:40:53 2016 +0100 +++ b/Modules/_testcapimodule.c Thu Dec 15 14:23:30 2016 +0100 @@ -4005,6 +4005,60 @@ dict_get_version(PyObject *self, PyObjec return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)version); } +static Py_uintptr_t compute_stack_start; + +static PyObject* +compute_stacksize(PyObject *self, PyObject *args) +{ + PyObject *func, *n2, *res; + Py_ssize_t n; + Py_uintptr_t stack; + size_t stack_size; + + if (!PyArg_ParseTuple(args, "On", &func, &n)) { + return NULL; + } + + stack = (Py_uintptr_t)&func; + if (stack > compute_stack_start) { + stack_size = stack - compute_stack_start; + } + else { + stack_size = compute_stack_start - stack; + } + + if (n == 0) { + return PyLong_FromSize_t(stack_size); + } + + n2 = PyLong_FromSsize_t(n - 1); + if (n2 == NULL) { + return NULL; + } + + res = PyObject_CallFunctionObjArgs(func, func, n2, NULL); + Py_DECREF(n2); + return res; +} + +static PyObject* +pyobjectl_callfunctionobjargs_stacksize(PyObject *self, PyObject *arg) +{ + PyMethodDef meth = {"compute_stacksize", compute_stacksize, METH_VARARGS}; + PyObject *func, *res; + + compute_stack_start = (Py_uintptr_t)&meth; + func = PyCFunction_NewEx(&meth, self, NULL); + if (func == NULL) { + return NULL; + } + + res = PyObject_CallFunctionObjArgs(func, func, arg, NULL); + Py_DECREF(func); + + return res; +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, @@ -4208,6 +4262,8 @@ static PyMethodDef TestMethods[] = { {"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS}, {"tracemalloc_get_traceback", tracemalloc_get_traceback, METH_VARARGS}, {"dict_get_version", dict_get_version, METH_VARARGS}, + {"pyobjectl_callfunctionobjargs_stacksize", + pyobjectl_callfunctionobjargs_stacksize, METH_O}, {NULL, NULL} /* sentinel */ }; diff -r 71876e4abce4 Objects/abstract.c --- a/Objects/abstract.c Thu Dec 15 12:40:53 2016 +0100 +++ b/Objects/abstract.c Thu Dec 15 14:23:30 2016 +0100 @@ -2701,58 +2701,76 @@ PyObject * return retval; } -PyObject * -_PyObject_VaCallFunctionObjArgs(PyObject *callable, va_list vargs) +/* Issue #28870: Inlining helps the reduce the stack consumption */ +Py_LOCAL_INLINE(PyObject *) +object_vacall(PyObject *callable, va_list vargs) { PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; + Py_ssize_t allocated, nargs; PyObject **stack; - Py_ssize_t nargs; PyObject *result; - Py_ssize_t i; - va_list countva; if (callable == NULL) { return null_error(); } - /* Count the number of arguments */ - va_copy(countva, vargs); + stack = small_stack; + allocated = Py_ARRAY_LENGTH(small_stack); + nargs = 0; while (1) { - PyObject *arg = va_arg(countva, PyObject *); + PyObject *arg = va_arg(vargs, PyObject *); if (arg == NULL) { break; } nargs++; - } - va_end(countva); - - /* Copy arguments */ - if (nargs <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { - stack = small_stack; + + if (nargs > allocated) { + if (stack == small_stack) { + /* Choose a size small enough to not waste heap memory + and to reduce the need of realloc() */ + allocated = Py_MAX(8, _PY_FASTCALL_SMALL_STACK * 2); + + stack = PyMem_Malloc(allocated * sizeof(PyObject*)); + if (stack == NULL) { + PyErr_NoMemory(); + return NULL; + } + memcpy(stack, small_stack, (nargs - 1) * sizeof(PyObject*)); + } + else { + PyObject **stack2; + + /* overallocate to reduce the number of realloc() */ + allocated = nargs * 2; + + stack2 = PyMem_Realloc(stack, allocated * sizeof(PyObject*)); + if (stack2 == NULL) { + PyMem_Free(stack); + PyErr_NoMemory(); + return NULL; + } + stack = stack2; + } + } + stack[nargs - 1] = arg; } - else { - stack = PyMem_Malloc(nargs * sizeof(stack[0])); - if (stack == NULL) { - PyErr_NoMemory(); - return NULL; - } - } - - for (i = 0; i < nargs; ++i) { - stack[i] = va_arg(vargs, PyObject *); - } - - /* Call the function */ + result = _PyObject_FastCall(callable, stack, nargs); - if (stack != small_stack) { PyMem_Free(stack); } + return result; } PyObject * +_PyObject_VaCallFunctionObjArgs(PyObject *callable, va_list vargs) +{ + return object_vacall(callable, vargs); +} + +PyObject * PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...) { va_list vargs; @@ -2768,7 +2786,7 @@ PyObject_CallMethodObjArgs(PyObject *cal } va_start(vargs, name); - result = _PyObject_VaCallFunctionObjArgs(callable, vargs); + result = object_vacall(callable, vargs); va_end(vargs); Py_DECREF(callable); @@ -2792,7 +2810,7 @@ PyObject * } va_start(vargs, name); - result = _PyObject_VaCallFunctionObjArgs(callable, vargs); + result = object_vacall(callable, vargs); va_end(vargs); Py_DECREF(callable); @@ -2806,7 +2824,7 @@ PyObject_CallFunctionObjArgs(PyObject *c PyObject *result; va_start(vargs, callable); - result = _PyObject_VaCallFunctionObjArgs(callable, vargs); + result = object_vacall(callable, vargs); va_end(vargs); return result;