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-23 17:56:06 +02:00
parent f428521481
commit a9ba1ab21b
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 * static PyObject *
map_next(mapobject *lz) map_next(mapobject *lz)
{ {
PyObject *val; PyObject *small_stack[5];
PyObject *argtuple; PyObject **stack;
PyObject *result; Py_ssize_t niters, nargs, i;
Py_ssize_t numargs, i; PyObject *result = NULL;
numargs = PyTuple_GET_SIZE(lz->iters); niters = PyTuple_GET_SIZE(lz->iters);
argtuple = PyTuple_New(numargs); if (niters <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) {
if (argtuple == NULL) stack = small_stack;
}
else {
stack = PyMem_Malloc(niters * sizeof(PyObject*));
if (stack == NULL) {
PyErr_NoMemory();
return NULL; return NULL;
}
}
for (i=0 ; i<numargs ; i++) { nargs = 0;
for (i=0; i < niters; i++) {
PyObject *it = PyTuple_GET_ITEM(lz->iters, i); PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
val = Py_TYPE(it)->tp_iternext(it); PyObject *val = Py_TYPE(it)->tp_iternext(it);
if (val == NULL) { if (val == NULL) {
Py_DECREF(argtuple); goto exit;
return NULL;
} }
PyTuple_SET_ITEM(argtuple, i, val); 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);
} }
result = PyObject_Call(lz->func, argtuple, NULL);
Py_DECREF(argtuple);
return result; return result;
} }