Bei einer sehr kleinen Anzahl von Sockets (hängt natürlich von Ihrer Hardware ab, aber es handelt sich um etwas in der Größenordnung von 10 oder weniger) kann select die Speicherauslastung und die Laufzeitgeschwindigkeit übertreffen. Natürlich sind bei so wenigen Steckdosen beide Mechanismen so schnell, dass Sie sich in den allermeisten Fällen nicht wirklich um diesen Unterschied kümmern.
Eine Klarstellung. Sowohl Select als auch Epoll skalieren linear. Ein großer Unterschied besteht jedoch darin, dass die APIs mit Blick auf den Benutzerbereich Komplexitäten aufweisen, die auf unterschiedlichen Faktoren beruhen. Die Kosten eines select
Anrufs hängen ungefähr vom Wert des Dateideskriptors mit der höchsten Nummer ab, den Sie übergeben. Wenn Sie auf einem einzelnen fd 100 auswählen, ist das ungefähr doppelt so teuer wie auf einem einzelnen fd 50. Das Hinzufügen von mehr fds unter dem höchsten ist nicht ganz kostenlos, daher ist es in der Praxis etwas komplizierter, aber dies ist eine gute erste Annäherung für die meisten Implementierungen.
Die Kosten für epoll liegen näher an der Anzahl der Dateideskriptoren, auf denen tatsächlich Ereignisse vorhanden sind. Wenn Sie 200 Dateideskriptoren überwachen, aber nur 100 von ihnen Ereignisse enthalten, zahlen Sie (sehr grob) nur für diese 100 aktiven Dateideskriptoren. Hier bietet epoll tendenziell einen seiner Hauptvorteile gegenüber ausgewählten. Wenn Sie tausend Kunden haben, die größtenteils untätig sind, zahlen Sie bei Verwendung von select immer noch für alle tausend. Mit epoll haben Sie jedoch nur wenige - Sie zahlen nur für diejenigen, die zu einem bestimmten Zeitpunkt aktiv sind.
All dies bedeutet, dass epoll bei den meisten Workloads zu einer geringeren CPU-Auslastung führt. Was die Speichernutzung angeht, ist es ein bisschen schwierig. select
schafft es, alle notwendigen Informationen auf sehr kompakte Weise darzustellen (ein Bit pro Dateideskriptor). Und die FD_SETSIZE-Beschränkung (normalerweise 1024) für die Anzahl der Dateideskriptoren, mit denen Sie arbeiten können, select
bedeutet, dass Sie nie mehr als 128 Byte für jeden der drei fd-Sätze ausgeben, mit denen Sie arbeiten könnenselect
(Lesen, Schreiben, Ausnahme). Im Vergleich zu diesen maximal 384 Bytes ist Epoll eine Art Schwein. Jeder Dateideskriptor wird durch eine Multi-Byte-Struktur dargestellt. In absoluten Zahlen wird jedoch immer noch nicht viel Speicher benötigt. Sie können eine große Anzahl von Dateideskriptoren in ein paar Dutzend Kilobyte darstellen (ungefähr 20.000 pro 1000 Dateideskriptoren, glaube ich). Sie können auch die Tatsache select
berücksichtigen , dass Sie alle 384 dieser Bytes ausgeben müssen, wenn Sie nur einen Dateideskriptor überwachen möchten, dessen Wert jedoch 1024 beträgt, während Sie mit epoll nur 20 Bytes ausgeben würden. Trotzdem sind alle diese Zahlen ziemlich klein, so dass es keinen großen Unterschied macht.
Und es gibt noch den anderen Vorteil von epoll, den Sie vielleicht bereits kennen, dass er nicht auf FD_SETSIZE-Dateideskriptoren beschränkt ist. Sie können damit so viele Dateideskriptoren überwachen, wie Sie haben. Und wenn Sie nur einen Dateideskriptor haben, dessen Wert jedoch größer als FD_SETSIZE ist, funktioniert epoll auch damit, aber select
nicht.
Zufällig habe ich kürzlich auch einen kleinen Nachteil epoll
gegenüber select
oder entdeckt poll
. Während keine dieser drei APIs normale Dateien (dh Dateien in einem Dateisystem) unterstützt select
und poll
diese mangelnde Unterstützung als Bericht über solche Deskriptoren darstellt, die immer lesbar und immer beschreibbar sind. Dies macht sie für jede sinnvolle Art von nicht blockierenden Dateisystem-E / A ungeeignet. Ein Programm, das einen Dateideskriptor aus dem Dateisystem verwendet select
oder poll
zufällig findet, funktioniert zumindest weiterhin (oder wenn dies fehlschlägt, liegt dies nicht daran von select
oder poll
), wenn auch vielleicht nicht mit der besten Leistung.
Auf der anderen Seite epoll
wird schnell mit einem Fehler ( EPERM
anscheinend) fehlschlagen , wenn Sie aufgefordert werden, einen solchen Dateideskriptor zu überwachen. Genau genommen ist das kaum falsch. Es signalisiert lediglich explizit den Mangel an Unterstützung. Normalerweise würde ich explizite Fehlerbedingungen begrüßen, aber diese sind nicht dokumentiert (soweit ich das beurteilen kann) und führen zu einer vollständig fehlerhaften Anwendung und nicht zu einer Anwendung, die lediglich mit potenziell verschlechterter Leistung arbeitet.
In der Praxis ist der einzige Ort, an dem ich dies gesehen habe, die Interaktion mit stdio. Ein Benutzer kann stdin oder stdout von / in eine normale Datei umleiten. Während früher stdin und stdout eine Pipe gewesen wären - von epoll ganz gut unterstützt -, wird es dann zu einer normalen Datei und epoll schlägt laut fehl und bricht die Anwendung.
poll
Vollständigkeit halber explizit zu beschreiben ?man select
). Der Linux-Kernel legt kein festes Limit fest, aber die glibc-Implementierung macht fd_set zu einem Typ mit fester Größe, wobei FD_SETSIZE als 1024 definiert ist und die FD _ * () -Makros entsprechend arbeiten diese Grenze. Verwenden Sie stattdessen poll (2), um Dateideskriptoren zu überwachen, die größer als 1023 sind. Unter CentOS 7 sind bereits Probleme aufgetreten, bei denen mein eigener Code bei select () fehlgeschlagen ist, weil der Kernel ein Dateihandle> 1023 zurückgegeben hat, und ich sehe derzeit ein Problem, das nach Twisted riecht und möglicherweise dasselbe Problem betrifft.Bei Tests in meinem Unternehmen trat ein Problem mit epoll () auf, also ein einziger Preis im Vergleich zu select.
Wenn Sie versuchen, mit einer Zeitüberschreitung aus dem Netzwerk zu lesen, ist das Erstellen eines epoll_fd (anstelle eines FD_SET) und das Hinzufügen des fd zum epoll_fd viel teurer als das Erstellen eines FD_SET (ein einfaches Malloc).
Gemäß der vorherigen Antwort werden die Kosten für select () höher, wenn die Anzahl der FDs im Prozess groß wird, aber in unseren Tests war select selbst bei fd-Werten in den 10.000 immer noch ein Gewinner. Dies sind Fälle, in denen es nur einen fd gibt, auf den ein Thread wartet, und der Versuch, die Tatsache zu überwinden, dass das Lesen und Schreiben im Netzwerk bei Verwendung eines blockierenden Thread-Modells keine Zeitüberschreitung verursacht. Natürlich sind blockierende Thread-Modelle im Vergleich zu nicht blockierenden Reaktorsystemen von geringer Leistung, aber es gibt Fälle, in denen eine Integration in eine bestimmte Legacy-Codebasis erforderlich ist.
Diese Art von Anwendungsfall ist in Hochleistungsanwendungen selten, da ein Reaktormodell nicht jedes Mal ein neues epoll_fd erstellen muss. Für das Modell, bei dem ein epoll_fd langlebig ist - was für jedes Hochleistungsserverdesign eindeutig bevorzugt wird -, ist epoll in jeder Hinsicht der klare Gewinner.
quelle
select()
wenn Sie Dateideskriptorwerte im Bereich von 10k + haben - es sei denn, Sie kompilieren die Hälfte Ihres Systems neu, um FD_SETSIZE zu ändern -, also frage ich mich, wie diese Strategie überhaupt funktioniert hat. Für das Szenario, das Sie beschrieben haben, würde ich mir wahrscheinlich ansehen,poll()
welches viel ähnlicher istselect()
als es istepoll()
- aber die FD_SETSIZE-Einschränkung wird entfernt.FD_SETSIZE
ist eine Kompilierungszeitkonstante, die beim Kompilieren Ihrer C- Bibliothek festgelegt wird. Wenn Sie beim Erstellen Ihrer Anwendung einen anderen Wert definieren, stimmen Ihre Anwendung und die C-Bibliothek nicht überein und es läuft schlecht. Wenn Sie Referenzen haben, die behaupten, es sei sicher, sie neu zu definieren,FD_SETSIZE
wäre ich interessiert, sie zu sehen.