wielowątkowość a losowy wynik działania aplikacji

0

Cześć, prośba o pomoc gdzie powinienem ustawić jakieś sekcje krytyczne lub jakoś inaczej to zabezpieczyć, by wynik był dobry. Obecnie jest z tym różnie, raz jest poprawny a raz błędny. :( Funkcja iloczyn mnoży macierze.

int iloczyn(int A[][S], int B[][S], int r1, int c1, int r2, int c2)
{  
    int i, j, k, sum, C[S][S], a=7;

   #pragma omp parallel private(i,j) 
   { 
       for(i=0; i<r1; ++i)
          for(j=0; j<c2; ++j)
          {
             C[i][j]=0;
          }
          
  #pragma omp parallel for schedule(static,2) private(i,j,k)  
       for(i=0; i<r1; ++i)
          for(j=0; j<c2; ++j)
             for(k=0; k<c1; ++k)
            {
               C[i][j]+=A[i][k]*B[k][j];
            }
        } 
    
       wypiszA(C, r1, c2); //wypisuje wynik
}

0

Masz chyba źle podomykane klamry. Zamknij pierwszą klamrę przed drugim #pragma.

0
GutekSan napisał(a):

Masz chyba źle podomykane klamry. Zamknij pierwszą klamrę przed drugim #pragma.

A to nie powinno być w jednej? Myślałem, że trzeba zrobić w jednej i dodać potem sekcje zabezpieczające: critical lub reduction. Bo jak mam to w dwóch pragma to to wtedy dwa razy uruchamiają się wątki ?? Albo ja to źle to ogarniam?

0
marnit napisał(a):
GutekSan napisał(a):

Masz chyba źle podomykane klamry. Zamknij pierwszą klamrę przed drugim #pragma.

A to nie powinno być w jednej? Myślałem, że trzeba zrobić w jednej i dodać potem sekcje zabezpieczające: critical lub reduction. Bo jak mam to w dwóch pragma to to wtedy dwa razy uruchamiają się wątki ?? Albo ja to źle to ogarniam?

W obecnym kodzie linijka:

   #pragma omp parallel private(i,j) 

dokonuje podziału wątku głównego na pewną liczbę wątków. Wszystko co dzieje się od klamry pomiędzy liniami 6 i 23 dzieje się równolegle. Czyli nic nie zyskujesz, bo praca nie jest dzielona, tylko wykonywana wielokrotnie.

Linia:

#pragma omp parallel for schedule(static,2) private(i,j,k)  

Dokonuje ponownego podziału każdego wątku na 2 i podzielenia pracy w następującej po niej pętli for.

Pamiętaj, że:

#pragma omp parallel for
for(int i = 1; i < 100; ++i)
{
   ...
}

jest równoważne

#pragma omp parallel
{ 
    #pragma omp for
    for(int i = 1; i < 100; ++i)
    {
        ...
    }
}

zwróć uwagę na brak parallel w drugiej sekcji #pragma.

Ja zrobiłbym pierwszą sekcję, w której zeruję macierz C

#pragma omp parallel for private(i,j) 
{ 
   for(i=0; i<r1; ++i)
      for(j=0; j<c2; ++j)
      {
         C[i][j]=0;
      }
}

To powinno rozdzielić zewnętrzną iteracje zewnętrznej pętli for pomiędzy wątkami, i wydaje mi się, że sekcje krytyczne nie będą potrzebne, bo każdy wątek będzie próbował zapisać tylko w komórkach o swoim indeksie i.

Podobnie tu:

#pragma omp parallel for schedule(static,2) private(i,j,k) 
       for(i=0; i<r1; ++i)
          for(j=0; j<c2; ++j)
            for(k=0; k<c1; ++k)
            {
               C[i][j]+=A[i][k]*B[k][j];
            }

Wydaje mi się, że też obejdzie się bez sekcji krytycznych, bo każdy wątek wpisuje do swoich komórek. Usunąłem jedną klamrę, dzięki temu wypiszA wykona się tylko raz.

Tak w ogóle to nie musiałbyś deklarować prywatności i,j,k gdybyś deklarował je wewnątrz for.

0
marnit napisał(a):

Zacząłem to testować i jak dam w pierwszym #pragma tak jak napisałeś to wyrzuca błąd error: for statement expected before ‘{’ token

Spróbuj tak:

#pragma omp parallel for private(i,j) 
for(i=0; i<r1; ++i)
    for(j=0; j<c2; ++j)
   {
      C[i][j]=0;
   }

Może pierwsze klamry były niepotrzebne. Chodziło mi o to, żeby były sparowane.

0

To jeszcze jedno pytanko mam.
Mnożenie macierzy muszę zrównoleglić w każdej możliwej opcji. Czyli 1,2 i 3 pętla, 1 i 2 pętla, 1 i 3 pętla.

I zastanawiam się czy to tak być powinno (dla kombinacji 1,2,3). No bo to działa tak, że dopiero w ostatniej pętli się wszystko dzieje więc chyba nie za bardzo coś przyśpieszę?

#pragma omp parallel for schedule(static,2) private(i) 
    for(i=0; i<r1; ++i)
    #pragma omp parallel for schedule(static,2) private(j) 
        for(j=0; j<c2; ++j)
        #pragma omp parallel for schedule(static,2) private(k) 
            for(k=0; k<c1; ++k)
            {
               C[i][j]+=A[i][k]*B[k][j];
            }
0
marnit napisał(a):

To jeszcze jedno pytanko mam.
Mnożenie macierzy muszę zrównoleglić w każdej możliwej opcji. Czyli 1,2 i 3 pętla, 1 i 2 pętla, 1 i 3 pętla.

I zastanawiam się czy to tak być powinno (dla kombinacji 1,2,3). No bo to działa tak, że dopiero w ostatniej pętli się wszystko dzieje więc chyba nie za bardzo coś przyśpieszę?

#pragma omp parallel for schedule(static,2) private(i) 
    for(i=0; i<r1; ++i)
    #pragma omp parallel for schedule(static,2) private(j) 
        for(j=0; j<c2; ++j)
        #pragma omp parallel for schedule(static,2) private(k) 
            for(k=0; k<c1; ++k)
            {
               C[i][j]+=A[i][k]*B[k][j];
            }

W ogóle moje doświadczenia są takie, że zagnieżdżenia w OpenMP są słabe - proces działał wolniej ze zrównolegloną drugą pętlą niż bez zrównoleglenia. Chyba, że zastosowało się inny mechanizm wielowątkowości wewnątrz, np. jakiś std::thread, albo thready windowsowe. Nie wiem czemu tak było i czy coś się nie zmieniło.

Jak dla mnie sens ma tylko zrównoleglenie dla zmiennych i i j, wtedy każdy wątek może pracować nad swoją komórką, więc jakieś przyśpieszenie powinno być. Jak dodasz zrównoleglenie dla k, to trzeba wykonać atomowo niemal każdą operację odczytu i zapisu w wyrażeniu:

C[i][j]+=A[i][k]*B[k][j];

Tak w ogóle to polecam Ci zapoznać się z tym:
https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#shared-memory

Tu masz podobny problem, tylko że mnożysz równolegle macierz na GPU, ale pokazano jak napisać kernele, żeby było efektywnie. Wnioski powinny być te same w kontekście OpenMP.

1 użytkowników online, w tym zalogowanych: 0, gości: 1