Per un numero molto piccolo di socket (varia a seconda dell'hardware, ovviamente, ma stiamo parlando di qualcosa nell'ordine di 10 o meno), select può battere epoll in termini di utilizzo della memoria e velocità di runtime. Ovviamente, per un numero così ridotto di socket, entrambi i meccanismi sono così veloci che non ti interessa davvero questa differenza nella stragrande maggioranza dei casi.
Una precisazione, però. Sia select che epoll scalano linearmente. Una grande differenza, tuttavia, è che le API rivolte allo spazio utente hanno complessità che si basano su cose diverse. Il costo di un fileselect
chiamata corrisponde all'incirca al valore del descrittore di file con il numero più alto passato. Se selezioni su un singolo fd, 100, costa circa il doppio della selezione su un singolo fd, 50. L'aggiunta di più fd al di sotto del più alto non è del tutto gratuita, quindi è un po 'più complicato di questo in pratica, ma questo è una buona prima approssimazione per la maggior parte delle implementazioni.
Il costo di epoll è più vicino al numero di descrittori di file che contengono effettivamente eventi. Se stai monitorando 200 descrittori di file, ma solo 100 di essi contengono eventi, allora stai (molto approssimativamente) pagando solo per quei 100 descrittori di file attivi. È qui che epoll tende a offrire uno dei suoi principali vantaggi rispetto a select. Se hai mille clienti per lo più inattivi, quando usi select stai ancora pagando per tutti e mille. Tuttavia, con epoll, è come se ne avessi solo pochi: stai pagando solo per quelli attivi in un dato momento.
Tutto ciò significa che epoll porterà a un minore utilizzo della CPU per la maggior parte dei carichi di lavoro. Per quanto riguarda l'utilizzo della memoria, è un po 'un problema. select
riesce a rappresentare tutte le informazioni necessarie in modo estremamente compatto (un bit per descrittore di file). E la limitazione FD_SETSIZE (tipicamente 1024) sul numero di descrittori di file che puoi usare select
significa che non spenderai mai più di 128 byte per ciascuno dei tre set di fd che puoi usare conselect
(lettura, scrittura, eccezione). Rispetto a quei 384 byte al massimo, epoll è una specie di maiale. Ogni descrittore di file è rappresentato da una struttura multibyte. Tuttavia, in termini assoluti, non utilizzerà ancora molta memoria. Puoi rappresentare un numero enorme di descrittori di file in poche dozzine di kilobyte (circa 20k per 1000 descrittori di file, credo). E puoi anche aggiungere il fatto che devi spendere tutti i 384 di quei byte conselect
se vuoi monitorare solo un descrittore di file ma il suo valore sembra essere 1024, mentre con epoll spenderesti solo 20 byte. Tuttavia, tutti questi numeri sono piuttosto piccoli, quindi non fa molta differenza.
E c'è anche quell'altro vantaggio di epoll, di cui forse sei già a conoscenza, che non è limitato ai descrittori di file FD_SETSIZE. Puoi usarlo per monitorare tutti i descrittori di file che hai. E se hai un solo descrittore di file, ma il suo valore è maggiore di FD_SETSIZE, anche epoll funziona con quello, ma select
non lo fa.
A caso, ho anche scoperto di recente un leggero inconveniente epoll
rispetto a select
o poll
. Sebbene nessuna di queste tre API supporti file normali (cioè file su un file system), select
e poll
presenti questa mancanza di supporto riportando tali descrittori come sempre leggibili e sempre scrivibili. Questo li rende inadatti per qualsiasi tipo significativo di I / O del filesystem non bloccante, un programma che usa select
o poll
e capita di incontrare un descrittore di file dal filesystem continuerà almeno a funzionare (o se fallisce, non sarà perché di select
o poll
), anche se forse non con le migliori prestazioni.
D'altra parte, epoll
fallirà velocemente con un errore ( EPERM
, apparentemente) quando viene chiesto di monitorare un tale descrittore di file. A rigor di termini, questo non è affatto sbagliato. Sta semplicemente segnalando la sua mancanza di supporto in modo esplicito. Normalmente applaudirei condizioni di errore esplicite, ma questa non è documentata (per quanto ne so) e si traduce in un'applicazione completamente rotta, piuttosto che in una che funziona semplicemente con prestazioni potenzialmente degradate.
In pratica, l'unico posto in cui ho visto questo risultato è quando interagisco con stdio. Un utente potrebbe reindirizzare stdin o stdout da / a un file normale. Mentre in precedenza stdin e stdout sarebbero stati un pipe - supportato da epoll benissimo - diventa quindi un file normale ed epoll fallisce rumorosamente, interrompendo l'applicazione.
poll
per completezza?