You are on page 1of 7

Algoritmi Paraleli i Distribuii 2012 2013

Laborator nr. 1

Introducere n OpenMP
1. Prezentare general
Acronim: Open Multi-Processing
OpenMP reprezint un API (Application Program Interface) prin intermediul cruia se pot
dezvolta aplicaii paralele (multi-threaded) bazate pe memorie partajat. Se ofer suport att
pentru paralelism la nivel de task, ct i la nivel de bucl.

Fig. 1. Modelul aplicaiilor OpenMP


(figura este preluat din [1])
API-ul OpenMP este alctuit din 3 componente importante: directive pentru compilator,
librrii runtime, variabile de mediu, i este implementat pentru limbajele C/C++ i Fortran, fiind
portat pe majoritatea sistemelor de operare (Unix/Linux, Max OS, Windows). Aplicaiile dezvoltate
utiliznd acest API sunt portabile n sensul ca un cod surs recompilat pentru o alt platform se
va comporta similar platformei originale (ATENIE: SIMILAR nu IDENTIC!!!). Avantajul OpenMP
este acela de a putea combina ntr-o maniera relativ uoar codul secvenial cu cel paralel, fr a
fi n general nevoie de a gestiona firele de execuie ale aplicaiei.

Caracteristici ale programelor dezvoltate utiliznd OpenMP:


toate firele de execuie au acces la aceeai zon partajat de memorie (vezi fig. 1);
datele pot fi partajate (n cadrul aceleiai zone comune de memorie) sau private;
datele partajate sunt accesibile fiecrui thread din mulimea de lucru;
datele private pot fi accesate doar de thread-ul proprietar;
transferul datelor se realizeaz ntr-un mod transparent pentru programator;
n cadrul oricrei execuii este prezent cel puin un mecanism de sincronizare.

Algoritmi Paraleli i Distribuii 2012 2013

Laborator nr. 1

Modelul de programare OpenMP [2]:


este un model bazat pe memorie partajat, paralelismul fiind bazat pe fire de execuie;
paralelismul este explicit, n sensul c se ofer programatorului mecanisme de control
pentru gestiunea complet a paralelizrii;
este un model de tip fork/join vezi fig. 2:

toate programele OpenMP ncep cu un singur fir de execuie denumit thread master;

thread-ul master se execut secvenial pn la ntlnirea unui constructor al unei


regiuni paralele;

Fig. 2. Modelul de execuie


(figura este preluat din [2])

n momentul ntlnirii unui astfel de constructor, thread-ul master creaz o echip de


thread-uri worker prin intermediul unor apeluri de tip fork;

secvena de cod prezent ntr-o regiune paralel este executat n paralel de ctre
fiecare fir de execuie n parte (inclusiv de ctre thread-ul master);

la finalul execuiei secvenei paralele are loc o operaie de sincronizare a thread-urilor


(inclusiv masterul), urmat de un set de operaii de tip join n urma crora aplicaia
revine doar la firul de execuie master;
paralelismul OpenMP se obine de cele mai multe ori prin intermediul directivelor
compilator;
API-ul OpenMP ofer suport:

pentru regiuni paralele de tip nested: regiune paralel construit n cadrul altei regiuni
paralele;

pentru gestiunea dinamic a numrului de fire de execuie ce sunt utilizate pentru


execuia diferitelor regiuni paralele.

Cu toate c OpenMP reprezint un API performant pentru programarea paralel, este


important s se cunoasc limitrile acestuia:
API-ul nu se adreseaz sistemelor paralele cu memorie partajat distribuit;
nu este implementat identic de ctre toi productorii;
nu se garanteaz ntotdeauna o utilizare eficient a memoriei comune;
nu ofer mecanisme de verificare a accesului datelor partajate: nu verific
interdependenele dintre date, nu verific eventualele conflicte ce pot aprea ntre variabile,
nu verific dac modificarea unei variabile partajate este realizat sau nu de un singur
thread, nu verific blocajele ce pot aprea n cadrul unor programe paralele;
nu acoper alte directive specifice compilatoarelor, directive ce pot asista n dezvoltarea
unei paralelizri eficiente;
nu a fost dezvoltat astfel nct s acopere sincronizarea operaiilor de intrare/ieire.

Algoritmi Paraleli i Distribuii 2012 2013

Laborator nr. 1

2. Componentele OpenMP

Directivele compilator
sunt n general utilizate pentru construirea regiunilor paralele;
sintaxa general (C/C++):
#pragma omp construct [clause [clause] ]

