Issue #27809: map_next() uses fast call

Use a small stack allocated in the C stack for up to 5 iterator functions,
otherwise allocates a stack on the heap memory.
This commit is contained in:
Victor Stinner 2016-08-24 01:45:13 +02:00
parent dd69564c38
commit cdb5cee980
1 changed files with 33 additions and 17 deletions

View File

@ -1156,27 +1156,43 @@ map_traverse(mapobject *lz, visitproc visit, void *arg)
static PyObject *
map_next(mapobject *lz)
{
PyObject *val;
PyObject *argtuple;
PyObject *result;
Py_ssize_t numargs, i;
PyObject *small_stack[5];
PyObject **stack;
Py_ssize_t niters, nargs, i;
PyObject *result = NULL;
numargs = PyTuple_GET_SIZE(lz->iters);
argtuple = PyTuple_New(numargs);
if (argtuple == NULL)
return NULL;
for (i=0 ; i<numargs ; i++) {
PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
val = Py_TYPE(it)->tp_iternext(it);
if (val == NULL) {
Py_DECREF(argtuple);
niters = PyTuple_GET_SIZE(lz->iters);
if (niters <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) {
stack = small_stack;
}
else {
stack = PyMem_Malloc(niters * sizeof(stack[0]));
if (stack == NULL) {
PyErr_NoMemory();
return NULL;
}
PyTuple_SET_ITEM(argtuple, i, val);
}
result = PyObject_Call(lz->func, argtuple, NULL);
Py_DECREF(argtuple);
nargs = 0;
for (i=0; i < niters; i++) {
PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
PyObject *val = Py_TYPE(it)->tp_iternext(it);
if (val == NULL) {
goto exit;
}
stack[i] = val;
nargs++;
}
result = _PyObject_FastCall(lz->func, stack, nargs);
exit:
for (i=0; i < nargs; i++) {
Py_DECREF(stack[i]);
}
if (stack != small_stack) {
PyMem_Free(stack);
}
return result;
}