2012-01-17 6 views
20

Ho un vector<int> con 10.000.000 (10 milioni) elementi e che la mia workstation ha quattro core. Esiste una funzione, denominata ThrFunc, che opera su un numero intero. Supponiamo che il tempo di esecuzione per ThrFunc per ogni numero intero nel vector<int> sia all'incirca lo stesso.Qual è il modo migliore per determinare il numero di thread da attivare in una macchina con n core? (C++)

Come si determina il numero ottimale di thread da attivare? La risposta è semplice come il numero di elementi diviso per il numero di core? O c'è un calcolo più sottile?

Editing per fornire informazioni aggiuntive

  • Nessuna necessità per il blocco; Ogni funzione esigenze invocazione sola lettura sola accesso
+2

Sarebbe un sacco di discussioni! Penso che tu intenda il numero di core, giusto? – dasblinkenlight

+0

Supponendo che tutte le operazioni sugli interi possano avvenire completamente simultaneamente, è sufficiente dividere per il numero di core. È molto più difficile valutare quando il lavoro non può essere svolto contemporaneamente. –

+1

Questi thread eseguono qualsiasi I/O (bloccante) o qualsiasi operazione di blocco come le comunicazioni di rete o il database? Se no, allora è probabile che il numero ottimale di core sia N. Nel tuo caso, 4. Altrimenti, vale la pena sperimentare 2N o 3N - mentre un thread sta facendo I/O, un altro thread può funzionare. – selbie

risposta

23

Il numero ottimale di thread possa essere il numero di nuclei a macchina o il numero di nuclei volte due.

In termini più astratti, si desidera il massimo throughput possibile. Ottenere il throughput più elevato richiede il minor numero di punti di conflitto tra i thread (poiché il problema originale è banalmente parallelizzabile). Il numero di punti di conflitto è probabilmente il numero di thread che condividono un core o il doppio, dal momento che un core può eseguire uno o due thread logici (due con hyperthreading).

Se il carico di lavoro utilizza una risorsa di cui sono disponibili meno di quattro (ALU su Bulldozer? Accesso al disco fisso?), Il numero di thread da creare sarà limitato.

Il modo migliore per trovare la risposta corretta è, con tutte le domande sull'hardware, testare e scoprire.

+0

Grazie per la risposta. Accettato. – Shredderroy

+0

Se i tuoi calcoli useranno gli stessi dati su ogni thread, probabilmente sarebbe meglio ignorare l'hyperthreading, o addirittura disabilitarlo completamente. I dati per entrambi i thread saranno probabilmente memorizzati nella cache abbastanza rapidamente, quindi nessuno dei due si fermerà, quindi HT non avrà mai il tempo di fare effettivamente qualcosa. –

+0

+1 Ottimo consiglio. – Tudor

4

Supponendo che ThrFunc sia collegato alla CPU, si desidera probabilmente un thread per core e dividere gli elementi tra di essi.

Se esiste un elemento I/O per la funzione, la risposta è più complessa, poiché è possibile avere uno o più thread per core in attesa di I/O mentre un altro è in esecuzione. Fai dei test e vedi cosa succede.

+0

Supponendo che tu non voglia fare altro con la tua macchina, ovviamente :-) – paxdiablo

+0

@paxdiablo - Ovviamente, anche se il sistema operativo darà un po 'di tempo alla CPU per altri processi. –

2

Il numero ottimale di thread deve essere uguale al numero di core, in cui la capacità di calcolo di ciascun core sarà pienamente utilizzata, se il calcolo su ciascun elemento è indipendente.

11

Borealid's answer include test e scoprire, che è impossibile da battere come consiglio.

Ma forse c'è ancora di più da testare su ciò che si potrebbe pensare: si desidera che i thread evitino la contesa per i dati laddove possibile. Se i dati sono interamente di sola lettura, è possibile che si ottengano le migliori prestazioni se i thread accedono a dati "simili", assicurandosi di scorrere i dati in piccoli blocchi alla volta, in modo che ogni thread acceda ai dati da same pages over and over again. Se i dati sono completamente di sola lettura, non c'è alcun problema se ogni core riceve la propria copia delle linee della cache. (Anche se questo potrebbe non sfruttare al massimo la cache di ogni core.)

