Risposte:
Questa è una domanda complicata che stai ponendo. Senza sapere di più sulla natura dei tuoi thread è difficile da dire. Alcuni aspetti da considerare nella diagnosi delle prestazioni del sistema:
È il processo / thread
Tutte e tre queste risorse sono limitate e ognuna può limitare le prestazioni di un sistema. Devi guardare a quale (potrebbero essere 2 o 3 insieme) la tua situazione particolare sta consumando.
Puoi usare ntop
e iostat
, e vmstat
per diagnosticare cosa sta succedendo.
"Perché succede?" è un po 'facile rispondere. Immagina di avere un corridoio in cui puoi adattare quattro persone una accanto all'altra. Vuoi spostare tutta la spazzatura da un lato, dall'altro. Il numero più efficiente di persone è 4.
Se hai 1-3 persone, ti perdi nell'usare un po 'di spazio nel corridoio. Se hai 5 o più persone, almeno una di quelle persone è praticamente bloccata in coda dietro un'altra persona per tutto il tempo. L'aggiunta di sempre più persone intasa il corridoio, non accelera l'attività.
Quindi vuoi avere quante più persone puoi inserirti senza causare alcuna coda. Perché hai le code (o i colli di bottiglia) dipende dalle domande nella risposta di slm.
4
è il numero migliore.
Una raccomandazione comune è n + 1 thread, n il numero di core della CPU disponibili. In questo modo n thread possono funzionare la CPU mentre 1 thread è in attesa di I / O del disco. Avere meno thread non utilizzerebbe completamente la risorsa CPU (ad un certo punto ci sarà sempre I / O da aspettare), avere più thread causerebbe conflitti tra le risorse della CPU.
I thread non sono gratuiti, ma con switch di contesto come overhead e - se i dati devono essere scambiati tra thread che è solitamente il caso - vari meccanismi di blocco. Questo vale solo quando hai effettivamente più core CPU dedicati su cui eseguire il codice. Su una CPU single core, un singolo processo (senza thread separati) è in genere più veloce di qualsiasi thread eseguito. I thread non rendono magicamente più veloce la tua CPU, significa solo un lavoro extra.
Come altri hanno sottolineato ( risposta slm , risposta EightBitTony ) questa è una domanda complicata e ancora di più poiché non descrivi cosa fai e come lo fanno.
Ma aggiungere definitivamente più thread può peggiorare le cose.
Nel campo dell'informatica parallela c'è la legge di Amdahl che può essere applicabile (o non può, ma non descrivi i dettagli del tuo problema, quindi ....) e può fornire alcune informazioni generali su questa classe di problemi.
Il punto della legge di Amdahl è che in qualsiasi programma (in qualsiasi algoritmo) c'è sempre una percentuale che non può essere eseguita in parallelo (la parte sequenziale ) e c'è un'altra percentuale che può essere eseguita in parallelo (la parte parallela ) [Ovviamente queste due porzioni aggiungono fino al 100%].
Queste porzioni possono essere espresse come percentuale del tempo di esecuzione. Ad esempio, può esserci un 25% di tempo impiegato in operazioni strettamente sequenziali e il restante 75% di tempo è impiegato in operazioni che possono essere eseguite in parallelo.
(Immagine da Wikipedia )
La legge di Amdahl prevede che per ogni data porzione parallela (ad esempio il 75%) di un programma è possibile accelerare l'esecuzione solo finora (ad esempio al massimo 4 volte) anche se si utilizzano sempre più processori per fare il lavoro.
Come regola generale, più programmi programmabili non è possibile trasformare in esecuzione parallela, meno è possibile ottenere utilizzando più unità di esecuzione (processori).
Dato che stai usando thread (e non processori fisici), la situazione può essere anche peggiore di così. Ricorda che i thread possono essere elaborati (a seconda dell'implementazione e dell'hardware disponibile, ad esempio CPU / core) condividendo lo stesso processore / core fisico (è una forma di multitasking, come indicato in un'altra risposta).
Questa previsione teorica (circa i tempi della CPU) non considera altri colli di bottiglia pratici come
che può facilmente essere il fattore limitante nelle applicazioni pratiche.
Il colpevole qui dovrebbe essere il "CONTEXT SWITCHING". È il processo di salvataggio dello stato del thread corrente per iniziare l'esecuzione di un altro thread. Se a un numero di thread viene assegnata la stessa priorità, è necessario cambiarli fino al termine dell'esecuzione.
Nel tuo caso, quando ci sono 50 thread, si verificano molti cambi di contesto rispetto al solo 10 thread in esecuzione.
Questa volta il sovraccarico introdotto a causa del cambio di contesto è ciò che rende lento il programma
ps ax | wc -l
riporta 225 processi, e non è per nulla pesantemente caricato). Sono propenso ad andare con l'ipotesi di @ EightBitTony; l'invalidazione della cache è probabilmente un problema maggiore, poiché ogni volta che si svuota la cache, la CPU deve attendere eoni per il codice e i dati dalla RAM.
Per correggere la metafora di EightBitTony:
"Perché succede?" è un po 'facile rispondere. Immagina di avere due piscine, una piena e una vuota. Vuoi spostare tutta l'acqua dall'una all'altra e avere 4 secchi . Il numero più efficiente di persone è 4.
Se hai 1-3 persone, ti stai perdendo l'uso di alcuni secchi . Se hai 5 o più persone, almeno una di quelle persone è bloccata in attesa di un secchio . L'aggiunta di sempre più persone ... non accelera l'attività.
Quindi vuoi avere quante più persone possono fare un po 'di lavoro (usa un secchio) contemporaneamente .
Una persona qui è un thread e un bucket rappresenta qualunque risorsa di esecuzione sia il collo di bottiglia. L'aggiunta di più thread non aiuta se non possono fare nulla. Inoltre, dovremmo sottolineare che passare un secchio da una persona all'altra è in genere più lento di una sola persona che trasporta il secchio alla stessa distanza. Cioè, due thread a turno su un core in genere eseguono meno lavoro di un singolo thread che corre il doppio del tempo: ciò è dovuto al lavoro extra fatto per passare da un thread all'altro.
Se la risorsa di esecuzione limitante (bucket) è una CPU o un core o una pipeline di istruzioni hyper-thread per i tuoi scopi dipende da quale parte dell'architettura è il tuo fattore limitante. Nota anche che stiamo assumendo che i thread siano completamente indipendenti. Questo è solo il caso se condividono non dati (ed evitare eventuali collisioni di cache).
Come hanno suggerito un paio di persone, per l'I / O la risorsa limitante potrebbe essere il numero di operazioni di I / O utilmente accodabili: ciò potrebbe dipendere da tutta una serie di fattori hardware e kernel, ma potrebbe essere facilmente maggiore del numero di nuclei. Qui, il cambio di contesto che è così costoso rispetto al codice associato all'esecuzione, è piuttosto economico rispetto al codice associato I / O. Purtroppo penso che la metafora diventerà completamente fuori controllo se provo a giustificare questo con dei secchi.
Si noti che il comportamento ottimale con il codice associato I / O deve ancora avere al massimo un thread per pipeline / core / CPU. Tuttavia, è necessario scrivere codice I / O asincrono o sincrono / non bloccante e il miglioramento delle prestazioni relativamente piccolo non giustifica sempre la complessità aggiuntiva.
PS. Il mio problema con la metafora del corridoio originale è che suggerisce fortemente che dovresti essere in grado di avere 4 code di persone, con 2 code che trasportano spazzatura e 2 che ritornano per raccoglierne altre. Quindi puoi rendere ogni coda quasi quanto il corridoio e l'aggiunta di persone ha accelerato l'algoritmo (in pratica hai trasformato l'intero corridoio in un nastro trasportatore).
In effetti questo scenario è molto simile alla descrizione standard della relazione tra latenza e dimensione della finestra nella rete TCP, motivo per cui mi è saltato fuori.
È abbastanza semplice e semplice da capire. Avere più thread di quelli supportati dalla CPU in realtà sta serializzando e non parallelizzando. Più thread hai, più lento sarà il tuo sistema. I tuoi risultati sono in realtà una prova di questo fenomeno.