Differences

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

Link to this comparison view

app:laboratoare:03 [2022/10/22 17:09]
florin.mihalache [Clauze legate de vizibilitatea variabilelor]
app:laboratoare:03 [2022/10/26 16:26] (current)
emil.slusanschi [Resurse]
Line 1: Line 1:
-===== Laboratorul 3 - Advanced OpenMP ===== +====== Laboratorul 3 - Advanced OpenMP ​====== 
-==== Sections ==== +===== Sections ​===== 
 Uneori dorim să distribuim ca thread-uri diferite să execute task-uri diferite în același timp. În această privință ne vine de ajutor conceptul de sections, prin care două sau mai multe thread-uri execută două sau mai multe sections corespunzătoare acestora (adică thread-urilor,​ fiecare thread cu un section). Uneori dorim să distribuim ca thread-uri diferite să execute task-uri diferite în același timp. În această privință ne vine de ajutor conceptul de sections, prin care două sau mai multe thread-uri execută două sau mai multe sections corespunzătoare acestora (adică thread-urilor,​ fiecare thread cu un section).
  
Line 56: Line 56:
 {{ :​app:​laboratoare:​sections.png?​400 |}} {{ :​app:​laboratoare:​sections.png?​400 |}}
  
-==== Single ====+===== Single ​=====
 Dacă dorim ca o secvență de cod (dintr-o bucată de cod paralelizat) să fie executat doar de un singur thread, folosim directiva ''​SINGLE''​. Aceasta este folosită, de regulă, în operații I/O. Dacă dorim ca o secvență de cod (dintr-o bucată de cod paralelizat) să fie executat doar de un singur thread, folosim directiva ''​SINGLE''​. Aceasta este folosită, de regulă, în operații I/O.
  
Line 70: Line 70:
 </​code>​ </​code>​
  
-=== Master ===+==== Master ​====
 Directiva ''​MASTER''​ este o particularizare a directivei ''​SINGLE'',​ unde codul din zona paralelizată este executat de thread-ul master (cel cu id-ul 0). Directiva ''​MASTER''​ este o particularizare a directivei ''​SINGLE'',​ unde codul din zona paralelizată este executat de thread-ul master (cel cu id-ul 0).
 <code c> <code c>
Line 82: Line 82:
 </​code>​ </​code>​
  
-==== Construcții de sincronizare ==== +===== Construcții de sincronizare ​===== 
-=== Mutex ===+==== 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. 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.
  
Line 105: Line 105:
 </​code>​ </​code>​
  
-=== Barieră ===+==== 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. 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.
  
Line 127: Line 127:
 </​code>​ </​code>​
  
-=== Reduction ===+==== 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. ''​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.
  
Line 144: Line 144:
 </​code>​ </​code>​
  
-=== Atomic ===+==== Atomic ​====
 Directiva ''​ATOMIC''​ permite executarea unor instrucțiuni în mod atomic, instrucțiuni care provoacă race conditions între thread-uri, problemă pe care această directivă o rezolvă. Directiva ''​ATOMIC''​ permite executarea unor instrucțiuni în mod atomic, instrucțiuni care provoacă race conditions între thread-uri, problemă pe care această directivă o rezolvă.
  
Line 166: Line 166:
 </​code>​ </​code>​
  
-=== Ordered ===+==== Ordered ​====
 Directiva ''​ORDERED''​ este folosit în for-uri cu scopul de a distribui în ordine iterațiile către thread-uri. Directiva ''​ORDERED''​ este folosit în for-uri cu scopul de a distribui în ordine iterațiile către thread-uri.
  
Line 202: Line 202:
   * ''​COPYIN''​ - asignarea unei variabile ''​THREADPRIVATE''​ este vizibilă tuturor thread-urilor   * ''​COPYIN''​ - asignarea unei variabile ''​THREADPRIVATE''​ este vizibilă tuturor thread-urilor
  
-==== Tasks (opțional) ==== +===== Tasks (opțional) ​===== 
-Task-urile în OpenMP reprezintă un concept prin care putem să avem thread pools pentru paralelizarea de soluții ale căror dimensiune nu o știm (echivalent cu ExecutorService din Java). Un task este executat la un moment dat de către un thread din thread pool.+Task-urile în OpenMP reprezintă un concept prin care putem să avem thread pools pentru paralelizarea de soluții ale căror dimensiune nu o știm (echivalent cu ''​ExecutorService'' ​din Java). Un task este executat la un moment dat de către un thread din thread pool.
  
