2014-06-23 15 views
14

Sto sviluppando un codice di moltiplicazione della matrice denso di grandi dimensioni. Quando registro il codice, a volte ottiene circa il 75% dei picchi del mio sistema a quattro core e altre volte il 36% circa. L'efficienza non cambia tra le esecuzioni del codice. Inizia al 75% e continua con quell'efficienza o inizia al 36% e continua con quell'efficienza.Prestazioni scarse a causa di hyper-threading con OpenMP: come eseguire il binding dei thread ai core

Ho tracciato il problema fino all'hyper-threading e il fatto che ho impostato il numero di thread su quattro invece che su quello predefinito. Quando disattivo l'hyper-threading nel BIOS ottengo un'efficienza del 75% costante (o almeno non vedo mai il drastico calo del 36%).

Prima di chiamare qualsiasi codice parallelo faccio omp_set_num_threads(4). Ho anche provato export OMP_NUM_THREADS=4 prima di eseguire il mio codice ma sembra essere equivalente.

Non voglio disabilitare l'hyper-threading nel BIOS. Penso di aver bisogno di legare i quattro fili ai quattro core. Ho testato alcuni casi diversi di GOMP_CPU_AFFINITY ma finora ho ancora il problema che l'efficienza è del 36% a volte. Che cos'è il mapping con hyper-threading e core? Es. thread 0 e thread 1 corrispondono allo stesso core e thread 2 e thread 3 a un altro core?

Come si possono associare i thread a ciascun core senza migrazione di thread in modo da non dover disabilitare l'hyper-threading nel BIOS? Forse ho bisogno di esaminare usando sched_setaffinity?

Alcuni dettagli del mio sistema attuale: kernel Linux 3.13, GCC 4.8, Intel Xeon E5-1620 (quattro core fisici, otto hyper-thread).

Edit: Questo sembra funzionare bene finora

export GOMP_CPU_AFFINITY="0 1 2 3 4 5 6 7" 

o

export GOMP_CPU_AFFINITY="0-7" 

Edit: Questo sembra anche funzionare bene

export OMP_PROC_BIND=true 

Edit: These options funziona anche bene (gemm è il nome della mia eseguibile)

numactl -C 0,1,2,3 ./gemm 

e

taskset -c 0,1,2,3 ./gemm 
+0

Poiché export GOMP_CPU_AFFINITY = "0 1 2 3 4 5 6 7" dà buoni risultati, suppongo che significhi che il thread 0 e 4 sono core 0, thread 1 e 5 core2, ... cioè i thread sono assegnati come elettroni in orbitali. Prima riempie ogni core (thread 0-3) e quando tutti i core hanno un thread torna indietro e assegna i thread rimanenti allo stesso core (thread 4-7). –

+1

Entrambi 'hwloc-ls' dalla libreria hwloc o' cpuinfo' da Intel MPI forniscono informazioni di topologia essenziali sulla macchina, ad es. mappatura dei numeri logici della CPU in core/thread fisici. La numerazione dipende dal BIOS, ma nella mia esperienza la maggior parte dei casi ha riguardato il fatto che gli hyperthread sono ciclicati in un "ciclo esterno". Inoltre, puoi usare la notazione di stenografia '" 0-7 "'. –

+0

@HristoIliev, per la portabilità sembra il modo giusto per farlo è utilizzare OMP_PLACES, ad es. 'Esporta OMP_PLACES = core da OpenMP4.0. Sui sistemi AMD ogni modulo ha solo una FPU ma ottiene due thread e penso che sia assegnato linearmente https://stackoverflow.com/questions/19780554/what-limits-scaling-in-this-simple-openmp-program/19871592#19871592 così facendo GOMP_CPU_AFFINITY = "0-7" non funzionerà, penso. In realtà, OMP_PROC_BIND = true potrebbe andare bene anche allora. Forse è la soluzione migliore. –

risposta

3

Questa non è una risposta diretta alla sua domanda, ma forse vale la pena guardare in a: apparently, hyperthreading can cause your cache to thrash. Hai provato a controllare valgrind per vedere che tipo di problema causa il tuo problema? Ci potrebbe essere una soluzione rapida da allocare alcuni junk nella parte superiore dello stack di ogni thread in modo che i thread non finiscano per buttare a vicenda le code della cache.

It looks like your CPU is 4-way set associative quindi non è assurdo pensare che, attraverso 8 thread, si possa finire con alcuni accessi davvero sfortunatamente allineati. Se le tue matrici sono allineate su un multiplo della dimensione della tua cache, e se hai avuto coppie di thread che accedono a una cache multipla a parte, qualsiasi lettura accidentale da parte di un terzo thread sarebbe sufficiente per iniziare a causare errori di conflitto.

Per un test rapido: se modifichi le matrici di input con qualcosa che non è un multiplo della dimensione della cache (quindi non sono più allineate su un confine) e i tuoi problemi scompaiono, allora ci sono buone probabilità che tu si sta occupando di missili di conflitto.

+0

Dovrei usare valgrind ad un certo punto (non l'ho mai usato). Ma il fatto che l'hyper-threading peggiori le cose non è sorprendente nel mio codice. L'hyper-threading è utile per il codice non ottimizzato. Inoltre, quando eseguo GEMM in MKL utilizza quattro thread sul mio sistema e non otto. Per determinati codici ottimizzati per hihgly, l'hyper-threading fornisce risultati peggiori. –