Perché l'utilizzo di più thread rende più lento rispetto all'utilizzo di meno thread


30

Ho provato a eseguire il programma X usando 8 thread ed è finito in n minuti .
Ho provato a eseguire lo stesso programma usando 50 thread ed è finito in n * 10 minuti .

Perché questo accade e come posso ottenere il numero ottimale di thread che posso usare?

Risposte:


33

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

  • CPU bound (necessita di molte risorse CPU)
  • Memoria associata (richiede molte risorse RAM)
  • Collegamento I / O (risorse di rete e / o del disco rigido)

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 ntope iostat, e vmstatper diagnosticare cosa sta succedendo.


8
Anche l'hardware è importante. Fisico, virtuale, numero di core, tipo di core, cache L1 / L2 / L3, ecc.
EightBitTony

46

"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.


1
Il tuo esempio è fuorviante. Sarebbe meglio dire qualcosa del tipo: "Hai un corridoio in cui puoi adattare quattro persone una accanto all'altra e viene usato da te e da altre persone per compiti diversi. C'è un arbitro che decide chi può attraversare il corridoio Quindi il numero più efficiente di persone è maggiore di 4 e inferiore a un certo numero, in cui le persone iniziano a fare la coda [altamente dipendente dal contesto] ". Di solito avere alcuni thread in più rispetto al numero di CPU ha prestazioni migliori rispetto all'utilizzo di esattamente 4 thread. Se sei l' unico che utilizza la CPU, allora 4è il numero migliore.
Bakuriu,

7
Grande esempio, +1. Bakuriu, è un esempio che illustra il problema di una risorsa condivisa limitata. Sta spiegando il problema, non come trovare il numero ottimale di thread.
Bananguin,

1
Sarebbe anche utile tenere presente che i thread hanno ancora il loro tipo di cambio di contesto che continua. L'aumento del numero di thread non aumenta la capacità delle prestazioni (come hai sottolineato) ma riduce anche il tempo della CPU dando al kernel più lavoro da fare. Fondamentalmente, ci sono rendimenti decrescenti sul threading e fare troppo causa prestazioni retrograde.
Bratchley,

9
Ogni problema può essere descritto a molti livelli di complessità. Ho offerto un'approssimazione del problema, che ritengo utile per spiegare le basi. Certo, può essere più raffinato e più dettagliato, ma più dettagliato lo fai, meno utile è come introduzione al problema.
EightBitTony il

Vorrei solo aggiungere che, invece di dedicare molto tempo a calcolare il numero ottimale di thread, basta codificarlo in modo che possa essere modificato facilmente. Qualsiasi fusione di grandi dimensioni come questa richiederà numerose esecuzioni di test (la maggior parte con piccoli sottogruppi di dati) per essere perfezionate. Aumentare il numero di thread fino a quando non si vede un forte calo delle prestazioni o un impatto su altre attività del sistema è inaccettabile.
DocSalvager,

20

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.


Questa dovrebbe essere la risposta generale data la quantità di informazioni disponibili in questione. non abbiamo bisogno di una tesi e di una filosofia in piena
regola

9

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 (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

  1. Velocità I / O limitata (velocità "disco" e rete)
  2. Limiti di dimensione della memoria
  3. Altri

che può facilmente essere il fattore limitante nelle applicazioni pratiche.


Questa deve essere una risposta selezionata.
Eonil,

6

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


Dal momento che non sappiamo quali siano i thread, questa sembra essere un'ipotesi. Sì, il cambio di contesto aggiunge un sovraccarico, ma se i thread stanno eseguendo un qualche tipo di analisi dei dati, il problema potrebbe essere problemi di cache (cioè non essere in grado di utilizzare la cache perché ogni volta che si cambia thread è necessario svuotarlo).
EightBitTony,

Discussione del cambio di contesto in sé e per sé , a meno che non abbiamo a che fare con un numero enorme di cambi di contesto, probabilmente non avrà un impatto dell'ordine di grandezza sulle prestazioni. 50 thread sono alti ma non estremi (sulla mia scatola in questo momento, ps ax | wc -lriporta 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.
un CVn il

3

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.


Non è una metafora, è un'approssimazione progettata per spiegare il sistema alle persone in modo che possano visualizzarlo facilmente. Come tale, sarà sempre 'spazzato via' da persone che conoscono il prossimo livello di dettaglio, ma non si rendono conto che il loro livello di dettaglio non è effettivamente necessario per i principianti. Nessuno impara la fisica delle particelle a partire dal livello di dottorato. Tutta la roba prima è un'approssimazione che ti conduce gradualmente, perfezionandola mentre procedi. Non è "sbagliato", non è il quadro completo.
EightBitTony,

Nessuno è confuso su quale figura retorica hai usato e non è una cattiva analogia. Ogni analogia ha qualche limite oltre il quale si discosta dalla cosa che dovrebbe descrivere e cessa di essere utile. L'ho menzionato solo perché l'originale mi ha ricordato così fortemente uno scenario diverso e perché non credo che questa versione sia più complessa per la previsione (si spera) migliorata.
Inutile

0

È 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.

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.