-
- Notifications
You must be signed in to change notification settings - Fork 33.6k
Closed
Labels
performancePerformance or resource usagePerformance or resource usagestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
Description
split out from #106555 (comment)
The current EpollSelector can be sped up a bit. This makes quite a difference when there are 100000+ iterations of the event loop per minute (the use case being receiving bluetooth data from multiple sources) since selectors have to run every iteration.
original: 11.831302762031555
new: 9.579423972172663
import timeit import math import select import os from selectors import EpollSelector, EVENT_WRITE, EVENT_READ class OriginalEpollSelector(EpollSelector): def select(self, timeout=None): if timeout is None: timeout = -1 elif timeout <= 0: timeout = 0 else: # epoll_wait() has a resolution of 1 millisecond, round away # from zero to wait *at least* timeout seconds. timeout = math.ceil(timeout * 1e3) * 1e-3 # epoll_wait() expects `maxevents` to be greater than zero; # we want to make sure that `select()` can be called when no # FD is registered. max_ev = max(len(self._fd_to_key), 1) ready = [] try: fd_event_list = self._selector.poll(timeout, max_ev) except InterruptedError: return ready for fd, event in fd_event_list: events = 0 if event & ~select.EPOLLIN: events |= EVENT_WRITE if event & ~select.EPOLLOUT: events |= EVENT_READ key = self._key_from_fd(fd) if key: ready.append((key, events & key.events)) return ready NOT_EPOLLIN = ~select.EPOLLIN NOT_EPOLLOUT = ~select.EPOLLOUT class NewEpollSelector(EpollSelector): def select(self, timeout=None): if timeout is None: timeout = -1 elif timeout <= 0: timeout = 0 else: # epoll_wait() has a resolution of 1 millisecond, round away # from zero to wait *at least* timeout seconds. timeout = math.ceil(timeout * 1e3) * 1e-3 # epoll_wait() expects `maxevents` to be greater than zero; # we want to make sure that `select()` can be called when no # FD is registered. max_ev = len(self._fd_to_key) or 1 ready = [] try: fd_event_list = self._selector.poll(timeout, max_ev) except InterruptedError: return ready fd_to_key = self._fd_to_key for fd, event in fd_event_list: key = fd_to_key.get(fd) if key: ready.append( ( key, ( (event & NOT_EPOLLIN and EVENT_WRITE) | (event & NOT_EPOLLOUT and EVENT_READ) ) & key.events, ) ) return ready original_epoll = OriginalEpollSelector() new_epoll = NewEpollSelector() for _ in range(512): r, w = os.pipe() os.write(w, b"a") original_epoll.register(r, EVENT_READ) new_epoll.register(r, EVENT_READ) original_time = timeit.timeit( "selector.select()", number=100000, globals={"selector": original_epoll}, ) new_time = timeit.timeit( "selector.select()", number=100000, globals={"selector": new_epoll}, ) print("original: %s" % original_time) print("new: %s" % new_time) Linked PRs
Metadata
Metadata
Assignees
Labels
performancePerformance or resource usagePerformance or resource usagestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory