2011-11-07 20 views
5

Ho un problema molto strano con OpenMP nel mio codice C++:OpenMP - Easy Loop, ma ancora infinito?

void update(double *source, double *target, int n) 
{ 
    target[0] = source[0]; 
    target[n-1] = source[n-1]; 
    #pragma omp parallel for 
    for(int i = 1; i < n-1; ++i) 
     target[i] = (1.0/3.0) * (source[i-1] + source[i] + source[i+1]); 
} 

sia di origine e di destinazione sono doppie array con n elementi. Il codice funziona bene quando lo si utilizza senza OpenMP. Ma non appena uso il pragma, il codice sembra rimanere bloccato in questo ciclo. Il fatto è: non ho assolutamente IDEA perché. Spero che chiunque mi può aiutare

+0

Compila e funzionare bene con GCC 4.6.1. Quale compilatore usi? –

+0

È possibile aggiungere dettagli sul compilatore e sul sistema operativo? – talonmies

+0

gcc 4.2.1 su MacOS Snow Leopard, precisamente: 686-apple-darwin10-g ++ - 4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (punto 3) – Chris

risposta

2

Quanto è grande n?

La pianificazione predefinita per una direttiva OpenMP parallel for è specifica dell'implementazione. Sembra che in GOMP (l'implementazione OpenMP utilizzata da gcc), l'impostazione predefinita sia (dynamic,1) in base allo documentation here. Ciò significa che ogni thread sta accedendo (alle posizioni i-1 e i+1) alle posizioni caricate dai thread adiacenti, il che potrebbe comportare un utilizzo della cache insufficiente. Nelle moderne architetture CPU, le operazioni di stencil come questa sono spesso legate alla memoria e sensibili alla memorizzazione nella cache. Si potrebbe provare a specificare una pianificazione con pezzi più grandi, per esempio con:

#pragma omp parallel for schedule(dynamic,1024) 

sto usando solo 1024 qui come esempio. In pratica, dovresti sperimentare per trovare il fattore di chunking ottimale (o cercare sistematicamente con uno sweep di parametri, un processo spesso chiamato "auto-tuning"). Oppure puoi scegliere un valore basato in teoria, ad esempio derivandolo dalla dimensione della cache L1 o L2 della tua CPU.

Oppure si potrebbe provare la programmazione statica, poiché la quantità di calcolo all'interno del ciclo for è uniforme tra i thread e il sovraccarico dello scheduler dinamico potrebbe causare un collo di bottiglia. Se si specifica

#pragma omp parallel for schedule(static) 

senza una dimensione del blocco, quindi ogni thread viene assegnato un unico pezzo di circa la stessa dimensione.

Infine, si consiglia di fissare i thread OpenMP ai propri core CPU. È possibile farlo utilizzando la variabile di ambiente GOMP_CPU_AFFINITY.

Edit:

Stavo solo giocando intorno con il seguente programma di test compilato con gcc 4.2.1, e penso che la documentazione ho linkato sopra non è corretto. Sembra che GOMP sia impostato su schedule(static).

#include <stdio.h> 
#include <omp.h> 

int main(int argc, char** argv) 
{ 
    int i; 
    #pragma omp parallel for 
    for (i=0; i<15; i++) { 
     int id = omp_get_thread_num(); 
     printf("%d assigned to thread %d\n", i, id); 
    } 
} 

E l'uscita con due fili è:

$ ./test_sched | sort -n 
0 assigned to thread 0 
1 assigned to thread 0 
2 assigned to thread 0 
3 assigned to thread 0 
4 assigned to thread 0 
5 assigned to thread 0 
6 assigned to thread 0 
7 assigned to thread 0 
8 assigned to thread 1 
9 assigned to thread 1 
10 assigned to thread 1 
11 assigned to thread 1 
12 assigned to thread 1 
13 assigned to thread 1 
14 assigned to thread 1 
+0

L'impostazione predefinita quando non è definita alcuna pianificazione dipende effettivamente dall'implementazione, ma AFAIK di solito è 'statico'. Il link GOMP che suggerisce '(dynamic, 1)' è il default in realtà si riferisce al caso in cui 'schedule (runtime)' è usato. – eran