2012-04-13 6 views
5

Ho un metodo nel mio algoritmo che esegue un ciclo molto stretto su un insieme di dati molto grande. L'ho originariamente scritto a thread singolo che andava bene, ma ci è voluto molto tempo. Ora sono al punto di voler accelerare, quindi ora sto usando ThreadPool per parallelizzare il lavoro. Il problema è che questo fa sì che il mio utilizzo della CPU passi al 95-100%, cosa che mi aspettavo. Tuttavia, le mie prestazioni sono aumentate notevolmente, ma penso che avrei potuto renderlo migliore se avessi potuto ridurre tutto il cambio di contesto. Questo fa sì che anche i miei altri programmi siano un po 'più lenti dal momento che devono combattere i thread per le risorse della CPU.Chiamate threadback in anello chiuso - 100% CPU

La mia domanda è: come devo procedere? L'unica cosa che ho potuto pensare è di limitare il numero di thread in esecuzione contemporaneamente, ma questo potrebbe rallentare il mio algoritmo poiché solo pochi thread saranno in grado di essere eseguiti contemporaneamente. Non voglio aggiungere addormentamenti nelle mie discussioni perché ho solo bisogno dell'algoritmo per eseguire il completamento il più rapidamente possibile.

MODIFICA: diverse persone hanno menzionato l'utilizzo del TPL. Penso che sia una grande idea, ma sfortunatamente ho dimenticato di dire che sono bloccato usando .NET 3.5 dato che l'applicazione madre non ha ancora rilasciato una versione che usa .NET 4.

+1

Se si desidera la velocità, perché si eliminano tutte le cose più veloci? L'interruttore di contesto è fatto dal sistema operativo, non si scherza con questo ... – gbianchi

+1

La soluzione è ridurre la priorità dei thread nel pool. Questa non è una risposta perché non so come farlo in modo efficiente :( –

+0

Sembra che tu debba ampliare le singole attività: OTOH, il threadpool è abbastanza intelligente da fare già la maggior parte di ciò che suggerisci. rispetto alle CPU, li accoderà invece di avviare più thread. –

risposta

6

Si tratta di gestione delle risorse. Il tuo programma sta attualmente registrando tutte le risorse, e così altri programmi hanno accesso ridotto a loro. È necessario bilanciare la parte "Ho solo bisogno dell'algoritmo per eseguire al più presto possibile" con "Questo fa sì che anche i miei altri programmi siano un po 'laggosi dal momento che devono combattere i thread per le risorse della CPU". Si escludono a vicenda; non puoi far funzionare la tua app il più velocemente possibile su un particolare computer e mantenere le altre app perfettamente reattive. C'è semplicemente un limite a quanto la CPU può fare in qualsiasi periodo di tempo.

Per quanto guadagni di efficienza, ci sono alcune cose che puoi fare:

  • Non utilizzare il ThreadPool per gli algoritmi ultra-ottimizzato filettati. Il ThreadPool è eccellente per le semplici operazioni "Vai fuori e fai questo e fammi sapere che hai finito". Tuttavia, se si sta cercando di ottimizzare, il sovraccarico inerente all'aggiunta di un ulteriore livello di pianificazione dei thread con ThreadPool (in aggiunta al sovraccarico inerente alla CPU e al sistema operativo) può essere evitato. È inoltre disponibile un controllo più limitato sui thread in un ThreadPool, il che significa che le ottimizzazioni come l'assegnazione dell'affinità del processore (per il bilanciamento del carico) e la priorità (per fornire un thread più o meno tempo) dei singoli thread non sono disponibili.Prova a creare thread semplici o a esaminare la TPL che ha un numero di strategie per ottenere più operazioni (non tutte richiedono il threading in primo luogo).

  • Sì, vorrai essere in grado di "limitare" il numero di thread. Questo è sia per consentire ad altri programmi un po 'di tempo per la CPU, riducendo la necessità del programma, ma come ho detto, c'è anche un sovraccarico inerente al multithreading. La regola generale è che se a una CPU viene dato più del doppio del conteggio dei thread attivi in ​​esecuzione con "unità di esecuzione" (questi sono i core fisici su un chip CPU e "processori logici" come la tecnologia HyperThreading che divide un core in due), quindi il sistema operativo impiegherà più tempo a pianificare i thread e a passare da uno all'altro ("cache-thrashing") a quello che impiegherà effettivamente nell'esecuzione dei thread. In termini più generali, c'è una legge di rendimenti decrescenti, che progredirà in "diseconomie di scala"; alla fine, aggiungendo un altro thread, il tuo programma verrà eseguito più lentamente rispetto a quando non avessi usato quel thread. Sì, ThreadPool gestisce le discussioni massime per te, ma probabilmente è la più semplice delle sue varie funzioni per implementare te stesso nel tuo algoritmo.

  • Assicurarsi che il lavoro di ogni thread sia ottimizzato. Cercare algoritmi ingenui o inefficienti (li chiamo "O (My God) -complessità") e semplificarli. C'è un limite inferiore all'efficienza della maggior parte delle operazioni (varia in base al tipo di operazione) e "l'ottimizzazione prematura è la radice di tutti i mali" (non ottimizzare le prestazioni a scapito del funzionamento effettivo del codice), ma Capisci che in un ambiente multithreading, qualsiasi guadagno che puoi ottenere sull'efficienza di un algoritmo quando viene eseguito una volta verrà moltiplicato per il numero di volte in cui lo stai eseguendo, quindi assicurarti che un'operazione parallela sia efficiente è un doppio vantaggio.

+0

+1 solo per O (Mio Dio) da solo - ottima risposta ;-) – BrokenGlass

