Ha la Task Parallel Library (o PLINQ) prendono altri processi in considerazione?

StackOverflow https://stackoverflow.com/questions/3488381

  •  28-09-2019
  •  | 
  •  

Domanda

In particolare, sto cercando di utilizzare TPL per iniziare (e aspettare) processi esterni. Fa l'aspetto TPL a carico della macchina totale (sia della CPU e I / O) prima di decidere di iniziare un'altra operazione (da qui - nel mio caso - un altro processo esterno)?

Ad esempio:

Ho circa 100 file multimediali che devono essere codificati o trascodifica (per esempio da WAV a FLAC o da FLAC in MP3). La codifica avviene avviando un processo esterno (ad esempio FLAC.EXE o lame.exe). Ogni file è di circa 30 secondi. Ogni processo è in gran parte della CPU-bound, ma c'è un po 'di I / O in là. Ho 4 core, quindi il caso peggiore (transcodifica reindirizzando decodificatore nell'encoder) ancora utilizza solo 2 core. Mi piacerebbe fare qualcosa di simile:

Parallel.ForEach(sourceFiles,
    sourceFile =>
        TranscodeUsingPipedExternalProcesses(sourceFile));

Sarà questo kick off 100 attività (e quindi 200 processi esterni in competizione per la CPU)? O sarà vedere che il CPU occupato e fare solo 2-3 alla volta?

È stato utile?

Soluzione

Si sta andando a correre in un paio di questioni qui. Il meccanismo di inedia evitare lo scheduler vedrà le attività come bloccato mentre aspettano sui processi. Si fatica a distinguere tra un filo deadlock e uno semplicemente in attesa di un processo per il completamento. Come risultato si può programmare nuovi compiti, se le attività eseguite o un lungo periodo di tempo (vedi sotto). L'euristica hillclimbing dovrebbe tener conto il carico complessivo sul sistema, sia dalla vostra applicazione e gli altri. Si cerca semplicemente di massimizzare il lavoro fatto, in modo che aggiungerà più lavoro fino a quando il throughput complessivo del sistema smette di aumentare e poi si marcia indietro. Non che questo effetto dell'applicazione ma la questione stavation evitamento probabilmente lo farà.

È possibile trovare maggiori dettagli su come funziona tutto questo in programmazione parallela con Microsoft®.NET , Colin Campbell, Ralph Johnson, Ade Miller, Stephen Toub (un precedente progetto è linea ) .

"Il pool di thread .NET gestisce automaticamente il numero di lavoratori thread nel pool. Aggiunge e rimuove le discussioni in base a built-in euristico. Il pool di thread NET ha due meccanismi principali per iniettare filetti: un meccanismo inedia la prevenzione che aggiunge lavoratore le discussioni se non vede progressi fatti su oggetti in coda e una hillclimbing euristico che cerca di massimizzare il throughput durante l'utilizzo come pochi fili possibili.

L'obiettivo di fame evasione è quello di prevenire stallo. Questo tipo di deadlock può verificarsi quando un thread di lavoro attende una sincronizzazione evento che può essere soddisfatta solo da un elemento di lavoro che è ancora in attesa in coda globali o locali del pool di thread. Se ci fosse un fisso il numero di thread di lavoro, e tutti quei fili erano similmente bloccato, il sistema sarebbe in grado di rendere sempre ulteriori progressi. L'aggiunta di un nuovo thread di lavoro risolve il problema.

Un obiettivo della euristica in salita è quello di migliorare l'utilizzo di nuclei quando thread sono bloccati da I / O o altre condizioni di attesa che stallo del processore. Per impostazione predefinita, il pool di thread gestito ha uno thread di lavoro per core. Se uno di questi thread di lavoro diventa bloccato, c'è una possibilità che un nucleo potrebbe essere sottoutilizzata, a seconda sul carico di lavoro complessivo del computer. La logica di iniezione filo non distingue tra un filo che è bloccato e un filetto che è eseguire un'operazione lunga, intensivo del processore. Perciò, ogni volta che le code globali o locali del pool di thread contengono in attesa elementi di lavoro, elementi di lavoro attivi che richiedono molto tempo per l'esecuzione (più di mezzo secondo) può innescare la creazione di nuovo lavoratore pool di thread fili.

Il pool di thread .NET ha l'opportunità di iniettare discussioni ogni volta una completa l'elemento di lavoro o a 500 millisecondi intervalli, a seconda di quale è più breve. Il pool di thread utilizza questa opportunità per provare ad aggiungere le discussioni (O prendendo via), guidati da feedback da precedenti variazioni il numero di thread. Se l'aggiunta di discussioni sembra aiutare il throughput, il pool di thread aggiunge più; altrimenti, si riduce il numero di thread di lavoro. Questa tecnica è chiamata l'euristica in salita. Pertanto, una delle ragioni per mantenere le singole attività a breve è quello di evitare “Rilevamento di fame”, ma un altro motivo per tenerli a breve è quello di dare il pool di thread più opportunità per migliorare il throughput regolando il numero di thread. Più breve è la durata dei singoli compiti, il più delle volte il pool di thread in grado di misurare il throughput e regolare il numero di thread di conseguenza.

Per rendere questo concreto, consideriamo un esempio estremo. supporre che si dispone di una simulazione finanziaria complesso con 500 processor-intensive operazioni, ognuno dei quali richiede pochi minuti dieci in media completare. Se si creano le attività di primo livello nella coda globale per ogni di queste operazioni, troverete che after circa cinque minuti la pool di thread crescerà a 500 thread di lavoro. La ragione è che il pool di thread vede tutti i compiti come bloccato e inizia ad aggiungere nuove fili al tasso di circa due fili al secondo.

Cosa c'è di sbagliato con 500 thread di lavoro? In linea di principio, nulla, se si dispone di 500 core per loro di utilizzare e grandi quantità di sistema memoria. In realtà, questa è la visione a lungo termine del calcolo parallelo. Tuttavia, se non si dispone di così tanti core sul vostro computer, si è in una situazione in cui molti thread sono in competizione per intervalli di tempo. Questo situazione è noto come oversubscription processore. permettere molti filetti intensivo del processore di competere per volta su un singolo core aggiunge contesto ambientale commutazione che può ridurre drasticamente sistema complessivo il throughput. Anche se non si esaurisce la memoria, le prestazioni in questo situazione può essere molto, molto peggio che in computazione sequenziale. (Ogni interruttore contesto dura tra 6.000 e 8.000 cicli del processore.) Il costo di commutazione di contesto non è l'unica fonte di overhead. Un thread gestito NET consuma circa un megabyte di pila spazio, indipendentemente dal fatto che lo spazio è utilizzato per le funzioni attualmente in esecuzione. Ci vogliono circa 200.000 cicli di CPU per creare un nuovo filo, e circa 100.000 cicli di andare in pensione un filo. Si tratta di operazioni costose.

Fino a quando il lavoro non ogni ripresa minuti, il pool di thread di algoritmo in salita alla fine rendersi conto che ha troppe discussioni e posteriore di propria iniziativa. Tuttavia, se si dispone di compiti che occupano un thread di lavoro per molti secondi o minuti o ore, che getterà via euristica del pool di thread, ea quel punto si dovrebbe prendere in considerazione un'alternativa.

La prima opzione è quella di scomporre l'applicazione in breve compiti che abbastanza veloce completa per il pool di thread di successo controllare il numero di filetti per rendimento ottimale. Una seconda possibilità è quella di implementare il proprio task scheduler oggetto che non esegue l'iniezione thread. Se le attività sono di lunga la durata, non hai bisogno di un programmatore di operazione altamente ottimizzato a causa il costo di schedulazione sarà trascurabile rispetto all'esecuzione tempo del compito. programma per sviluppatori MSDN ha un esempio di implementazione scheduler semplice compito che limita il massimo grado di concorrenza. Per ulteriori informazioni, consultare la sezione “Approfondimenti” alla fine di questo capitolo.

Come ultima risorsa, è possibile utilizzare il metodo SetMaxThreads a configurare la classe ThreadPool con un limite superiore per il numero di thread di lavoro, generalmente pari al numero di nuclei (questo è il Proprietà Environment.ProcessorCount). Questo limite superiore vale per l'intero processo, compresi tutti AppDomain ".

Altri suggerimenti

La risposta breve è:. No

Internamente, il TPL utilizza lo standard ThreadPool per programmare i propri compiti. Così si sta effettivamente chiedendo se il ThreadPool prende carico della macchina in considerazione e non è così. L'unica cosa che limita il numero di task in esecuzione contemporaneamente è il numero di thread nel pool di thread, non altro.

E 'possibile avere i processi esterni riferire alla propria applicazione una volta che sono pronti? In questo caso non c'è bisogno di aspettare per loro (le discussioni mantenendo occupati).

eseguito un test utilizzando TPL / ThreadPool per programmare un gran numero di compiti che fanno giri in loop. Utilizzando un'applicazione esterna Ho caricato uno dei nuclei 100% utilizzando proc affinità. Il numero di attività attive mai diminuito.

Ancora meglio, ho corso più istanze dello stesso per la CPU .NET TPL abilitato app. Il numero di thread per tutte le applicazioni è stato lo stesso, e non è mai andato al di sotto del numero di core, anche se la mia macchina era a malapena utilizzabile.

Quindi, a parte la teoria, TPL utilizza il numero di core disponibili, ma mai i controlli sulla loro carico effettivo. Una scarsa applicazione, a mio parere.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top