Se i dati sono in qualche modo modificati, allora vedrete miglioramenti significativi delle prestazioni se tenete i fili di distanza uno dall'altro, da molto .La maggior parte delle cache memorizza i dati lungo cache lines e si desidera disperatamente mantenere ogni cache line from bouncing among CPUs per buone prestazioni. In tal caso, potresti voler mantenere i diversi thread in esecuzione su dati che sono effettivamente distanti tra loro per evitare di incontrarsi mai l'uno con l'altro.

Quindi: se si stanno aggiornando i dati mentre si lavora su di esso, si consiglia di avere N o 2 * N thread di esecuzione (per N core), iniziando con SIZE/N * M come punto di partenza, per i thread da 0 a M. (0, 1000, 2000, 3000, per quattro thread e 4000 oggetti dati.) Ciò ti darà la migliore possibilità di alimentare diverse linee di cache per ogni core e di consentire agli aggiornamenti di continuare senza il rimbalzo della linea cache:

+--------------+---------------+--------------+---------------+--- ... 
| first thread | second thread | third thread | fourth thread | first ... 
+--------------+---------------+--------------+---------------+--- ... 

Se siete non aggiornamento dei dati mentre si lavora su di esso, si potrebbe desiderare di iniziare a N o 2 * N fili di esecuzione (per N core), li iniziando con 0, 1, 2, 3 ecc. e spostando ciascuno avanti di N o 2 * N elementi con ogni iterazione. Ciò consentirà al sistema cache di recuperare ogni pagina dalla memoria una volta, popolando le cache della CPU con dati quasi identici, e si spera che ogni core venga riempito con nuovi dati.

+-----------------------------------------------------+ 
| 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 ... | 
+-----------------------------------------------------+ 

ho anche consigliamo di utilizzare sched_setaffinity(2) direttamente nel codice per forza i diversi thread per i propri processori. Nella mia esperienza, Linux punta a keep each thread on its original processor così tanto che non migra le attività su altri core altrimenti inutilizzati.

+0

Grazie mille per le tue spiegazioni. Informazioni sull'ultima frase: ha importanza se sono su Windows 7 o Windows Server 2008 R2? – Shredderroy

+0

@Shredderroy: è importante che 'sched_setaffinity (2)' sia Unix (o è Linux?) Specifico, su Windows sarà una funzione diversa. –

+0

@Shredderroy, Matthieu è corretto; Windows potrebbe comunque svolgere un lavoro di bilanciamento del lavoro migliore tra le CPU rispetto a Linux. Test test test :) – sarnold

1

Sono d'accordo con i commenti precedenti. È necessario eseguire i test per determinare quale numero produce il rendimento migliore. Tuttavia, questo produrrà solo le migliori prestazioni per il particolare sistema che stai ottimizzando. Nella maggior parte degli scenari, il tuo programma verrà eseguito su macchine di altre persone, sull'architettura di cui non dovresti fare troppe ipotesi.

Un buon modo per determinare numericamente il numero di fili per iniziare sarebbe usare

std::thread::hardware_concurrency() 

Questo fa parte del C++ 11 e dovrebbe produrre il numero di core logici nel sistema attuale. Per core logici si intende il numero fisico di core, nel caso in cui il processore non supporti i thread hardware (ad esempio HyperThreading) o il numero di thread hardware.

C'è anche una funzione Boost che fa lo stesso, vedere Programmatically find the number of cores on a machine.

0

Il numero ottimale di core (thread) sarà determinato dal raggiungimento della saturazione del sistema di memoria (cache e RAM). Un altro fattore che potrebbe entrare in gioco è quello del blocco inter-core (bloccare un'area di memoria che altri core potrebbero voler accedere, aggiornarlo e quindi sbloccarlo) e quanto è efficiente (per quanto tempo è attiva la serratura e quanto spesso è bloccato/sbloccato).

Un single core che esegue un software generico il cui codice e i cui dati non sono ottimizzati per il multi-core si avvicinano alla memoria di saturazione da solo. L'aggiunta di più core, in questo caso, determinerà un'applicazione più lenta.

Quindi, a meno che il tuo codice non ottimizzi notevolmente gli accessi alla memoria, suppongo che la risposta alla tua domanda sia una (1).

Problemi correlati