it-swarm.com.de

Warum ist epoll schneller als select?

Ich habe viele Vergleiche gesehen, die besagen, dass select die fd-Liste durchgehen muss, und das ist langsam. Aber warum muss epoll das nicht tun?

51
amazingjxq

Es gibt viele Fehlinformationen darüber, aber der wahre Grund ist der folgende:

Ein typischer Server kann beispielsweise 200 Verbindungen verwalten. Es wird jede Verbindung bedienen, bei der Daten geschrieben oder gelesen werden müssen, und dann muss es warten, bis mehr Arbeit zu tun ist. Während es wartet, muss es unterbrochen werden, wenn Daten auf einer dieser 200 Verbindungen empfangen werden.

Mit select muss der Kernel den Prozess zu 200 Wartelisten hinzufügen, eine für jede Verbindung. Dazu ist ein "Thunk" erforderlich, um den Prozess an die Warteliste anzuhängen. Wenn der Prozess endgültig beendet ist, muss er aus allen 200 Wartelisten entfernt und alle diese Thunks müssen freigegeben werden.

Im Gegensatz dazu hat bei epoll der Socket epoll selbst eine Warteliste. Der Prozess muss nur auf diese eine Warteliste gesetzt werden, wobei nur ein Thunk verwendet wird. Wenn der Prozess aufwacht, muss er von nur einer Warteliste entfernt werden und nur ein Thunk muss freigegeben werden.

Um klar zu sein, muss bei epoll der epoll Socket selbst an jede dieser 200 Verbindungen angeschlossen werden. Dies geschieht jedoch einmal für jede Verbindung, wenn sie überhaupt akzeptiert wird. Und dies wird für jede Verbindung einmal abgerissen, wenn sie entfernt wird. Im Gegensatz dazu muss jeder Aufruf von select, der blockiert, den Prozess zu jeder Warteschlange für jeden überwachten Socket hinzufügen.

Ironischerweise entstehen bei select die größten Kosten durch die Überprüfung, ob Sockets, die keine Aktivität hatten, eine Aktivität hatten. Mit epoll müssen Sockets, die keine Aktivität hatten, nicht überprüft werden, da sie, wenn sie Aktivität hatten, den Socket epoll informiert hätten, wenn diese Aktivität stattgefunden hätte. In gewisser Weise fragt select jedes Mal, wenn Sie select aufrufen, jeden Socket ab, um festzustellen, ob Aktivitäten vorhanden sind, während epoll sie abruft, sodass die Socket-Aktivität selbst den Prozess benachrichtigt.

95
David Schwartz

Der Hauptunterschied zwischen epoll und select besteht darin, dass in select() die Liste der zu wartenden Dateideskriptoren nur für die Dauer eines einzelnen select() - Aufrufs vorhanden ist. und die aufrufende Task bleibt nur für die Dauer eines einzelnen Anrufs in den Warteschlangen der Sockets. In epoll hingegen erstellen Sie einen einzelnen Dateideskriptor, der Ereignisse aus mehreren anderen Dateideskriptoren zusammenfasst, auf die Sie warten möchten. Die Liste der überwachten FDS ist also langlebig, und die Aufgaben bleiben am Socket Warteschlangen über mehrere Systemaufrufe hinweg abwarten. Da ein epoll fd für mehrere Tasks freigegeben werden kann, handelt es sich nicht mehr um eine einzelne Task in der Warteschlange, sondern um eine Struktur, die selbst eine andere Warteschlange enthält, die alle Prozesse enthält, die derzeit auf epoll fd. (In Bezug auf die Implementierung wird dies durch die Warteschlangen der Sockets abstrahiert, die einen Funktionszeiger und einen void* - Datenzeiger enthalten, der an diese Funktion übergeben wird.).

Um die Mechanik etwas näher zu erläutern:

  1. Ein epoll -Dateideskriptor hat einen privaten struct eventpoll, Der festhält, welche FDs an diesen FD angehängt sind. struct eventpoll Hat auch eine Warteschlange, die alle Prozesse aufzeichnet, die derzeit epoll_wait Auf diesem Feld sind. struct epoll Enthält auch eine Liste aller Dateideskriptoren, die derzeit zum Lesen oder Schreiben zur Verfügung stehen.
  2. Wenn Sie einem epoll fd mit epoll_ctl() einen Dateideskriptor hinzufügen, fügt epoll den struct eventpoll Zur Warteschlange dieses fd hinzu. Außerdem wird geprüft, ob der fd derzeit zur Verarbeitung bereit ist, und er wird gegebenenfalls der Bereitschaftsliste hinzugefügt.
  3. Wenn Sie mit epoll_wait Auf ein epoll fd warten, prüft der Kernel zuerst die Bereitschaftsliste und gibt sofort zurück, ob bereits Dateideskriptoren bereitstehen. Wenn nicht, fügt es sich der einzelnen Warteschlange in struct eventpoll Hinzu und geht in den Ruhezustand.
  4. Wenn ein Ereignis auf einem Socket auftritt, der epoll() bearbeitet wird, ruft es den Rückruf epoll auf, der den Dateideskriptor zur Bereitschaftsliste hinzufügt, und weckt auch alle Wartenden auf dass struct eventpoll.

Offensichtlich müssen struct eventpoll Und die verschiedenen Listen und Warteschlangen sorgfältig gesperrt werden, aber das ist ein Implementierungsdetail.

Das Wichtigste ist, dass ich zu keinem Zeitpunkt oben einen Schritt beschrieben habe, der alle relevanten Dateideskriptoren durchläuft. Wenn Sie vollständig ereignisbasiert sind und einen langlebigen Satz von fds und eine Bereitschaftsliste verwenden, kann epoll vermeiden, jemals O (n Zeit für eine Operation zu benötigen, wobei n die Anzahl der Dateideskriptoren ist überwacht werden.

20
elite21