Avvertenze sui reattori di selezione / sondaggio rispetto a epoll in Twisted


95

Tutto ciò che ho letto e sperimentato (app basate su Tornado) mi porta a credere che ePoll sia un sostituto naturale del networking basato su Select e Poll, specialmente con Twisted. Il che mi rende paranoico, è piuttosto raro che una tecnica o una metodologia migliore non abbia un prezzo.

Leggere un paio di dozzine di confronti tra epoll e alternative mostra che epoll è chiaramente il campione per velocità e scalabilità, in particolare perché scala in modo lineare, il che è fantastico. Detto questo, per quanto riguarda il processore e l'utilizzo della memoria, epoll è ancora il campione?

Risposte:


190

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. selectriesce 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 selectsignifica 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 conselectse 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 selectnon lo fa.

A caso, ho anche scoperto di recente un leggero inconveniente epollrispetto a selecto poll. Sebbene nessuna di queste tre API supporti file normali (cioè file su un file system), selecte pollpresenti 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 selecto polle capita di incontrare un descrittore di file dal filesystem continuerà almeno a funzionare (o se fallisce, non sarà perché di selecto poll), anche se forse non con le migliori prestazioni.

D'altra parte, epollfallirà 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.


Risposta molto bella. Considerare di essere esplicito sul comportamento di pollper completezza?
quark

6
I miei due centesimi sul comportamento di lettura da file ordinari: generalmente preferisco il fallimento totale al degrado delle prestazioni. Il motivo è che è molto più probabile che venga rilevato durante lo sviluppo e quindi funzionato correttamente (ad esempio avendo un metodo alternativo per eseguire l'I / O per i file effettivi). YMMV ovviamente: potrebbe non esserci un rallentamento evidente, nel qual caso il fallimento non è migliore. Ma un rallentamento drammatico che si verifica solo in casi speciali può essere molto difficile da rilevare durante lo sviluppo, lasciandolo come una bomba a orologeria quando viene effettivamente dispiegato.
quark

1
Devo solo leggere completamente la tua modifica. In un certo senso sono d'accordo sul fatto che probabilmente non è giusto che epoll non imiti i suoi predecessori, ma poi di nuovo posso immaginare che lo sviluppatore che ha implementato l'errore EPERM abbia pensato "Solo perché è sempre stato rotto, non è giusto rompere il mio come bene." E ancora un altro controargomentazione, sono un programmatore difensivo, qualsiasi cosa oltre 1 + 1 sia sospetta e codifico in modo tale da consentire errori graziosi. Avere il kernel che lancia un errore fuori dalle aspettative non è carino o premuroso.
David

1
@ Jean-Paul potresti aggiungere qualche spiegazione anche su kqueue?
Brava persona

Mettendo da parte le prestazioni, c'è un problema derivante da questo (da man select) Il kernel Linux non impone limiti fissi, ma l'implementazione glibc rende fd_set un tipo a dimensione fissa, con FD_SETSIZE definito come 1024, e le macro FD _ * () che operano secondo quel limite. Per monitorare i descrittori di file maggiori di 1023, utilizzare invece poll (2). Su CentOS 7 ho già riscontrato problemi in cui il mio codice ha fallito un select () perché il kernel ha restituito un handle di file> 1023 e attualmente sto osservando un problema che puzza come se Twisted colpisse lo stesso problema.
Paul D Smith

4

Durante i test presso la mia azienda, è emerso un problema con epoll (), quindi un costo unico rispetto a select.

Quando si tenta di leggere dalla rete con un timeout, la creazione di un epoll_fd (invece di un FD_SET) e l'aggiunta di fd a epoll_fd, è molto più costoso rispetto alla creazione di un FD_SET (che è un semplice malloc).

Come per la risposta precedente, man mano che il numero di FD nel processo diventa grande, il costo di select () aumenta, ma nei nostri test, anche con valori fd di 10.000, select era ancora un vincitore. Questi sono casi in cui c'è un solo fd su cui un thread sta aspettando, e semplicemente cercando di superare il fatto che la lettura in rete e la scrittura in rete non vanno in timeout quando si utilizza un modello di thread di blocco. Naturalmente, i modelli di thread di blocco hanno prestazioni ridotte rispetto ai sistemi di reattori non bloccanti, ma ci sono occasioni in cui, per integrarsi con una particolare base di codice legacy, è necessario.

Questo tipo di caso d'uso è raro nelle applicazioni ad alte prestazioni, perché un modello di reattore non ha bisogno di creare un nuovo epoll_fd ogni volta. Per il modello in cui epoll_fd è di lunga durata --- che è chiaramente preferito per qualsiasi progetto di server ad alte prestazioni --- epoll è il chiaro vincitore in ogni modo.


5
Ma non puoi nemmeno usarlo select()se hai valori del descrittore di file nell'intervallo 10k + - a meno che tu non ricompili metà del tuo sistema per cambiare FD_SETSIZE - quindi mi chiedo come abbia funzionato questa strategia. Per lo scenario che hai descritto, probabilmente guarderei a poll()quale è molto più simile di select()quanto non sia epoll(), ma rimuove la limitazione FD_SETSIZE.
Jean-Paul Calderone

Puoi usare select () se hai valori del descrittore di file nell'intervallo di 10K, perché puoi malloc () un FD_SET. Infatti, poiché FD_SETSIZE è in fase di compilazione e il limite fd effettivo è in fase di esecuzione, l'UNICO utilizzo sicuro di FD_SET controlla il numero del descrittore di file rispetto alla dimensione di FD_SET, ed esegue un malloc (o equivalente morale) se FD_SET è troppo piccolo. Sono rimasto scioccato quando l'ho visto in produzione con un cliente. Dopo aver programmato socket per 20 anni, tutto il codice che ho scritto e la maggior parte dei tutorial sul Web non sono sicuri.
Brian Bulkowski

5
Questo non è vero, per quanto ne so, su nessuna piattaforma popolare. FD_SETSIZEè una costante del tempo di compilazione impostata quando la libreria C viene compilata. Se lo definisci su un valore diverso quando crei l'applicazione, l'applicazione e la libreria C non saranno d'accordo e le cose andranno male. Se hai riferimenti che affermano che è sicuro ridefinire, FD_SETSIZEsarei interessato a vederli.
Jean-Paul Calderone
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.