prin intermediul acestora se poate specifica:


modalitatea de partiionare a volumului de lucru ntre thread-uri;
modalitatea de sincronizare a thread-urilor;
vizibilitatea variabilelor (variabile private, partajate, etc.).
Variabilele de mediu
specific numrul de thread-uri ce pot fi lansate n execuie ($OMP_NUM_THREADS);
specific tipul de schedul-er ce poate fi utilizat;
specific modurile de ajustare dinamic a thread-urilor n timpul execuiei unui program
OpenMP.
Librriile runtime
sunt reprezentate de un set de funcii necesare interogrii i/sau setarea mediului de lucru
(aflarea numrului de procesoare de pe maina de lucru, aflarea numrului total de threaduri ce execut o anumit seciune paralel, etc);
au prototipurile funciilor define n <omp.h> (pentru C/C++).

3. Structura general a
Vizibilitatea variabilelor

programelor

OpenMP.

Directive

compilator

de

baz.

#include <omp.h>
main () {
int var1, var2, *var3;
//Serial code
.
.
//Beginning of parallel section. Fork a team of threads.
#pragma omp construct [clause [clause]]
{
int var4, var5;
//Parallel section executed by all threads
.
.
//All threads join master thread and disband
}
//Resume serial code
.
.
}

Cod 1: Structura generala a programelor OpenMP


Conceptul fundamental pe care se bazeaz OpenMP n stabilirea zonelor paralele este
acela de bloc structurat (eng: structured block). Un bloc structurat reprezint o secven de cod
pentru care exist un singur punct de intrare, marcat de inceputul secvenei, i, respectiv, un

Algoritmi Paraleli i Distribuii 2012 2013

Laborator nr. 1

