Differences

This shows you the differences between two versions of the page.

Link to this comparison view

app:laboratoare:01 [2022/10/13 12:27]
127.0.0.1 external edit
app:laboratoare:01 [2022/10/23 14:46] (current)
florin.mihalache
Line 1: Line 1:
-===== Laboratorul ​01. =====+====== Laboratorul ​1 - Introducere în OpenMP ​=====
 +===== Ce învățăm la APP? ===== 
 +La APP vom învăța cum să analizăm o problemă și o soluție a acesteia (mai precis, un program secvențial) și cum am putea să îmbunătățim soluțiile la problema respectivă (adică cum am putea să eficientizăm prin paralelizare soluțiile problemei).
  
 +În cadrul laboratoarelor de APP vom studia despre programarea paralelă (OpenMP, pthreads), despre programarea distribuită (MPI) și despre analiza performanțelor unui program (profiling).
  
 +===== Ce este OpenMP? =====
 +OpenMP reprezintă un API prin care putem paraleliza programe secvențiale scrise în C/C++. Acesta este un API high-level, în sensul că programatorul are o varietate de tool-uri și de opțiuni la dispoziția sa, ele putând fi folosite cu mare ușurință.
 +
 +OpenMP este creat pe baza modelului fork-join, unde avem un thread principal (master), din care se creează alte thread-uri (fork, echivalent cu ''​pthread_create''​ din pthreads), care, împreună cu thread-ul master, execută task-uri în paralel, în cadrul unor zone numite regiuni paralele. După ce task-urile respective sunt terminate, thread-urile forked "​revin"​ în thread-ul principal (join).
 +
 +{{ :​app:​laboratoare:​fork_join.png?​700 |}}
 +
 +====== Includere și compilare ======
 +Pentru a putea folosi OpenMP în cod, trebuie inclusă biblioteca omp.h în cod: ''#​include <​omp.h>''​.
 +
 +Pentru compilare, este necesar un flag, care diferă în funcție de compilator:
 +  * gcc: ''​gcc helloworld.c -o helloworld -fopenmp''​
 +  * SunStudio (pe fep): ''​cc -xopenmp helloworld.c -o helloworld''​
 +
 +===== Sintaxa OpenMP =====
 +Pentru OpenMP, se folosesc directive de compilare de tip pragma pentru a marca blocuri de cod paralelizate și pentru a folosi elemente de sincronizare.
 +
 +Tipar: ''#​pragma omp numele_directivei [clause, ...]''​
 +Exemplu: ''#​pragma omp parallel default(shared) private(beta,​ pi)''​
 +
 +==== Paralelizarea secvențelor de cod ====
 +Pentru ca o bucată de cod să fie executată de mai multe thread-uri, folosim directiva ''#​pragma omp parallel''​ prin care marcăm faptul că acea zonă de cod este executată în paralel de mai multe thread-uri.
 +
 +<code c>
 +#pragma omp parallel
 +{
 + // cod paralelizat
 +}
 +</​code>​
 +
 +Exemplu de folosire:
 +<code c>
 +#include <​stdio.h>​
 +#include <​omp.h>​
 +
 +int main(int argc, char** argv) {
 +    #pragma omp parallel ​
 +    {
 +        int tid = omp_get_thread_num();​
 +        printf("​Hello world from thread number %d\n", tid);
 +    }
 +
 +    #pragma omp parallel ​
 +    {
 +        int tid = omp_get_thread_num();​
 +        printf("​Frumos in anul 4, zice thread-ul %d\n", tid);
 +    }
 +    return 0;
 +}
 +</​code>​
 +
 +De asemenea, putem avea regiuni paralele imbricate:
 +<code c>
 +#include <​stdio.h>​
 +#include <​omp.h>​
 +
 +int main(int argc, char** argv) {
 +    #pragma omp parallel ​
 +    {
 +        printf("​Parallel region thread %d\n", omp_get_thread_num());​
 +        ​
 +        #pragma omp parallel ​
 +        {
 +            printf("​Nested parallel region thread %d\n", omp_get_thread_num());​
 +        }
 +    }
 +    return 0;
 +}
 +</​code>​
 +
 +===== Funcții utile =====
 +Pentru setarea numărului de thread-uri din cadrul programului paralelizat,​ putem să facem în două moduri:
 +  * din linia de comandă: ''​export OMP_NUM_THREADS=8''​
 +  * din cod: ''​omp_set_num_threads(8)''​
 +Dacă dorim să măsurăm timpul de execuție al unei secvențe de cod paralelizat,​ putem folosi ''​omp_get_wtime()''​. Exemplu de folosire:
 +<code c>
 +t1 = omp_get_wtime();​
 +#pragma omp parallel ​
 +{
 +    int tid = omp_get_thread_num();​
 +    printf("​Hello world from thread number %d\n", tid);
 +}
 +t2 = omp_get_wtime();​
 +printf("​Total execution time = %lf\n",​ (t2 - t1));
 +</​code>​
 +
 +Alte funcții utile:
 +  * numărul de threads: ''​omp_get_num_threads()''​
 +  * id-ul thread-ului curent: ''​omp_get_thread_num()''​
 +  * numărul de procesoare: ''​omp_get_num_procs()''​
 +
 +===== Vizibilitatea variabilelor =====
 +Variabilele în cadrul blocurilor paralele pot fi:
 +  * globale - văzute și partajate de toate thread-urile
 +  * private - fiecare thread are variabilele sale private care nu sunt vizibile către alte thread-uri
 +
 +În acest caz avem două clauze pentru context:
 +''​SHARED''​ - variabilă partajată între thread-uri (exemplu: ''​SHARED(c)''​)
 +''​PRIVATE''​ - variabilă văzută doar de thread-ul respectiv în blocul paralelizat (exemplu: ''​PRIVATE(a,​ b)''​)
 +Exemplu:
 +<code c>
 +#include <​stdio.h>​
 +#include <​omp.h>​
 +
 +int main(int argc, char** argv) {
 +    int a = 6, b = 9, c = 10;
 +
 +    #pragma omp parallel private(a,​b) shared(c) ​
 +    {
 +        // privates set the scope of variables
 +        a = 1, b = 2, c = a + b; // cu private(a, b), aceste valori (la a si b) vor fi vizibile doar in acest bloc
 +        int tid = omp_get_thread_num();​
 +        printf("​In parallel block, in thread no %d: %d %d %d\n", tid, a, b, c); // printing 1 2 3
 +    }
 +    printf("​%d %d %d\n", a, b, c); // printing 6 9 3
 +
 +    #pragma omp parallel shared(c) ​
 +    {
 +        a = 1, b = 2, c = a + b;
 +        int tid = omp_get_thread_num();​
 +        printf("​In parallel block, in thread no %d: %d %d %d\n", tid, a, b, c); // printing 1 2 3
 +    }
 +    printf("​%d %d %d\n", a, b, c); // printing 1 2 3
 +
 +    #pragma omp parallel ​
 +    {
 +        a = 1, b = 2, c = a + b;
 +        int tid = omp_get_thread_num();​
 +        printf("​In parallel block, in thread no %d: %d %d %d\n", tid, a, b, c); // printing 1 2 3
 +    }
 +    printf("​%d %d %d\n", a, b, c); // printing 1 2 3
 +
 +    return 0;
 +}
 +</​code>​
 +
 +===== Paralelizarea buclelor =====
 +În OpenMP putem paraleliza buclele de tip for folosind directiva ''#​pragma omp for''​ în cadrul unei zone paralele. În acest fel, iterațiile din for sunt împărțite egal thread-urilor,​ fiecare thread având iterațiile sale din cadrul buclei for.
 +
 +Paralelizarea buclelor poate fi eficientizată folosind directiva ''​SCHEDULE'',​ despre care vom discuta în laboratorul 2.
 +
 +Exemplu de folosire:
 +<code c>
 +#include <​stdio.h>​
 +#include <​omp.h>​
 +
 +int main(int argc, char** argv) {
 +    int i, x[20];
 +    #pragma omp parallel private(i) shared(x) ​
 +    {
 +        #pragma omp for 
 +        for (i = 0; i < 20; i++) {
 +            x[i] = i;
 +            printf("​iteration no. %d | thread no. %d\n", i, omp_get_thread_num());​
 +        }
 +    }
 +
 +    printf("​\n"​);​
 +
 +    // o alta forma, aceeasi functionalitate
 +    #pragma omp parallel for private(i) shared(x) ​
 +    for (i = 0; i < 20; i++) {
 +        x[i] = i;
 +        printf("​iteration no. %d | thread no. %d\n", i, omp_get_thread_num());​
 +    }
 +
 +    return 0;
 +}
 +</​code>​
 +
 +===== Elemente de sincronizare =====
 +În OpenMP avem la dispoziție elemente de sincronizare,​ prin care putem să ne asigurăm faptul că soluția paralelizată funcționează corect, fără probleme în ceea ce privește rezultatele incorecte sau deadlocks.
 +
 +==== Mutex ====
 +Pentru zonele critice, unde avem operații de read-write, folosim directiva #pragma omp critical, care reprezintă un mutex, echivalentul lui ''​pthread_mutex_t''​ din pthreads, care asigură faptul că un singur thread accesează zona critică la un moment dat, thread-ul deținând lock-ul pe zona critică în momentul respectiv, și că celelalte thread-uri care nu au intrat încă în zona critică așteaptă eliberarea lock-ului de către thread-ul aflat în zona critică în acel moment.
 +
 +Exemplu de folosire:
 +<code c>
 +#include <​stdio.h>​
 +#include <​omp.h>​
 +
 +int main (int argc, char** argv) {
 +    int thread_id, sum = 0;
 +    #pragma omp parallel private(thread_id) shared(sum)
 +    {
 +        thread_id = omp_get_thread_num();​
 +        #pragma omp critical
 +        sum += thread_id;
 +    }
 +    printf("​%d",​sum);​
 +    ​
 +    return 0;
 +}
 +</​code>​
 +
 +==== Barieră ====
 +Un alt element de sincronizare reprezintă bariera, care asigură faptul că niciun thread gestionat de barieră nu trece mai departe de aceasta decât atunci cand toate thread-urile gestionate de barieră au ajuns la punctul unde se află bariera.
 +
 +În OpenMP, pentru barieră avem directiva ''#​pragma omp barrier'',​ echivalent cu ''​pthread_barrier_t''​ din pthreads.
 +
 +Exemplu de folosire:
 +<code c>
 +#include <​stdio.h>​
 +#include <​omp.h>​
 +
 +int main (int argc, char** argv) {
 +    #pragma omp parallel ​
 +    {
 +        printf("​First print by %d\n", omp_get_thread_num());​
 +        #pragma omp barrier
 +        printf("​Second print by %d\n", omp_get_thread_num());​
 +    }
 +    ​
 +    return 0;
 +}
 +</​code>​
 +
 +===== Reduction =====
 +''​reduction''​ este o directivă folosită pentru operații de tip reduce / fold pe arrays / colecții sau simple însumări / înmulțiri în cadrul unui loop. Mai precis, elementele dintr-un array sau indecșii unui loop sunt "​acumulați"​ într-o singură variabilă, cu ajutorul unei operații, al cărui semn este precizat.
 +
 +Tipar: ''​reduction(operator_operatie:​variabila_in_care_se_acumuleaza)''​
 +
 +Exemplu de reduction: ''​reduction(+:​sum)'',​ unde se însumează elementele unui array în variabila ''​sum''​
 +
 +Exemplu de folosire de reduction:
 +<code c>
 +int sum = 0;
 +
 +#pragma omp parallel for reduction(+:​sum) private(i)
 +for (i = 1; i <= num_steps; i++) {
 +    sum += i;
 +}
 +</​code>​
 +
 +===== Exerciții =====
 +  - Descărcați [[https://​github.com/​cs-pub-ro/​app-labs/​tree/​master/​lab1/​skel | scheletul de laborator]] și [[https://​github.com/​cs-pub-ro/​app-labs/​tree/​master/​lab1/​demo | demo-ul pentru laborator]]. Rulați exemplele din demo.
 +  - Paralelizați însumarea elementelor unui array, încât suma să fie corectă, folosind fișierul ''​array_sum.c''​ din schelet, unde este implementată suma serială a elementelor dintr-un array.
 +
 +===== Resurse utile =====
 +  * http://​jakascorner.com/​blog/​
 +  * https://​ppc.cs.aalto.fi/​
app/laboratoare/01.1665653278.txt.gz · Last modified: 2022/10/22 17:16 (external edit)
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0