This is an old revision of the document!
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).
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).
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 helloworld.c -o helloworld -fopenmp
cc -xopenmp helloworld.c -o helloworld
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)
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.
#pragma omp parallel { // cod paralelizat }
Exemplu de folosire:
#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; }
De asemenea, putem avea regiuni paralele imbricate:
#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; }
Pentru setarea numărului de thread-uri din cadrul programului paralelizat, putem să facem în două moduri:
export OMP_NUM_THREADS=8
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:
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));
Alte funcții utile:
omp_get_num_threads()
omp_get_thread_num()
omp_get_num_procs()
Variabilele în cadrul blocurilor paralele pot fi:
În acest caz avem două clauze pentru context:
SHARED
- variabilă partajată între thread-uri (exemplu: SHARED©
)
PRIVATE
- variabilă văzută doar de thread-ul respectiv în blocul paralelizat (exemplu: PRIVATE(a, b)
)
Exemplu:
#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; }