singur punct de ieire, marcat de terminarea secvenei. Singura excepie admis de la aceast
regul este apelarea funciei exit() (n C/C++).
Exemplu de blocuri structurate i nestructurate [3]:
#pragma omp construct
{
int id = omp_get_thread_num();
more: res[id] = do_big_job (id);
if (conv (res[id]) goto more;
}
printf (All done\n);

if (go_now()) goto more;


#pragma omp construct
{
int id = omp_get_thread_num();
more: res[id] = do_big_job(id);
if (conv (res[id]) goto done;
goto more;
}
done: if (!really_done()) goto more;

Bloc structurat
Bloc nestructurat
Cod 2: Exemplu de bloc structurat vs. bloc nestructurat
Directivele de baz ale API-ului OpenMP
omp parallel
Declararea unei regiuni paralele simple (sub forma de bloc structurat) se realizeaz prin
intermediul directivei pragma omp parallel.

#pragma omp parallel


{
//instructiuni
} //sfarsitul regiunii paralele

Sintaxa complet a directivei este:


#pragma omp parallel [clause ...] newline
if (scalar_expression)
private (list)
shared (list)
default (shared | none)
firstprivate (list)
reduction (operator: list)
copyin (list)
num_threads (integer-expression)
structured_block

Exemplu de program:
1
2
3
4
5
6
7
8
9
10

#include <stdio.h>
int main(void) {
printf ("Master thread before omp par\n\n");
#pragma omp parallel
{
printf ("Hello from team thread\n");
}
printf ("\nMaster thread after omp par\n");
return 0;
}

Cod 3: Hello OpenMP


Nota:

Compilarea i rularea programelor ce utilizeaz OpenMP se realizeaz adugnd


opiunea -fopenmp pentru compilatorul standard Linux:
gcc -fopenmp sursa.c -o fila.bin

Algoritmi Paraleli i Distribuii 2012 2013

Laborator nr. 1

sau
g++ -fopenmp sursa.cpp -o fila.bin

omp for
Declararea unui for paralel (sub forma unui bloc structurat) se realizeaz prin intermediul
directivei pragma omp for.

#pragma omp parallel


{
#pragma omp for
for (i=0; i<MAX; i++)
{
res[i] = huge();
}
}

#pragma omp parallel for


for (i=0;i< MAX; i++)
{
res[i] = huge();
}

Cod 4: Declararea unei bucle for


Sintaxa complet a directivei este:
#pragma omp for [clause ...] newline
schedule (type [,chunk])
ordered
private (list)
firstprivate (list)
lastprivate (list)
shared (list)
reduction (operator: list)
collapse (n)
nowait
for_loop

Modul general de operare a unei bucle omp for este descris schematic n exemplul
urmtor:
#define N 12
[...]
#pragma omp parallel
#pragma omp for
for(i = 1, i < N+1, i++)
c[i] = a[i] + b[i];

Cod 5: Exemplu omp for

Fig. 3. Shema de functionare [3]

Algoritmi Paraleli i Distribuii 2012 2013

Laborator nr. 1

Vizibilitatea variabilelor
O variabil declarat n exteriorul unui bloc paralel este n mod implicit variabil
partajat (vezi n Cod 1 variabilele var1, var2, var3). O variabil declarat n interiorul unui bloc
paralel este n mod implicit variabil privat a thread-ului care o declar (vezi n Cod 1
variabilele var4, var5). O prim modificare a vizibilitii variabilelor poate fi realizat prin intermediul
clauzelor shared i private.
clauza shared (lista de variabile)
Dac aceast clauz este prezent n cadrul unei directive pragma omp, atunci toate
variabilele prezente n list vor fi partajate ntre firele de execuie lansate de master. API-ul
OpenMP asigur mecanisme de gestionare a accesului acestor variabile, dar NU realizeaz
nici un fel de sincronizare n mod implicit.

clauza private (lista de variabile)


Dac aceast clauz este prezent n cadrul unei directive pragma omp, atunci toate
variabilele prezente n lista ataat sunt marcate ca fiind private pentru fiecare thread n parte.
Acest lucru nseamn c fiecare variabil din list va fi redeclarat n spaiul de memorie
privat ataat fiecrui thread (se menine ca nume i tip de dat), dar NU se vor copia valorile
acestor variabile din spaiul de memorie partajat. Modificrile operate asupra variabilelor de
tip private sunt vizibile numai n cadrul thread-ului, nu i n exterior.

4. Funcii pentru interogarea/setarea mediului de lucru

int omp_get_thread_num(void)

Funcia returneaz ID-ul thread-ului n cadrul mulimii de thread-uri. Dac este apelat n
cadrul unei regiuni secveniale, atunci funcia va returna ntotdeauna valoarea 0, altfel, dac este
apelat ntr-o regiune paralel, va returna o valoarea cuprins ntre 0 i n-1, unde n reprezint
numrul total de thread-uri alocat pentru regiunea paralel. Thread-ul master va avea ntotdeauna
ID-ul 0.

int omp_get_num_threads(void)

Funcia returneaz numrul total de thread-uri lansate n cadrul unei regiuni paralele. Dac
funcia este apelat n afara unei regiuni paralele, atunci va returna ntotdeauna valoarea 1.

void omp_set_num_threads(int)

Funcia poate fi utilizata pentru a seta numrul de thread-uri ce vor fi lansate n execuie
pentru urmtoarea regiune paralela. Valoarea transmisa trebuie sa fie strict pozitiva. Funcia nu are
sens n cazul n care directiva compilator include clauza num_threads i acioneaz independent de
variabila de mediu OMP_NUM_THREADS.

int omp_get_num_procs(void)

Funcia poate fi utilizat pentru a determina numrul de procesoare active din sistem.
Comportamentul funciei este acelai indiferent de regiunea n care este apelat.

5. Probleme
1. Implementai exemplul descris n Cod 3, compilai i lansai n execuie. Testai
comportarea funciilor descrise n cadrul paragrafului 4.
2. Implementai o aplicaie OpenMP care s realizeze produsul dintre o matrice de
dimensiune NxN i un vector de dimensiune N. Se va considera N=1000 (cel puin) i se va
varia numrul de thread-uri al aplicaiei. Pentru fiecare rulare se vor msura timpii implicai.

Algoritmi Paraleli i Distribuii 2012 2013

Laborator nr. 1

3. Implementai o aplicaie OpenMP care s realizeze ridicarea la ptrat a unei matrici de


dimensiune NxN. Se va considera N=500 (cel puin) i se variaz numrul de thread-uri al
aplicaiei. Pentru fiecare rulare se vor msura timpii implicai.
Bibliografie:
1. Ruud van der Pas, An Introduction Into OpenMP, IWOMP 2005 University of Oregon,
Eugene, Oregon, USA June 1-4, 2005
2. Blaise Barney, OpenMP, Lawrence Livermore National Laboratory,
https://computing.llnl.gov/tutorials/openMP/

3. Intel Software College, Programming with OpenMP


4.

http://gcc.gnu.org/onlinedocs/libgomp/Runtime-Library-Routines.html#Runtime-Library-Routines