+1

"La regola generale è che se a una CPU viene dato più del doppio del numero di thread attivi in ​​esecuzione come "unità di esecuzione" (questi sono i nuclei fisici su un chip della CPU e "processori logici" come la tecnologia HyperThreading che divide un core in due), quindi il sistema operativo impiegherà più tempo per pianificare i thread e passare da uno all'altro ("cache-thrashing")) di quanto spenderà effettivamente eseguendo i thread '- hai effettivamente provato questo? Sul codice non gestito, non fa alcuna differenza se si hanno 8 thread legati alla CPU o 800 - approssimativamente la stessa quantità di lavoro viene eseguita. –

+0

Quindi, se ho una CPU i7 core (4 core fisici + 4 core virtuali), 16 thread è il limite in base a tale regola? –

2

Se è possibile riscrivere l'applicazione principale in un ciclo foreach sopra un IEnumerable è possibile utilizzare PLINQ per parallelizzare il ciclo. È possibile utilizzare WithDegreeOfParallelism per controllare il numero di core che l'applicazione utilizzerà. Puoi evitare un po 'di "lag" che provi non usando tutti i core sul tuo computer. Inoltre, non è necessario gestire il partizionamento del ciclo tra i thread per evitare conflitti di risorse non necessari. PLINQ fa tutto questo per te.

Supponendo di avere questo molto semplice ciclo single-threaded:

var arrayOfStuff = new[] { ... }; 
for (var i = 0; i < arrayOfStuff.Length; ++i) 
    DoSomething(arrayOfStuff[i]); 

Se ordinamento non ha importanza è possibile parallelizzare utilizzando PLINQ utilizzando un core a meno di è disponibile:

var cores = Math.Max(1, Environment.ProcessorCount - 1); 
arrayOfStuff.AsParallel().WithDegreeOfParallelism(cores).ForAll(DoSomething); 

Anche se il tuo ciclo principale è più complesso puoi riscriverlo in un blocco iteratore che puoi parallelizzare:

IEnumerable<Stuff> GetStuff() { 
    for (... very complex looping ...) { 
    ... 
    yield return stuff; 
    } 
}