2012-11-17 6 views
7

Nel primo blocco, ho appena creato attività senza direttiva parallela, mentre nel secondo blocco, ho usato direttiva parallela e direttiva singola che è un modo comune che ho visto sui giornali. Mi chiedo qual è la differenza tra loro? A proposito, conosco il significato fondamentale di queste direttive.Attività OpenMP con e senza parallelo

Il codice nel mio commento:

void traverse(node *root) 
{ 
    if (root->left) 
    { 
     #pragma omp task 
     traverse(root->left); 
    } 
    if (root->right) 
    { 
     #pragma omp task 
     traverse(root->right); 
    } 
    process(root); 
} 

risposta

12

La differenza è che nel primo blocco si è non davvero la creazione di qualsiasi attività in quanto il blocco di per sé non è nidificato (né sintatticamente né lessicale) all'interno di una attiva regione parallela. Nel secondo blocco il costrutto task è nidificato sinteticamente all'interno della regione parallel e farebbe la coda delle attività esplicite se la regione è attiva al momento dell'esecuzione (una regione parallela attiva è quella che viene eseguita con un gruppo di più thread). Il nesting lessicale è meno ovvio. Osservare il seguente esempio:

void foo(void) 
{ 
    int i; 

    for (i = 0; i < 10; i++) 
     #pragma omp task 
     bar(); 
} 

int main(void) 
{ 
    foo(); 

    #pragma omp parallel num_threads(4) 
    { 
     #pragma omp single 
     foo(); 
    } 

    return 0; 
} 

La prima chiamata a foo() accade all'esterno di qualsiasi regioni parallele. Quindi la direttiva task non esegue (quasi) nulla e tutte le chiamate a bar() avvengono in serie. La seconda chiamata a foo() proviene dall'interno della regione parallela e quindi verranno generate nuove attività all'interno di foo(). La regione parallel è attiva poiché il numero di thread è stato corretto su 4 dalla clausola num_threads(4).

Questo diverso comportamento delle direttive OpenMP è una funzionalità di progettazione. L'idea principale è quella di essere in grado di scrivere codice che possa essere eseguito sia in serie che in parallelo.

Ancora la presenza del costrutto task in foo() esegue la trasformazione del codice, ad es. foo() si trasforma in qualcosa di simile:

void foo_omp_fn_1(void *omp_data) 
{ 
    bar(); 
} 

void foo(void) 
{ 
    int i; 

    for (i = 0; i < 10; i++) 
     OMP_make_task(foo_omp_fn_1, NULL); 
} 

Qui OMP_make_task() è una funzione ipotetica (non disponibili al pubblico) dalla libreria di supporto OpenMP che mette in coda una chiamata alla funzione, fornito come primo argomento. Se OMP_make_task() rileva che funziona al di fuori di un'area parallela attiva, chiamerebbe semplicemente foo_omp_fn_1(). Ciò aggiunge un sovraccarico alla chiamata a bar() nel caso seriale. Invece di main -> foo -> bar, la chiamata va come main -> foo -> OMP_make_task -> foo_omp_fn_1 -> bar. L'implicazione di questo è l'esecuzione del codice seriale più lenta.

questo è ancora più evidente illustrata con la direttiva worksharing:

void foo(void) 
{ 
    int i; 

    #pragma omp for 
    for (i = 0; i < 12; i++) 
     bar(); 
} 

int main(void) 
{ 
    foo(); 

    #pragma omp parallel num_threads(4) 
    { 
     foo(); 
    } 

    return 0; 
} 

La prima chiamata a foo() correrebbe il circuito in serie. La seconda chiamata distribuiva le 12 iterazioni tra i 4 thread, cioè ogni thread eseguiva solo 3 iter. Ancora una volta, per ottenere questo risultato viene utilizzata una magia di trasformazione del codice e il ciclo seriale verrebbe eseguito più lentamente rispetto a quando non era presente #pragma omp for in foo().

La lezione qui è di non aggiungere mai costrutti OpenMP dove non sono realmente necessari.

+0

+1 Risposta piacevole. – dreamcrash

+0

Sembra che ho commesso un errore nell'utilizzo del compito.Questo problema è sorto perché ho visto un codice di attraversare ricorsivamente l'albero con solo "task" come ho aggiunto nella domanda. Immagino che ci debbano essere "paralleli" e "singoli" che racchiudano la funzione trasversale dove viene chiamata. Mille grazie per la tua risposta sincera. –

+0

@AnnieKim, sì, la funzione 'traverse()' come mostrato nella domanda attraverserebbe l'albero in parallelo se chiamata dall'interno di una regione 'parallela 'attiva e in serie altrimenti. Questa è la bellezza di OpenMP :) (nonostante l'overhead aggiunto) –