Benchmark del codice asincrono

3

La programmazione asincrona sembra essere piuttosto popolare in questi giorni. Uno dei vantaggi più quotati è l'aumento delle prestazioni dalla rimozione di operazioni che bloccano i thread. Ma ho visto anche persone dire che il vantaggio non è così grande. E quel pool di thread correttamente configurato può avere lo stesso effetto del codice completamente asincrono.

La mia domanda è: ci sono dei veri benchmark che mettono a confronto il blocco contro il codice asincrono? Può essere qualsiasi lingua, ma penso che C # e Java sarebbero i più rappresentativi. Non sei sicuro di quanto sarebbe buono questo benchmark con Node.js o simili.

Modifica : il mio tentativo di rispondere a una domanda generale combinata con una terminologia poco chiara sembra aver fallito. Con "codice asincrono" intendo ciò che alcune risposte descrivono come programmazione di eventi o callback. Nel risultato finale, le operazioni che bloccano il thread sono invece delegate ad un sistema di callback in modo che i thread possano essere utilizzati meglio.

E se volessi fare una domanda specifica: Ci sono dei benchmark che mettono a confronto il throughput / latenza del codice server asincrono / atteso in .NET? O qualsiasi altro confronto simile?

    
posta Euphoric 16.08.2013 - 12:41
fonte

6 risposte

4

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.

    
risposta data 18.08.2013 - 15:26
fonte
3

Tutta la programmazione asincrona (nel nodo e in altre implementazioni asincroni a thread singolo) nasconde le latenze grazie alla possibilità di continuare a lavorare mentre si attendono risorse esterne e di richiedere più risorse esterne contemporaneamente in modo che le loro latenze si sovrappongano . Non ci dovrebbero essere differenze tra async e thread se l'uso dei thread è limitato alle risorse in attesa e non si tassano eccessivamente il sistema con i thread.

Esistono alcune implementazioni asincrone che eseguono le attività asincrone in un pool di thread, per quelle implementazioni non mi aspetto alcuna differenza oltre alla differenza di overhead della libreria e di intelligenza della libreria nella gestione dei thread.

link

    
risposta data 16.08.2013 - 20:17
fonte
1

Quando le persone fanno riferimento ai vantaggi in termini di prestazioni del codice asincrono rispetto al codice di blocco, stanno parlando nel contesto di un singolo thread. Se ti fermi per 2 secondi e non fai nulla mentre una pagina web viene scaricata, è ovviamente più lento di continuare a fare altre cose mentre aspetti.

Sì, i pool di thread possono ottenere lo stesso effetto. La differenza è che il codice asincrono è generalmente più facile da scrivere correttamente rispetto al codice multithread e il codice asincrono di solito non ha bisogno di un thread che lo supporta, quindi è più leggero sulle risorse.

Ad esempio, in attesa di 100 chiamate di rete sono necessari solo 100 handle di socket, anziché 100 handle di socket e 100 thread. Ciò significa che i benchmark come la latenza per uno scaricamento di una pagina Web saranno pressoché uguali, ma il codice asincrono di solito presenta vantaggi in termini di numero di socket simultanei. Il tipo di benchmark che usi dipende dal tuo caso d'uso.

    
risposta data 16.08.2013 - 19:01
fonte
0

Per confrontare il codice asincrono, puoi misurare le sue prestazioni nel solito modo e confrontarlo con il suo equivalente non sincrono.

Time for Synchronous call to return to caller: 900 ms
Time for asynchronous call to return to caller: 50 ms
Time saved: 850 ms.

Ovviamente, non ottieni quel risparmio di tempo gratis; il thread risultante che viene scorporato dalla chiamata asincrona deve ancora trascorrere 850 ms in background completando il suo lavoro, e se si fanno girare abbastanza thread finirà per esaurire i core (in attività legate al processore), quindi il tuo chilometraggio può variare .

Per scoprire quanti miglioramenti avrai nel complesso, puoi eseguire un load test .

    
risposta data 16.08.2013 - 18:48
fonte
0

In realtà entrambi gli approcci hanno il loro insieme di svantaggi. È un argomento piuttosto complicato che va ben oltre i thread e gli eventi.

Ti consiglio di leggere l'eccellente "Programmazione concorrente per architetture scalabili Web" , o tutto ( che consiglio) o almeno sezioni 4.2 Architetture server e 4.3 Caso di thread contro eventi .

    
risposta data 16.08.2013 - 19:15
fonte
0

Indipendentemente da come definite programmazione asincrona , non esiste una risposta globale. Alcuni problemi di programmazione sono risolti in modo più efficiente con i thread (o "programmazione asincrona"), altri si limitano a fare un colpo inutile alle prestazioni facendo girare un nuovo thread (o mandando un lavoro a un thread). Quando hai un problema particolare con due soluzioni, devi solo confrontare le prestazioni - dall'inizio alla fine - di ciascuna soluzione prospettica.

Alcuni indicatori che un lavoro può trarre beneficio da una soluzione filettata / asincrona:

  • Sono disponibili calcoli multipli, potenzialmente di lunga durata che possono essere eseguiti indipendentemente l'uno dall'altro
  • Ci sono "distanti" data-fetches / puts (via HTTP, per esempio) insieme ad altri codici che non dipendono dai dati
  • Ci sono molti recuperi dati, interazioni o calcoli medi-lunghi che possono essere eseguiti indipendentemente l'uno dall'altro

O più fondamentalmente, se raggiungi un punto nel codice in cui devi doSomething() e sai che

  1. doSomething() richiederà un tempo "lungo" e ...
  2. Puoi continuare a fare altre cose urgenti, importanti e / o che richiedono tempo mentre aspetti per doSomething() , quindi ...

... è probabilmente vantaggioso eseguire doSomething() in modo asincrono. (Ma devi ancora fare un benchmark, anche se "informalmente", per saperlo con certezza!) Sia che tu sia esplicitamente consapevole che doSomething() ha il suo thread o semplicemente che hai chiamato doSomethingAsync() invece dipende da te e / o dal tuo framework ...

    
risposta data 16.08.2013 - 19:55
fonte

Leggi altre domande sui tag