2012-11-17 24 views
7
Node *head = &node1; 
while (head) 
{ 
    #pragma omp task 
     cout<<head->value<<endl; 
    head = head->next; 
} 

#pragma omp parallel 
{ 
    #pragma omp single 
    { 
     Node *head = &node1; 
     while (head) 
     { 
      #pragma omp task 
       cout<<head->value<<endl; 
      head = head->next; 
     } 
    } 
} 

w pierwszym bloku, po prostu stworzony zadania bez dyrektywy równoległym, podczas gdy w drugim bloku, użyłem dyrektywę równoległego i jedną dyrektywą, która jest powszechnym sposobem widziałem w gazetach. Zastanawiam się, jaka jest między nimi różnica? BTW, znam podstawowe znaczenie tych dyrektyw.zadanie OpenMP i bez równoległego

Kod w moim komentarzu:

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

Odpowiedz

12

Różnica polega na tym, że w pierwszym bloku jesteś nie naprawdę tworzenie zadań, ponieważ sam blok nie jest zagnieżdżona (ani składniowo ani leksykalnie) wewnątrz aktywnym region równoległy. W drugim bloku konstrukcja task jest syntaktycznie zagnieżdżona w regionie parallel i może kolejkować jawne zadania, jeśli region stanie się aktywny w czasie wykonywania (aktywny region równoległy to taki, który wykonuje się w zespole złożonym z więcej niż jednego wątku). Zagnieżdżanie leksykalne jest mniej oczywiste. Należy zwrócić uwagę na poniższy przykład:

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; 
} 

Pierwsze wezwanie do foo() stanie całkowicie poza równoległych obszarach. Stąd dyrektywa task robi (prawie) nic i wszystkie połączenia z bar() odbywają się seryjnie. Drugie wywołanie foo() pochodzi z wnętrza równoległego regionu, a zatem nowe zadania zostaną wygenerowane wewnątrz foo(). Region parallel jest aktywny, ponieważ liczba wątków została ustalona na 4 przez klauzulę num_threads(4).

To inne zachowanie dyrektyw OpenMP to funkcja projektowania. Główną ideą jest możliwość napisania kodu, który mógłby wykonywać zarówno jako szeregowy, jak i równoległy.

Wciąż obecność konstruktu task w foo() powoduje pewną transformację kodu, np. foo() przekształca się w coś w rodzaju:

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); 
} 

Tutaj OMP_make_task() jest hipotetycznym (nie są publicznie dostępne) funkcja z biblioteki wsparcia OpenMP że kolejkuje wywołanie funkcji, dostarczony jako pierwszy argument. Jeśli OMP_make_task() wykryje, że działa poza aktywnym regionem równoległym, zamiast tego po prostu zadzwoni pod numer foo_omp_fn_1(). To dodaje trochę kosztów do połączenia z numerem bar() w przypadku szeregowym. Zamiast main -> foo -> bar, wywołanie wygląda następująco: main -> foo -> OMP_make_task -> foo_omp_fn_1 -> bar. Implikacją tego jest wolniejsze wykonanie kodu szeregowego.

ta jest jeszcze bardziej oczywisty ilustrowane dyrektywy podziału pracy:

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; 
} 

Pierwsze wywołanie foo() by uruchomić pętlę w serial. Drugie wywołanie rozprowadzałoby 12 iteracji między 4 wątkami, tj. Każdy wątek wykonywałby tylko 3 iteracje. Ponownie, do osiągnięcia tego celu użyta jest pewna magia transformacji kodu, a pętla szeregowa działałaby wolniej, niż gdyby #pragma omp for nie było obecne w .

Lekcja tutaj polega na tym, aby nigdy nie dodawać konstrukcji OpenMP tam, gdzie nie są one naprawdę konieczne.

+0

+1 Dobra odpowiedź. – dreamcrash

+0

Wygląda na to, że popełniłem błąd w korzystaniu z zadania.Ten problem pojawił się, ponieważ widziałem kod rekurencyjnie przechodzący przez drzewo z "tylko zadaniem", jak dodałem w pytaniu. Sądzę, że powinien istnieć "równoległy" i "pojedynczy" obejmujący funkcję trawersu tam, gdzie się ją nazywa. Wielkie dzięki dla Twojej szczerej odpowiedzi. –

+0

@AnnieKim, tak, funkcja 'traverse()', jak pokazano w twoim pytaniu, będzie poruszać się równolegle do drzewa, jeśli zostanie wywołana z aktywnego regionu 'równoległego' i szeregowo inaczej. To jest piękno OpenMP :) (pomimo dodanego narzutów) –