-Pentru crearea unui task se folosește directiva TASK:+Pentru crearea unui task se folosește directiva ​''​TASK''​:
 <code c> <code c>
 #pragma omp task [clause1 [[,] clause2, ...]] #pragma omp task [clause1 [[,] clause2, ...]]
 </​code>​ </​code>​
-Pentru sincronizarea task-urilor (în sensul să așteptăm toate rezultatele task-urilor,​ în stilul barierei), se folosește directiva TASKWAIT (exemplu de folosire în exemplul Fibonacci de mai jos).+Pentru sincronizarea task-urilor (în sensul să așteptăm toate rezultatele task-urilor,​ în stilul barierei), se folosește directiva ​''​TASKWAIT'' ​(exemplu de folosire în exemplul Fibonacci de mai jos).
  
 În privința variabilelor dintr-un task, aici avem trei variante de variabile: În privința variabilelor dintr-un task, aici avem trei variante de variabile:
  
-  * shared - toate task-urile au acces la aceeași adresă a unei variabile, o modificare asupra variabilei din partea unui task va fi vizibilă către toate task-urile (uneori putem avea potențial de erori în acest caz). +  * ''​shared'' ​- toate task-urile au acces la aceeași adresă a unei variabile, o modificare asupra variabilei din partea unui task va fi vizibilă către toate task-urile (uneori putem avea potențial de erori în acest caz). 
-  * firstprivate - fiecare task va avea o copie a unei variabile inițializate cu o valoare înainte de crearea task-ului respectiv. +  * ''​firstprivate'' ​- fiecare task va avea o copie a unei variabile inițializate cu o valoare înainte de crearea task-ului respectiv. 
-  * private - aici putem să avem variabile care nu sunt inițializate înainte de crearea task-ului și care să fie inițializate în cadrul task-ului.+  * ''​private'' ​- aici putem să avem variabile care nu sunt inițializate înainte de crearea task-ului și care să fie inițializate în cadrul task-ului.
  
 <code c> <code c>
Line 275: Line 275:
   * la sections instrucțiunile sunt executate imediat când thread-ul asociat acelui section ajunge în section-ul respectiv, când la tasks instrucțiunile pot fi executate după ce thread-ul asociat task-ului trece de task-ul respectiv   * la sections instrucțiunile sunt executate imediat când thread-ul asociat acelui section ajunge în section-ul respectiv, când la tasks instrucțiunile pot fi executate după ce thread-ul asociat task-ului trece de task-ul respectiv
   * la sections putem avea overhead și load balancing slab   * la sections putem avea overhead și load balancing slab
 +
 +===== Exerciții =====
 +  * Paralelizați fișierul ''​main.c''​ din [[https://​github.com/​cs-pub-ro/​app-labs/​tree/​master/​lab3/​skel | schelet]], unde se citește un fișier, unde pe prima linie se află numărul de elemente pentru un array și pe următoarea linie se află array-ul respectiv, se face suma numerelor (aici faceți în trei moduri, separat, cu ''​reduction'',​ cu ''​atomic''​ și cu ''​critical'',​ unde veți măsura timpii de execuție - hint, folosiți directiva master ca un singur thread să facă măsurătorile),​ iar la final, cu ajutorul ''​sections'',​ scrieți timpii de execuție în trei fișiere (este deja implementată funcția de scriere în fișier).
 +
 +<note tip>O să aveți nevoie de barieră la citire și înainte de scrierea în fișiere.</​note>​
 +
 +<​note>​De probă, încercați să puneți ORDERED la for-urile paralelizate,​ pentru a vedea cum este afectată performanța.</​note>​
 +
 +  * **(opțional)** Paralelizați folosind task-uri codul din [[https://​github.com/​cs-pub-ro/​app-labs/​blob/​master/​lab3/​skel/​tree.c | tree.c]] (folosiți task-uri în funcțiile ''​preorder''​ și ''​height''​ - la ultima trebuie să folosiți ''​taskwait''​).
 +
 +===== Resurse =====
 +
 +[[https://​stackoverflow.com/​questions/​18669296/​c-openmp-parallel-for-loop-alternatives-to-stdvector | User-defined OpenMP Reduction]]
app/laboratoare/03.1666447770.txt.gz · Last modified: 2022/10/22 17:09 by florin.mihalache
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