Temo di "fare la cosa sbagliata" qui, in tal caso cancellami e mi scuso. In particolare, non riesco a vedere come creo le piccole annotazioni ordinate che alcune persone hanno creato. Tuttavia, ho molte preoccupazioni / osservazioni da formulare su questo thread.
1) L'elemento commentato nello pseudo-codice in una delle risposte popolari
result = query( "select smurfs from some_mushroom" );
// twiddle fingers
go_do_something_with_result( result );
è essenzialmente falso. Se il thread sta elaborando, allora non è il pollice che gira, sta facendo il lavoro necessario. Se, d'altra parte, sta semplicemente aspettando il completamento di IO, allora non lo è utilizzare il tempo di CPU, il punto di tutta l'infrastruttura di controllo filo nel kernel è che la CPU troverà qualcosa di utile da fare. L'unico modo per "modificare i pollici" come suggerito qui sarebbe quello di creare un ciclo di polling, e nessuno che abbia codificato un vero server web è abbastanza inetto per farlo.
2) "I thread sono difficili", ha senso solo nel contesto della condivisione dei dati. Se hai essenzialmente thread indipendenti come nel caso della gestione di richieste web indipendenti, il threading è banalmente semplice, basta codificare il flusso lineare di come gestire un lavoro e rimanere abbastanza sapendo che gestirà più richieste, e ognuna sarà effettivamente indipendente. Personalmente, mi sarei azzardato a dire che per la maggior parte dei programmatori, apprendere il meccanismo di chiusura / callback è più complesso della semplice codifica della versione thread dall'alto verso il basso. (Ma sì, se devi comunicare tra i thread, la vita diventa davvero difficile molto velocemente, ma poi non sono convinto che il meccanismo di chiusura / callback lo cambi davvero, limita solo le tue opzioni, perché questo approccio è ancora realizzabile con i thread Comunque, quello "
3) Finora nessuno ha presentato prove concrete del perché un particolare tipo di cambio di contesto richiederebbe più o meno tempo rispetto a qualsiasi altro tipo. La mia esperienza nella creazione di kernel multi-tasking (su piccola scala per controller embedded, niente di così fantasioso come un sistema operativo "reale") suggerisce che non sarebbe così.
4) Tutte le illustrazioni che ho visto fino ad oggi che pretendono di mostrare quanto Node sia molto più veloce di altri server web sono orribilmente imperfette, tuttavia sono imperfette in un modo che illustra indirettamente un vantaggio che accetterei sicuramente per Node (e non è affatto insignificante). Il nodo non sembra che abbia bisogno (o addirittura non permetta, in realtà) di ottimizzazione. Se si dispone di un modello con thread, è necessario creare thread sufficienti per gestire il carico previsto. Fallo male e finirai con scarse prestazioni. Se ci sono troppi thread, la CPU è inattiva, ma non è in grado di accettare più richieste, creare troppi thread e sprecherai la memoria del kernel e, nel caso di un ambiente Java, sprecherai anche la memoria heap principale . Ora, per Java, sprecare heap è il primo, il migliore, modo per rovinare le prestazioni del sistema, perché un'efficiente raccolta dei rifiuti (attualmente, questo potrebbe cambiare con G1, ma sembra che la giuria sia ancora fuori su quel punto almeno all'inizio del 2013) dipende dall'avere un sacco di mucchio di riserva. Quindi, c'è il problema, ottimizzalo con troppi thread, hai CPU inattive e scarsa produttività, sintonizzalo con troppi e si impantana in altri modi.
5) Esiste un altro modo in cui accetto la logica dell'affermazione secondo cui l'approccio di Node "è più veloce in base alla progettazione", ed è questo. La maggior parte dei modelli di thread utilizza un modello di interruttore di contesto suddiviso in intervalli di tempo, sovrapposto al modello preventivo più appropriato (avviso di valutazione del valore :) e più efficiente (non un giudizio di valore). Ciò accade per due motivi, in primo luogo, la maggior parte dei programmatori non sembra comprendere la prevenzione preventiva, e in secondo luogo, se impari il threading in un ambiente Windows, il timelicing è lì che ti piaccia o no (ovviamente, questo rinforza il primo punto ; in particolare, le prime versioni di Java utilizzavano la prelazione prioritaria sulle implementazioni di Solaris e il timelicing in Windows, poiché la maggior parte dei programmatori non capiva e si lamentava che "il threading non funziona in Solaris" hanno cambiato il modello in timeslice ovunque). Comunque, la linea di fondo è che il timeslicing crea ulteriori cambi di contesto (e potenzialmente non necessari). Ogni cambio di contesto richiede tempo CPU e quel tempo viene effettivamente rimosso dal lavoro che può essere svolto sul lavoro reale a portata di mano. Tuttavia, la quantità di tempo investita nel cambio di contesto a causa del timeslicing non dovrebbe essere superiore a una percentuale molto piccola del tempo complessivo, a meno che non stia accadendo qualcosa di piuttosto stravagante e non c'è motivo per cui mi possa aspettare che ciò avvenga in un server web semplice). Quindi, sì, gli interruttori di contesto in eccesso coinvolti nel timeslicing sono inefficienti (e questi non si verificano in e quel tempo viene effettivamente rimosso dal lavoro che può essere svolto sul lavoro reale a portata di mano. Tuttavia, la quantità di tempo investita nel cambio di contesto a causa del timeslicing non dovrebbe essere superiore a una percentuale molto piccola del tempo complessivo, a meno che non stia accadendo qualcosa di piuttosto stravagante e non c'è motivo per cui mi possa aspettare che ciò avvenga in un server web semplice). Quindi, sì, gli interruttori di contesto in eccesso coinvolti nel timeslicing sono inefficienti (e questi non si verificano in e quel tempo viene effettivamente rimosso dal lavoro che può essere svolto sul lavoro reale a portata di mano. Tuttavia, la quantità di tempo investita nel cambio di contesto a causa del timeslicing non dovrebbe essere superiore a una percentuale molto piccola del tempo complessivo, a meno che non stia accadendo qualcosa di piuttosto stravagante e non c'è motivo per cui mi possa aspettare che ciò avvenga in un server web semplice). Quindi, sì, gli interruttori di contesto in eccesso coinvolti nel timeslicing sono inefficienti (e questi non si verificano inthread del kernel come regola, tra l'altro), ma la differenza sarà di qualche percento della velocità effettiva, non del tipo di fattori di numero intero impliciti nelle dichiarazioni di prestazione che sono spesso implicate per Node.
Ad ogni modo, mi scuso per tutto ciò che è lungo e sconclusionato, ma lo sento davvero finora, la discussione non ha dimostrato nulla e sarei felice di sentire qualcuno in una di queste situazioni:
a) una vera spiegazione del perché Node dovrebbe essere migliore (al di là dei due scenari che ho delineato sopra, il primo dei quali (scarsa sintonizzazione) credo sia la vera spiegazione di tutti i test che ho visto finora. ([modifica ], in realtà, più ci penso, più mi chiedo se la memoria utilizzata da un gran numero di stack potrebbe essere significativa qui. Le dimensioni di stack predefinite per i thread moderni tendono ad essere piuttosto enormi, ma la memoria allocata da un sistema di eventi basato sulla chiusura sarebbe solo ciò che è necessario)
b) un vero benchmark che offra effettivamente una buona possibilità al server threaded di scelta. Almeno in questo modo, dovrei smettere di credere che le affermazioni siano essenzialmente false;> ([modifica] probabilmente è piuttosto più forte di quanto pensassi, ma ritengo che le spiegazioni fornite per i vantaggi in termini di prestazioni siano nella migliore delle ipotesi incomplete, e il i benchmark mostrati sono irragionevoli).
Saluti, Toby
select()
è più veloce degli scambi di contesto di thread.