From f424f3856da382e0d430692e09a0da07e7d8d02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Fran=C3=A7ois=20Natali?= Date: Wed, 9 Jan 2013 19:00:26 +0100 Subject: [PATCH] Issue #16876: Optimize epoll.poll() by keeping a per-instance epoll events buffer instead of allocating a new one at each poll(). --- Misc/NEWS | 3 +++ Modules/selectmodule.c | 45 ++++++++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS index f6e4b4c158d..324d7218dde 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -212,6 +212,9 @@ Core and Builtins Library ------- +- Issue #16876: Optimize epoll.poll() by keeping a per-instance epoll events + buffer instead of allocating a new one at each poll(). + - Issue #16491: IDLE now prints chained exception tracebacks. - fcntl: add F_DUPFD_CLOEXEC constant, available on Linux 2.6.24+. diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 52be4d8eea0..466f9808860 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -1056,9 +1056,14 @@ static int select_have_broken_poll(void) #include #endif +/* default maximum number of events returned by epoll_wait() */ +#define EPOLL_DEFAULT_MAXEVENTS (FD_SETSIZE) + typedef struct { PyObject_HEAD - SOCKET epfd; /* epoll control file descriptor */ + SOCKET epfd; /* epoll control file descriptor */ + int maxevents; /* maximum number of epoll events */ + struct epoll_event *evs; /* epoll events buffer */ } pyEpoll_Object; static PyTypeObject pyEpoll_Type; @@ -1114,6 +1119,15 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, int flags, SOCKET fd) PyErr_SetFromErrno(PyExc_OSError); return NULL; } + + self->maxevents = EPOLL_DEFAULT_MAXEVENTS; + self->evs = PyMem_New(struct epoll_event, self->maxevents); + if (!self->evs) { + Py_DECREF(self); + PyErr_NoMemory(); + return NULL; + } + return (PyObject *)self; } @@ -1140,6 +1154,10 @@ static void pyepoll_dealloc(pyEpoll_Object *self) { (void)pyepoll_internal_close(self); + if (self->evs) { + PyMem_Free(self->evs); + self->evs = NULL; + } Py_TYPE(self)->tp_free(self); } @@ -1320,7 +1338,6 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds) int maxevents = -1; int nfds, i; PyObject *elist = NULL, *etuple = NULL; - struct epoll_event *evs = NULL; static char *kwlist[] = {"timeout", "maxevents", NULL}; if (self->epfd < 0) @@ -1344,24 +1361,27 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds) } if (maxevents == -1) { - maxevents = FD_SETSIZE-1; - } - else if (maxevents < 1) { + maxevents = EPOLL_DEFAULT_MAXEVENTS; + } else if (maxevents < 1) { PyErr_Format(PyExc_ValueError, "maxevents must be greater than 0, got %d", maxevents); return NULL; } + if (maxevents > self->maxevents) { + struct epoll_event *orig_evs = self->evs; - evs = PyMem_New(struct epoll_event, maxevents); - if (evs == NULL) { - Py_DECREF(self); - PyErr_NoMemory(); - return NULL; + PyMem_RESIZE(self->evs, struct epoll_event, maxevents); + if (!self->evs) { + self->evs = orig_evs; + PyErr_NoMemory(); + return NULL; + } + self->maxevents = maxevents; } Py_BEGIN_ALLOW_THREADS - nfds = epoll_wait(self->epfd, evs, maxevents, timeout); + nfds = epoll_wait(self->epfd, self->evs, self->maxevents, timeout); Py_END_ALLOW_THREADS if (nfds < 0) { PyErr_SetFromErrno(PyExc_OSError); @@ -1374,7 +1394,7 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds) } for (i = 0; i < nfds; i++) { - etuple = Py_BuildValue("iI", evs[i].data.fd, evs[i].events); + etuple = Py_BuildValue("iI", self->evs[i].data.fd, self->evs[i].events); if (etuple == NULL) { Py_CLEAR(elist); goto error; @@ -1383,7 +1403,6 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds) } error: - PyMem_Free(evs); return elist; }