Sulla base dei tuoi commenti, sembra che tu sia davvero interessato a "IO non bloccante". Ciò differisce dalla mia definizione di "programmazione asincrona", che è un approccio al lavoro di decomposizione esemplificato dai processi di Erlang o dalle goroutine.
E, se questa è la tua definizione, allora sì, ci sono stati benchmark . Ma, come tutti i parametri, non dovrebbero essere accettati alla cieca. Invece, devi pensare a cosa succede dietro le quinte.
-
Un thread è l'unità di pianificazione del sistema operativo. Piattaforme come Erlang e Go creano i propri scheduler in cima allo scheduler del sistema operativo, consentendo a più unità di esecuzione di condividere lo stesso thread. Questo è grande, a patto che le unità di esecuzione siano leggere, perché evita i sovraccarichi associati ai thread. * Tuttavia, le operazioni IO richiedono un intervento sul kernel, il che significa che è necessario un thread reale per farli. E se stai implementando un programmatore sottoprocesso, devi essere furbo per non pianificare le attività sottoprocesso su un thread che è bloccato in un'operazione del kernel.
-
Tutte le operazioni di I / O hanno il potenziale per bloccare. ** Quando fai una richiesta di
read
o write
, il kernel cerca se ci sono dati disponibili o ( per scrivere) stanza in un buffer. In caso contrario, il kernel sospende il thread fino al completamento dell'operazione. Ciò rende i server di thread-per-connessione davvero semplici da implementare, ma preoccupa le persone che pensano al sovraccarico dei thread.
-
I sistemi operativi offrono un modo per bloccare simultaneamente più canali IO. La chiamata
select
su POSIX è una di queste: fornisci un elenco di canali (descrittori / socket dei file) che ti interessano e ti dirà quando uno di loro è pronto per leggere o scrivere (leggi è ciò che più interessa alla maggior parte delle persone). Devi ancora fare una chiamata al kernel e finirai per bloccare un thread se non è disponibile nulla, ma questa è solo una una discussione. Questo è come funziona Node.js : quando i dati sono disponibili, viene chiamato il gestore di eventi appropriato (non conosco le parti interne del nodo , ma spero che verificheranno anche che i buffer di scrittura siano disponibili prima di chiamare write
).
-
Quando la tua CPU è al massimo, hai finito. Non importa se usi
select
o un approccio thread-per-connection, devi comunque spendere la CPU per fare qualunque cosa si intenda per server fare. Con l'approccio thread-per-connection, non presti molta attenzione a questo: lo scheduler assegnerà i thread ai core e ti degraderai con grazia. Con select
, dovrai consegnare le connessioni ai thread quando sono pronti per l'elaborazione, o sarai limitato dalle prestazioni di un singolo core (Node.js aggira il problema consentendo di generare più server).
Come ho detto, i parametri di riferimento non dovrebbero essere accettati alla cieca; sono validi solo se modellano il problema del mondo reale che stai cercando di risolvere. L'autore del benchmark collegato funziona per (ha funzionato per?) Mailinator, che se non l'hai usato, è un servizio poste restante per indirizzi email ad hoc. Il che significa che otterrà connessioni di breve durata e ad alta attività da un numero relativamente piccolo di client. Questo è un caso d'uso perfetto per la programmazione thread-per-connection. Come notato nei commenti, un server di chat (di lunga durata, bassa attività) potrebbe essere diverso.
Nella mia mente, la questione del blocco rispetto all'IO non bloccante è piuttosto noiosa: la maggior parte dei server del mondo reale non ha molte connessioni simultanee. Più interessante per me è il modello di programmazione: un modello basato sull'operaio come Erlang o Go significa che puoi concentrarti sulla logica di business e non preoccuparti di come vengono gestite le connessioni.
* Questi overhead includono strutture di scheduling del kernel e, cosa forse più importante, uno stack thread multi-megabyte, la maggior parte dei quali non viene utilizzato. Mentre 2 MB non sembrano molto, si aggiungono rapidamente se si hanno processi 100k ... che la maggior parte delle applicazioni non hanno.
** Non vero al 100%, ma non voglio entrare troppo in profondità nelle erbacce qui.