47
FAKULTET ELEKTROTEHNIKE I RAČUNARSTVA ZAVOD ZA ELEKTRONIKU, MIKROELEKTRONIKU, RAČUNALNE I INTELIGENTNE SUSTAVE SEMINARSKI RAD: OPENMP: VIŠEDRETVENO PROGRAMIRANJE U PROGRAMSKOM JEZIKU C/C++ Predmet: Višeprocesorski i paralelni sustavi Nastavnik: prof. dr. sc. Zoran Kalafatić Student: Miroslav Štampar, dipl. ing. MB: R-10/2008 Datum: 31. svibnja, 2009.

OpenMP: Višedretveno programiranje u programskom jeziku C/C++

Embed Size (px)

DESCRIPTION

Seminarski rad

Citation preview

Page 1: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

FAKULTET ELEKTROTEHNIKE I RAČUNARSTVAZAVOD ZA ELEKTRONIKU, MIKROELEKTRONIKU,

RAČUNALNE I INTELIGENTNE SUSTAVE

SEMINARSKI RAD:

OPENMP: VIŠEDRETVENO PROGRAMIRANJE U PROGRAMSKOM JEZIKU C/C++

Predmet: Višeprocesorski i paralelni sustaviNastavnik: prof. dr. sc. Zoran KalafatićStudent: Miroslav Štampar, dipl. ing.MB: R-10/2008Datum: 31. svibnja, 2009.

Page 2: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

Sadržaj1. Uvod...................................................................................................................................1

1.1. Primjer: Hello World....................................................................................................11.2. Primjer: Inicijalizacija tablice.......................................................................................21.3. Rasprava.....................................................................................................................21.4. Podrška u različitim C/C++ prevodiocima..................................................................3

2. Osnove...............................................................................................................................32.1. Paralelni odjeljci..........................................................................................................32.2. Petlje...........................................................................................................................4

2.2.1. Raspodjela posla.................................................................................................62.2.2. Poređivanje..........................................................................................................7

2.3. Sekcije........................................................................................................................72.4. Zadaci.........................................................................................................................92.5. Kontrola dijeljenja varijabli između dretvi.................................................................10

2.5.1. Klauzule private, firstprivate i shared................................................................112.5.2. Klauzula lastprivate...........................................................................................132.5.3. Klauzula default.................................................................................................152.5.4. Klauzula reduction.............................................................................................15

2.6. Dretvena sigurnost (međusobnim isključivanjem)....................................................182.6.1. Atomnost...........................................................................................................182.6.2. Kritični odjeljci....................................................................................................182.6.3. Mehanizam brave..............................................................................................19

2.7. Sinkronizacija izvođenja...........................................................................................222.7.1. Direktiva barrier i klauzula nowait.....................................................................222.7.2. Direktive single i master....................................................................................24

2.8. Očuvanje konzistentnosti podataka..........................................................................252.9. Ugniježđivanje petlji..................................................................................................26

2.9.1. Problem.............................................................................................................262.9.1.1. Rješenje (OpenMP 3.0).............................................................................262.9.1.2. Moguća rješenja (OpenMP 2.5).................................................................272.9.1.3. Performanse...............................................................................................28

2.9.2. Ograničenja.......................................................................................................283. Nedostaci.........................................................................................................................28

3.1. OpenMP i fork()........................................................................................................283.2. Prekid izvođenja dretvi (i izlaženje iz petlji)..............................................................303.3. Ostalo........................................................................................................................33

4. Praktični rad.....................................................................................................................34

Page 3: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

4.1. Metodologija..............................................................................................................344.2. Testno okruženje......................................................................................................344.3. Rezultati....................................................................................................................354.4. Rasprava...................................................................................................................37

5. Zaključak..........................................................................................................................386. Literatura..........................................................................................................................397. Dodatak............................................................................................................................40

7.1. Programski kod.........................................................................................................407.1.1. it.c......................................................................................................................407.1.2. mb.c...................................................................................................................417.1.3. mm.c..................................................................................................................427.1.4. pi.c.....................................................................................................................44

Page 4: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

1.Uvod

1. UvodPošto se procesorska brzina više ne povećava istom brzinom kao i ranije, važnost razvoja i uporabe višejezgrenih sustava postaje sve očiglednija. Kako bi se iskoristio sav taj potencijal, postaje sve bitnije za programere da nauče paralelno programiranje, odnosno, pisanje programa koji obavljaju više zadaća istovremeno.OpenMP (engl. Open Multi-Processing) je naziv za sučelje za programiranje aplikacija (engl. Application Programming Interface) namijenjen višeplatformskom (Unix, Windows,...) paralelnom programiranju u jezicima C, C++ i Fortranu. Sastoji se od skupa prevoditeljskih direktiva, naredbi i varijabli operativnog sustava kojima se utiče na izvršavanje takovih programa.Slijede dva jednostavna primjera u kojima će se demonstrirati korištenje OpenMP-a.Napomena: Za potrebe ovog rada koristit će se GCC (engl. krat. GNU Compiler Collection) skup programskih prevodioca, odnosno konkretno C i C++ prevodioc. Svi dati programi se mogu prevesti u izvršni oblik na sljedeći način: g++ <ime_datoteke> -fopenmp

1.1. Primjer: Hello World#include <omp.h> // OpenMP datoteka zaglavlja s pomoćnim naredbamaint main(){

int i = 0, j = 1;#ifdef _OPENMP // ovaj dio će se izvesti samo u slučaju da

// je OpenMP podržan od strane prevodiocaomp_set_num_threads(4); // ovom se naredbom određuje broj dretvi s

// kojima će OpenMP raspolagati u daljnjem radu#endif#pragma omp parallel default(shared) private(i, j) // osnovni OpenMP blok

{#ifdef _OPENMP

i = omp_get_thread_num(); j = omp_get_num_threads();#endif

printf("Hello World iz dretve %d od %d\n", i, j);}

// ovaj će program će ispisati Hello Worldreturn 0; // četiri puta ukoliko je OpenMP podržan od

} // strane prevodioca

1

Page 5: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

1.Uvod

1.2. Primjer: Inicijalizacija tablice#include <cmath>using namespace std;int main(){

const int size = 256;double sinTable[size];

#pragma omp parallel for // najčešće korišteni OpenMP konstrukt – paralelizira// sljedeću for petlju

for(int n=0; n<size; ++n)sinTable[n] = sin(2 * M_PI * n / size);

return 0; // ovaj će program paralelno inicijalizirati tablicu} // s automatski određenim brojem dretvi

1.3. Rasprava

Kao što se može primjetiti iz danih primjera, postoji vrlo malo indikacija da se program izvršava paralelno. Ukoliko bi se uklonile pragma linije, rezultat bi bio u potpunosti ispravan C++ program koji bi i dalje izvršavao očekivanu zadaću.Samo ukoliko prevodioc ispravno interpretira pragma linije, program postaje spreman za paralelno izvođenje. U tom se slučaju uistinu istovremeno računa N vrijednosti, pri čemu N predstavlja ukupan broj dretvi. Kod GCC-a, knjižnica izvođenja (engl. runtime library) libgomp je zadužena za vođenje brige o broju dretvi – obično to bude broj procesorskih jedinica.Po C i C++ standardima, ukoliko prevodioc započne obradu pragma linije koju ne podržava, jednostavno ju ignorira i nastavlja daljnje prevođenje. Stoga se dodavanje paralelnih direktiva može obaviti sigurno i bez straha od mogućih problema s kompatibilnošću, kao u slučaju sa starijim prevodiocima.Također postoje i dodatne pomoćne naredbe kojima se može pristupiti uključivanjem omp.h datoteke u zaglavlje programa, no za njom nema previše potrebe. Ukoliko su vam one neophodne, bilo bi poželjno da provjeriti stanje direktive #define _OPENMP kod uključivanja spomenutog zaglavlja, kako bi se izbjegli problemi s prevodiocima koji ne podržavaju OpenMP:#ifdef _OPENMP#include <omp.h>#endif

2

Page 6: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

1.Uvod

1.4. Podrška u različitim C/C++ prevodiocima

• OpenMP specifikacija 2.5 je podržana u GCC-u od verzije 4.2. Podrška se uključuje dodavanjem opcije –fopenmp komandnoj liniji prevodioca.

• OpenMP specifikacija 2.5 je podržana u Microsoft Visual C++ 2005 prevodiocu (cl). Podrška se uključuje dodavanjem opcije /openmp komandnoj liniji prevodioca.

• OpenMP specifikacija 2.5 je podržana u Intel C prevodiocu (icc). Podrška se uključuje dodavanjem opcije -openmp komandnoj liniji prevodioca.

• OpenMP specifikacija 3.0 je podržana u GCC-u od verzije 4.4. Podrška se uključuje dodavanjem opcije -fopenmp komandnoj liniji prevodioca.

Napomena: Ukoliko se vaš prevodioc žali da ne prepoznaje pripadnu opciju za uključivanje OpenMP podrške, tada je najvjerojatnije riječ o zastarjeloj verziji istog. No, ukoliko se povezivač (engl. linker) žali da ne prepoznaje pojedine funkcije OpenMP-a, tada ste vjerojatno zaboravili uključiti pripadnu opciju u komandnoj liniji.Za više informacija o podržanim prevodiocima posjetite OpenMP službene stranice: http://openmp.org/wp/openmp-compilers/

2. OsnoveSve OpenMP direktive u C/C++-u započinju s konstruktom #pragma omp, pri čemu parametri slijede u nastavku sve do kraja reda. Pragma se obično odnosi samo na naredbu (ili blok naredbi) u nastavku programa, osim u slučajevima naredbi barrier i flush, koje nemaju pridružene stavke.

2.1. Paralelni odjeljci

Pragmom parallel započinjemo paralelni odjeljak; s njom se stvara tim od N dretvi (pri čemu se vrijednost N određuje za vrijeme izvođenja aplikacije i to obično prema broju procesorskih jezgri, no postoje i drugi čimbenici koji utječu na nj) koje izvode sljedeću naredbu ili sljedeći blok naredbi ukoliko su naredbe ograđene sa zagradama {...}. Nakon izvršenja pridodjeljene naredbe, dretve se spajaju natrag opet u jednu.#pragma omp parallel{

// Programski kod unutar ovog odjeljka se izvodi u paraleliprintf("Hello!\n");

}

U ovom se primjeru ispisuje tekst "Hello!", iza kojeg slijedi nova linija, onoliko puta koliko postoji dretvi u timu, pri čemu je tim naziv za grupu dretvi pod kontrolom OpenMP-a koje izvršavaju program. Ukoliko se program izvodi na dvojezgrenom sustavu, ispis će se ponovit dva puta.

3

Page 7: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

Napomena: Također može doći do ispisa nečega poput "HeHlellolo", ovisno o platformi, stoga što se ispis obavlja u paraleli.Interno, GCC obavlja proces prevođenja paralelnog dijela programa stvarajući tzv. "magic" funkciju i premještanjem korisničkog koda u nju; sve varijable deklarirane unutar paralelnog odjeljka automatski postaju lokalne varijable te funkcije, odnosno, lokalne za svaku dretvu posebno.Intelov prevodioc ICC, u drugu ruku, koristi mehanizam sličan onom kod korištenja naredbe fork(), bez "magic" funkcije. Obje su implementacije, naravno, valjane i semantički identične.Paralelizam se također može učiniti kondicionalnim ukoliko se koristi klauzula if u paralelnoj direktivi, poput:extern int parallelism_enabled;#pragma omp parallel for if(parallelism_enabled)for(int c=0; c<n; ++c)

handle(c);

U ovom slučaju, ukoliko se u varijabli parallelism_enabled nalazi vrijednost 0, broj dretvi koji će obraditi navedenu petlju biti će uvijek jednak jedan.

2.2. Petlje

Direktiva for paralelizira pripadnu petlju tako da svaka dretva u trenutnom timu obrađuje jedan dio petlje.#pragma omp forfor(int n=0; n<10; ++n)

printf(" %d", n);printf(".\n");

Ova petlja će ispisati svaki broj u rasponu [0..9] jedanput, no to neće učiniti nužno u slijednom redoslijedu. Konačan ispis može izgledati poput: 0 5 6 7 1 8 2 3 4 9.Interno, gornja petlja postaje kod ekvivalentan ovome:int this_thread = omp_get_thread_num(), num_threads = omp_get_num_threads();int my_start = (this_thread ) * 10 / num_threads;int my_end = (this_thread+1) * 10 / num_threads;for(int n=my_start; n<my_end; ++n)

printf(" %d", n);

Na taj način svaka dretva dobiva različit dio petlje, izvršavajući svaka svoj dio u paraleli.

4

Page 8: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

Napomena: #pragma omp for raspoređuje dijelove petlje među različitim dretvama u trenutnom timu. Na početku programa, tim se sastoji samo od jednog člana, tzv. glavne dretve (engl. master thread), koja se izvršava kroz cijeli program.Kako bi se napravio novi tim dretvi, mora se koristiti direktiva parallel:#pragma omp parallel{#pragma omp for

for(int n=0; n<10; ++n) printf(" %d", n);}printf(".\n");

Skraćeni istovjetni oblik prethodnog koda bi bio:#pragma omp forfor(int n=0; n<10; ++n) printf(" %d", n);printf(".\n");

Eksplicitno se može specificirati broj dretvi koje će se kreirati u timu korištenjem klauzule num_threads:#pragma omp parallel num_threads(3){

// dijelovi sljedeće petlje će biti raspodijeljeni između // tri dretve u trenutnom timu.

#pragma omp forfor(int n=0; n<10; ++n) printf(" %d", n);

}

Kod korištenja OpenMP-a u programskom jeziku C, mora se eksplicitno navesti da je varijabla iteracije petlje tipa private, stoga što C ne podržava njezino deklariranje u tijelu petlje:int n;#pragma omp for private(n)for(n=0; n<10; ++n)

printf(" %d", n);printf(".\n");

U nastavku potražite detaljni opis klauzula private i shared za više detalja.

U specifikaciji OpenMP 2.5, varijabla iteracije u for petlji mora biti cjelobrojnog tipa s predznakom (engl. signed integer), dok se kod OpenMP 3.0 također mogu koristiti i cjelobrojne varijable bez predznaka (engl. unsigned integer), pokazivači (engl. pointers) i posebni iteratori.

5

Page 9: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

2.2.1. Raspodjela posla

Algoritam raspodjele (engl. scheduling) se kod for petlje može eksplicitno zadati.#pragma omp for schedule(static)for(int n=0; n<10; ++n) printf(" %d", n);printf(".\n");

Podrazumijevani tip raspodjele je statički (engl. static) čija se pravilna primjena može vidjeti u gornjem primjeru. Kod ove se vrste raspodjele cjeloukupan posao dijeli na onoliki broj komada koliki je broj dretvi u timu. Nakon ulaska u petlju, svaka dretva neovisno odlučuje koji će komad (engl. chunk) petlje obrađivati. Također postoji i dinamički (engl. dynamic) tip raspodjele: #pragma omp for schedule(dynamic)for(int n=0; n<10; ++n) printf(" %d", n);printf(".\n");

U dinamičkoj raspodjeli ne postoji predvidljivi poredak u kojem će se pojedine iteracije petlje raspoređivati između različitih dretvi. Svaka dretva vrši upit prema OpenMP knjižnici izvođenja (engl. runtime library) za pripadajući broj iteracije, obrađuje ga, te ponavlja cijeli proces do završetka petlje. Ovu je raspodjelu najkorisnije koristiti u kombinaciji s klauzulom ordered ili u slučajevima kad je različitim iteracijama u petlji potrebno različito vrijeme izvođenja.Veličina komada (engl. chunk) se također može zadati kako bi se smanjio broj poziva prema OpenMP knjižnici izvođenja:#pragma omp for schedule(dynamic, 3)for(int n=0; n<10; ++n) printf(" %d", n);printf(".\n");

U prethodnom primjeru svaka dretva vrši upit za sljedeći komad iteracija, izvodi dobivene tri, te potom traži druge, itd. Zadnji komad može biti i manji od tri iteracije.Interno, gornja petlja se prevodi u kod ekvivalentan sljedećem (samo za ilustraciju):int a,b;if(GOMP_loop_dynamic_start(0,10,1, 3, &a,&b)){

do {

for(int n=a; n<b; ++n) printf(" %d", n);

} while(GOMP_loop_dynamic_next(&a,&b));

}

6

Page 10: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

Navođena (engl. guided) raspodjela je kombinacija prethodne dvije vrste, zajedno s njihovim prednostima i nedostacima. Kod ove je vrste raspodjele broj iteracija koji se pridodjeljuje svakoj dretvi u timu (prilikom upita) određen sljedećom formulom:broj_iteracija = max(broj_neraspoređenih_iteracija/omp_get_num_threads(), veličina_komada)Nakon što dretva završi s obradom datog komada iteracija, pridodjeljuje joj se sljedeći komad primjenom navedene formule.Dinamička i navođena raspodjela su odlični mehanizmi u situacijama kada je broj iteracija unaprijed nepoznat i/ili kad je jedan procesor brži od drugog. Upotrebom statičke raspodjele ne postoji način kako da se ravnomjerno rasporedi posao (engl. load balancing) među dretvama u tim situacijama, za razliku od ostale dvije metode. Obično navođena raspodjela pokazuje bolje performanse od dinamičke zbog toga što se gubi manje vremena na posao vezan uz samu raspodjelu.

2.2.2. Poređivanje

Uobičajeni poredak po kojem se iteracije petlje izvode nije specificiran i ovisi o uvjetima tijekom izvođenja programa. Međutim, moguće je zadati da se određeni događaji (engl. events) unutar petlje dogode u slijednom (engl. sequential) poretku korištenjem klauzule poređivanja ordered:#pragma omp for ordered schedule(dynamic)for(int n=0; n<100; ++n){

files[n].compress();#pragma omp ordered

send(files[n]);}

U ovoj se petlji obrađuje 100 datoteka, pri čemu se neke datoteke sažimaju u paraleli, no sigurno je da će se sve datoteke slati u slijednom poretku.Ukoliko je dretva kojoj je dodijeljeno sažimanje datoteke n gotova, dok datoteka n-1 još uvijek nije poslana, dretva n će pričekati sa slanjem prije početka obrade sljedeće datoteke.Svaka datoteka se sažima i šalje točno jedanput, no samo se proces sažimanja može izvoditi u paraleli.Napomena: U svakoj poređenoj petlji može postojati samo jedan ordered odjeljak. Uz to, pragma zaglavlja petlje for mora sadržavati klauzulu ordered.

2.3. Sekcije

Ponekad je svrsishodno naznačiti da se pojedini dijelovi programa, za razliku od petlji, mogu izvoditi u paraleli. Upravo zbog toga postoji direktiva sections:

7

Page 11: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

#pragma omp sections{

{ Work1(); }#pragma omp section

{ Work2();Work3(); }

#pragma omp section{ Work4(); }

}

Ovaj programski kod označava da se bilo koji od zadataka Work1, Work2 + Work3 i Work4 može izvoditi u paraleli, no zadaci Work2 i Work3 se moraju slijedno izvesti. Svaki zadatak se izvodi točno jedanput.Kao i uobičajeno, ukoliko prevodilac ignorira pragma linije, rezultat je još uvijek program koji će se ispravno izvoditi, no u tom slučaju samo slijedno.Interno, GCC prevodi ovakvu konstrukciju kao kombinaciju paralelne petlje for sa switch-case konstruktom. Ostali ih prevodioci mogu drukčije implementirati.

Napomena: #pragma omp sections raspoređuje dijelove koda po različitim dretvama u trenutnom timu. Kako bi se kreirao novi tim, potrebno je navesti ključnu riječ parallel, i to ili u okolnom kontekstu ili u samoj pragmi kao #pragma omp parallel sections.

Primjer: #pragma omp parallel sections // stvara novi tim{

{ Work1(); }#pragma omp section

{ Work2();Work3(); }

#pragma omp section{ Work4(); }

}

ili#pragma omp parallel // stvara novi tim{

//Work0(); // ova funkcija bi se izvela u svim dretvama tima#pragma omp sections // dijeli tim u sekcije

{ // sljedeći kod se izvodi samo jedanput

8

Page 12: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

{ Work1(); }#pragma omp section

{ Work2();Work3(); }

#pragma omp section{ Work4(); }

}

//Work5(); // ova funkcija bi se izvela u svim dretvama tima}

2.4. Zadaci

U slučajevima kada se konstrukti for i sections čine prenespretnim za korištenje, umjesto njih se može upotrijebiti direktiva zadatka task.

Napomena: Ovo je podržano samo u verziji OpenMP 3.0.Primjer uporabe iz službene OpenMP dokumentacije:struct node { node *left, *right; };extern void process(node* );void traverse(node* p){

if (p->left)#pragma omp task // stvara novo dijete dretvu

traverse(p->left);if (p->right)

#pragma omp task // stvara novo dijete dretvutraverse(p->right);

process(p); // daljnje izvršavanje se nastavlja bez prekida}

U sljedećem primjeru, korištenjem direktive task prisiljavamo program na tzv. "postorder" obilaženje stabla. Na taj smo način sigurni da će se lijevo i desno dijete obraditi prije procesiranja trenutnog čvora.struct node { node *left, *right; };extern void process(node* );void postorder_traverse(node* p){

if (p->left)#pragma omp task // stvara novo dijete dretvu

9

Page 13: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

postorder_traverse(p->left);if (p->right)

#pragma omp task // stvara novo dijete dretvupostorder_traverse(p->right);

#pragma omp taskwait // čeka na završetak prethodnih dviju dretviprocess(p);

}

U sljedećem je primjeru demonstrirano korištenje direktive task kod paralelne obrade elemenata povezane liste. Pokazivač (engl. pointer) p je implicitno tipa firstprivate, tako da nije potrebno eksplicitno navođenje klauzule firstprivate.struct node { int data; node* next; };extern void process(node* );void increment_list_items(node* head){#pragma omp parallel

{#pragma omp single // sljedeći dio koda se izvršava od

{ // strane samo jedne dretve osim ukoliko// nije navedeno drugačije

for(node* p = head; p; p = p->next){

#pragma omp task // stvara novo dijete dretvuprocess(p);

}}

}}

U prethodnom se kodu korištenjem direktive single osigurava da glavni dio petlje for izvodi samo jedna dretva. Sama petlja iterira kroz sve elemente zadane liste i stvara novu dretvu za paralelno procesiranje svakog od njih, što znači da će se zajedno s iteriranjem petlje paralelno izvoditi i nekoliko dretvi u kojima će se obrađivati funkcija process(p). Kao što se može i primjetiti, ovaj gornji programski kod predstavlja ručnu paralelizaciju petlje koju OpenMP nije u stanju razlučiti (iteriranje pokazivača).

2.5. Kontrola dijeljenja varijabli između dretvi

U paralelnom odjeljku je moguće zadati koje će varijable biti dijeljene (odnosno njihov sadržaj) između različitih dretvi te koje neće. Ukoliko se ništa ne zada, sve varijable

10

Page 14: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

postaju dijeljene (engl. shared) osim onih deklariranih u paralelnom odjeljku – takve varijable postaju privatne (engl. private).

2.5.1. Klauzule private, firstprivate i sharedint a, b=0;#pragma omp parallel for private(a) shared(b) // u ovom bloku a postajefor(a=0; a<50; ++a) // privatna dok b postaje{ // dijeljena varijabla#pragma omp atomic

b += a;}

U ovom je primjeru eksplicitno specificirano da je varijabla a privatna (svaka dretva posjeduje njezinu kopiju) te da je b dijeljena varijabla (svaka dretva pristupa istoj varijabli).Treba imati na umu da je varijabla tipa private neinicijalizirana kopija izvorne varijable istog imena i tipa. To između ostalog znači da se sadržaj izvorne varijable ne kopira u varijablu tipa private.#include <string>#include <iostream>using namespace std;

int main(){

string a = "x", b = "y";int c = 3;

#pragma omp parallel private(a,c) shared(b) num_threads(2){

a += "k";c += 7; // neispravnocout << "a = (" << a << "), b = (" << b << ")\n";

}}

U ovom će se slučaju ispisati "k", a ne "xk" kako se možda čini na prvi pogled. Na ulazu u paralelni odjeljak, u varijablu a se sprema instanca klase std::string koja se inicijalizira s podrazumijevanim (engl. default) konstruktorom; valja primjetiti da se inicijalizacija ne izvodi kopirajućim konstruktorom (engl. copy constructor), odnosno da se vrijednost varijable a izvan odjeljka ne kopira u pripadnu privatnu varijablu.

11

Page 15: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

Prevoditelj interno prevodi prethodni primjer u programski kod ekvivalentan sljedećem:int main(){

std::string a = "x", b = "y";int c = 3;

OpenMP_thread_fork(2);{ // početak novog odjeljka

std::string a; // nova lokalna varijabla aint c; // nova lokalna varijabla ca += "k";c += 7;std::cout << "a = (" << a << "), b = (" << b << ")\n";

} // krajnji doseg lokalnih varijabli a i cOpenMP_join();

}

U slučaju primitivnih tipova podataka (int, float, char*,...), privatne varijable se ne inicijaliziraju, kao ni u slučaju svake deklarirane i eksplicitno neinicijalizirane lokalne varijable. To znači da se vrijednost originalne varijable izvan lokalnog konteksta ne kopira. Stoga je u danom primjeru neispravno izvoditi operaciju zbrajanja nad varijablom c stoga što je njezina vrijednost u tom trenutku nedefinirana.

Napomena: Na nesreću, GCC do verzije 4.2.1 kod prevođenja još uvijek ne upozorava na situacije poput ove.No, u slučajevima kada je potrebna kopija originalne vrijednosti, preporuča se korištenje klauzule firstprivate umjesto private:#include <string>#include <iostream>using namespace std;int main(){

string a = "x", b = "y";int c = 3;

#pragma omp parallel firstprivate(a,c) shared(b) num_threads(2){

a += "k";c += 7;

12

Page 16: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

cout << "a = (" << a << "), b = (" << b << ")\n";}

}

U ovom se slučaju ispisuje sljedeće: "a = (xk), b = (y)".

2.5.2. Klauzula lastprivateKlauzulom lastprivate definiramo privatnu varijablu, kao i kod klauzula firstprivate i private, no razlika je utoliko što se nakon izvršenja paralelnog bloka naredbi vrši kopiranje vrijednosti iz lokalne natrag u pripadnu originalnu varijablu.

• U petlji (direktiva for), vrijednost koja se kopira u originalnu varijablu je ona dobivena u zadnjoj iteraciji petlje. Vrijednosti dobivene za vrijeme ostalih iteracija se ignoriraju.

• U paralelnim sekcijama (direktiva sections), vrijednost koja se kopira natrag u originalnu varijablu je ona koja se postavlja za vrijeme izvođenja posljednje sekcije (direktiva section). Vrijednosti iz ostalih sekcija se igoriraju.

#include <stdio.h>int main(){

int done = 4, done2 = 5;#pragma omp parallel for lastprivate(done, done2) num_threads(2) schedule(static)

for(int a=0; a<8; ++a){

if(a==2) done = done2 = 0;if(a==3) done = done2 = 1;

}printf("%d,%d\n", done, done2);

}

U prethodnom se primjeru ispisuje neodređen znakovni niz (npr.: "4196224,-348582208"), stoga što ga prevodioc prevodi u programski kod ekvivalentan sljedećem:#include <stdio.h>int main(){

int done = 4, done2 = 5;OpenMP_thread_fork(2); // stvara dvije nove dretve{

13

Page 17: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

int this_thread = omp_get_thread_num(), num_threads = 2;int my_start = (this_thread ) * 8 / num_threads;int my_end = (this_thread+1) * 8 / num_threads;int priv_done, priv_done2; // nisu inicijalizirane stoga što nije

// korištena klauzula firstprivate

for(int a=my_start; a<my_end; ++a){

if(a==2) priv_done = priv_done2 = 0;if(a==3) priv_done = priv_done2 = 1;

}if(my_end == 8){

// zapisuje vrijednosti natrag u pripadne vanjske // varijable stoga što je riječ o zadnjoj iteraciji

done = priv_done;done2 = priv_done2;

}}OpenMP_join(); // spaja dretve natrag u jednuprintf("%d,%d\n", done, done2);

}

Kao što se može primjetiti, vrijednosti varijabli priv_done i priv_done2 se ne pridodjeljuju niti jednom tijekom izvođenja petlje kad je vrijednost varijable a u rasponu [4..7]. Kao takve, vrijednosti koje se zapisuju natrag u pripadne originalne varijable su u potpunosti neodređene. Stoga se klauzula lastprivate ne može koristiti u slučajevima kad se dohvaća vrijednost varijable koja se zapisuje na nekom neodređenom mjestu u petlji – u tom se slučaju koristi klauzula reduction.

No, ovakvo se ponašanje može iskoristiti u situacijima poput (primjer iz OpenMP dokumentacije):void loop(){

int i;#pragma omp for lastprivate(i)

for(i=0; i<get_loop_count(); ++i){ ... }printf("%d\n", i); // ovdje se ispisuje ukupan broj izvršenih iteracija.

}

14

Page 18: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

2.5.3. Klauzula defaultOva se klauzula najčešće koristi u slučajevima kada želimo da svaka varijabla korištena u paralelnom odjeljku ima eksplicitno pridodjeljeno stanje dijeljenosti (private ili shared), što se osigurava korištenjem opcije default(none).int a, b=0;// programski prevodioc neće htjet obradit // ovaj kod stoga što dijeljenost// varijable a nije eksplicitno navedena#pragma omp parallel default(none) shared(b){

b += a;}

Klauzula default se također može koristiti kako bi se svim varijablama pridodjelilo dijeljeno stanje dijeljenosti (default(shared)).

Napomena: Pošto različiti prevodioci imaju različite ideje o tome koje su varijable implicitno privatne, a koje dijeljene, te za koje je varijable neispravno eksplicitno pridodjeljivati stanje dijeljenosti, preporučeno je korištenje default(none) opcije tijekom razvoja aplikacije, te njezino brisanje tijekom završnog uređivanja koda.

2.5.4. Klauzula reductionKlauzula reduction je kombinacija klauzula private, shared i atomic. Omogućuje akumuliranje dijeljene varijable bez eksplicitnog navođenja klauzule atomic (osigurava atomnost navedene naredbe i/ili bloka naredbi). Najčešće će kao krajnji rezultat biti brži i pregledniji programski kod. Napomena: Detaljan opis klauzule atomic potražite u odjeljku 2.6.1.

U sljedećem se primjeru računa vrijednost matematičke funkcije faktorijel korištenjem više paralelnih dretvi:int factorial(int number){

int fac = 1;

#pragma omp parallel for reduction(*:fac)for(int n=2; n<=number; ++n)

fac *= n;

return fac;}

15

Page 19: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

Za bolje razumijevanje rada ove klauzule bitno je znati:

• Na početku se svakog paralelnog odjeljka stvara privatna kopija varijable te se inicijalizira na neku određenu vrijednost.

• Na kraju paralelnog odjeljka, privatna kopija se atomno spaja s ostalim dijeljenim varijablama korištenjem navedenog operatora.

Sintaksa klazule je sljedeća: reduction(operator:list)pri čemu list predstavlja listu varijabli nad kojima će se operator izvršiti, dok je vrijednost za operator jedna od:

Operator Inicijalna vrijednost+, -, |, ^, || 0

*, && 1& ~0

Ukoliko bi se faktorijel funkcija pisala bez ove klauzule, tada bi ona izgledala otprilike ovako:int factorial(int number){

int fac = 1;#pragma omp parallel for

for(int n=2; n<=number; ++n){

#pragma omp atomicfac *= n;

}return fac;

}

Ovaj je kod inferiorniji onom s redukcijom – nedostaje mu mogućnost korištenja lokalne (vrlo često registarske) varijable za akumulaciju te se nepotrebno zadaju zahtjevi za sinkronizacijom pristupa varijabli. U stvari, zbog samog korištenja atomarnog pristupa varijabli (samo joj jedna dretva može pristupiti u određenom trenutku), poništavaju se eventualni dobitci paralelizacije petlje.Verzija s redukcijom je ekvivalentna sljedećem programskom kodu (samo za ilustraciju):int factorial(int number){

16

Page 20: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

int fac = 1;#pragma omp parallel

{int fac_private = 1;

#pragma omp for nowaitfor(int n=2; n<=number; ++n)

fac_private *= n;#pragma omp atomic

fac *= fac_private;}return fac;

}

Napomena: Primjetite kako se direktiva atomic nalazi izvan petlje.

Ograničenja kod klauzule reduction i direktive atomic su vrlo slična: obje se mogu koristiti samo s primitivnim tipovima podataka, nijedna ne dozvoljava korištenje prepravljenih (engl. overloaded) operatora, te obje podržavaju isti skup operatora.U sljedećem se primjeru kao ilustracija prikazuje kako klauzula reduction može proizvesti semantički različit programski kod u slučajevima kad je OpenMP podržan od strane prevodioca i onda kad nije podržan:int a = 0;#pragma omp parallel reduction (+:a){

a = 1; // pridodjeljuje vrijednost privatnoj kopiji} // u ovom trenutku svaka dretva inkrementira vrijednost

// vanjske varijable za 1printf("%d\n", a);

Ukoliko se varijabla a inicijalizira vrijednošću 4 van paralelnog odjeljka, tada će se u ovom primjeru ispisati broj >= 5 ukoliko je OpenMP podrška omogućena, odnosno 1 ukoliko nije.Napomena: Ukoliko doista želite detektirati da li je OpenMP podržan od strane prevodioca, preporuča se korištenje #ifdef _OPENMP pretprocesorske naredbe. Ukoliko želite dobiti stvaran broj dretvi, koristite naredbu omp_get_ num_threads().#ifdef _OPENMPprintf("OpenMP je podržan od strane korištenog prevodioca");#elseprintf("OpenMP nije podržan od strane korištenog prevodioca");#endif

17

Page 21: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

2.6. Dretvena sigurnost (međusobnim isključivanjem)

Postoji široko polje problema konkurentnosti i međusobnog isključivanja koji se pojavljuju kod pisanja višedretvenih programa. Svima njima se nećemo baviti na ovom mjestu radi opširnosti teme.U ovom će se poglavlju opisati dijelovi OpenMP-a namijenjeni pravilnom baratanju mehanizmima međusobnog isključivanja dretvi.

2.6.1. Atomnost

Pojam atomnost (engl. atomicity) znači da je nešto nerazdvojno – događaj se u potpunosti izvrši ili se uopće ne izvrši, dok se pri tome ostale dretve ne mogu uplitati u obradu takvog događaja.#pragma omp atomiccounter += value;

Ključna riječ atomic u OpenMP-u označava da se navedena akcija/naredba izvršava atomno. Ovaj se mehanizam često koristi kako bi se zapisao novi sadržaj u brojačku varijablu i ostale jednostavne varijable kojima se pristupa simultano iz višestrukih dretvi.Nažalost, atomnost se može primijeniti samo na jednostavne izraze i to obično one koji se mogu prevesti u jednu procesorsku naredbu, poput inkremenata, dekremenata, logičkih naredbi i njima sličnih. Tako se na primjer ne mogu koristiti pozivi funkcije, pristupanje poljima, blokovi naredbi i složeni tipovi podataka. Nadalje, atomnost je zajamčena samo za akcije koje se tiču lijeve strane operacije – to znači da nije zagarantirano da će se izraz s desne strane operatora računati atomno. Ukoliko je potrebno atomizirati složenije izraze, tada se koristi direktiva critical ili mehanizam brave (engl. lock).Također je moguće rješenje problema korištenje mehanizma redukcije (engl. reduction).

2.6.2. Kritični odjeljci

Direktiva critical ograničava izvršavanje pripadajuće naredbe i/ili bloka naredbi od strane samo jedne dretve u bilo kojem trenutku. Također, može opcionalno sadržavati globalni naziv koji predstavlja univerzalni identifikator mehanizma. Na taj se način osigurava da dvije dretve ne mogu istodobno izvršavati direktivu critical s istim imenom. Ukoliko se identifikator izostavi, pretpostavlja se opće ime. #pragma omp critical(dataupdate){

datastructure.reorganize();}

18

Page 22: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

...#pragma omp critical(dataupdate){

datastructure.reorganize_again();}

U datom se primjeru može izvršavati samo jedna kritična sekcija pod imenom dataupdate u bilo kojem trenutku i to od strane samo jedne dretve; funkcije reorganize() i reorganize_again() se ne mogu izvoditi istovremeno, osim ukoliko se ne pozivaju s nekog drugog mjesta, nezaštićenog direktivom critical.

Napomena: Imena kritičnih odjeljaka (engl. sections) su globalna na razini cijelog programa, bez obzira na različite module. To znači da ukoliko postoje dva kritična odjeljka s istim imenom u dva različita modula (datoteke), u svakom se trenutku može izvršavati sadržaj samo jednog od njih.

2.6.3. Mehanizam brave

OpenMP knjižnica izvođenja omogućava korištenje mehanizma brave omp_lock_t dodavanjem datoteke zaglavlja omp.h u datom programu.

Postoji pet funkcija za manipuliranjem navedenim mehanizmom:

• omp_init_lock inicijalizira bravu. Nakon poziva, brava je otključana.• omp_destroy_lock uništava bravu. Brava mora biti otključana prije poziva.• omp_set_lock pokušava da zaključa bravu. Ukoliko je brava već zaključana od

strane druge dretve, čekat će sve dok se brava ne otključa kako bi ju potom zaključala.

• omp_unset_lock otključava bravu. Trebala bi se pozvati od strane dretve koja je dretvu i zaključala; ukoliko se navedeno pravilo ne ispoštuje, posljedice su nedefinirane.

• omp_test_lock pokušava da zaključa bravu. Ukoliko je već prethodno zaključana od strane druge dretve, vraća rezultat 0; ukoliko je brava uspješno postavljena, vraća 1.

Slijedi primjer u kojem se oko standardnog tipa podataka std::set<> stavlja ovojnica (engl. wrapper) koja omogućuje korištenje mehanizma brave u slučaju da je OpenMP podržan od strane prevodioca; u protivnom se program normalno izvršava, no bez podrške mehanizma brave.#ifdef _OPENMP#include <omp.h>#endif#include <set>using namespace std;

19

Page 23: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

class data{private:

set<int> flags;#ifdef _OPENMP

omp_lock_t lock;#endifpublic:

data() : flags(){

#ifdef _OPENMPomp_init_lock(&lock);

#endif}~data(){

#ifdef _OPENMPomp_destroy_lock(&lock);

#endif}

bool set_get(int c){

#ifdef _OPENMPomp_set_lock(&lock);

#endifbool found = flags.find(c) != flags.end();if(!found) flags.insert(c);

#ifdef _OPENMPomp_unset_lock(&lock);

#endifreturn found;

}};

Također se preporuča kreiranje zasebne strukture za korištenje mehanizma brave, kako bi se izbjeglo "prljanje" koda s pretprocesorskim direktivama #ifdef, te kako bi se omogućilo lakše baratanje iznimkama (engl. Exceptions):

20

Page 24: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

#ifdef _OPENMP# include <omp.h>/* Struktura koja omogućuje jednostavno korištenje * OpenMP mehanizma brave. */ struct MutexType{

MutexType() { omp_init_lock(&lock); }~MutexType() { omp_destroy_lock(&lock); }void Lock() { omp_set_lock(&lock); }void Unlock() { omp_unset_lock(&lock); }

MutexType(const MutexType& ) { omp_init_lock(&lock); }MutexType& operator= (const MutexType& ) { return *this; }

public:omp_lock_t lock;

};#else/* Struktura koja nema nikakvu praktičnu vrijednost, no* pošto i nema paralelizma, nema brige oko međusobnog * isključivanja dretvi. */struct MutexType{

void Lock() {}void Unlock() {}

};#endif

/* Primjer strukture koja služi kao posrednik mehanizma brave,* bez obzira da li je OpenMP podržan od strane prevodioca ili nije. */struct ScopedLock{

explicit ScopedLock(MutexType& m) : mut(m), locked(true) { mut.Lock(); }~ScopedLock() { Unlock(); }void Unlock() { if(!locked) return; locked=false; mut.Unlock(); }void LockAgain() { if(locked) return; mut.Lock(); locked=true; }

private:MutexType& mut;

21

Page 25: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

bool locked;private: // spriječi kopiranje brave

void operator=(const ScopedLock&);ScopedLock(const ScopedLock&);

};

Korištenjem ovih struktura, prijašnji primjer postaje dosta pregledniji:#include <set>using namespace std;

class data{private:

set<int> flags;MutexType lock;

public:bool set_get(int c){

ScopedLock lck(lock); // zaključava bravuif(flags.find(c) != flags.end())

return true; // zastavica pronađenaflags.insert(c);return false; // zastavica nije pronađena

} // automatski otpušta bravu kada lck izađe iz dosega};

Također postoji i mehanizam brave namijenjen ugnježđivanju (engl. nesting) – omp_nest_lock_t, no za daljnje detalje se preporuča uvid u službenu OpenMP dokumentaciju.

2.7. Sinkronizacija izvođenja

2.7.1. Direktiva barrier i klauzula nowaitDirektiva barrier uzrokuje čekanje kod dretve sve do trenutka dok ostale dretve iz tima ne dođu na tu istu poziciju.#pragma omp parallel{

// sve dretve izvode ovu naredbuSomeCode();

22

Page 26: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

#pragma omp barrier// sve dretve izvode ovu naredbu, no ne prije nego završe s izvođenjem// prethodne naredbe SomeCode()SomeMoreCode();

}

Napomena: Na kraju svakog paralelnog odjeljka postoji implicitna barijera, te također i na kraju svake sections, for te single konstrukcije, osim ukoliko se ne koristi direktiva nowait.

Primjer: #pragma omp parallel{#pragma omp for

for(int n=0; n<10; ++n) Work();// do ove linije se ne stiže prije nego se gornja// petlja izvrši u potpunosti

SomeMoreCode();}

// do ove linije se ne stiže sve dok sve dretve iz// prethodnog paralelnog odjeljka ne završe s izvođenjemCodeContinues();

#pragma omp parallel{#pragma omp for nowait

for(int n=0; n<10; ++n) Work();

// do ove linije se može stići i za vrijeme dok ostale dretve još izvode // prethodnu petljuSomeMoreCode();

}// do ove se linije stiže tek kad sve dretve iz prethodnog odjeljka // završe s radomCodeContinues();

Napomena: Klauzula nowait može biti upotrebljena isključivo u kombinaciji s direktivama sections, for i single.

23

Page 27: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

2.7.2. Direktive single i masterDirektiva single označava da se zadana naredba/blok izvršava od strane samo jedne dretve, dok je sama dretva koja ga izvodi neodređena. Ostale dretve preskaču zadanu naredbu/blok i čekaju na implicitnoj barijeri na kraju konstrukta.#pragma omp parallel{

Work1();#pragma omp single

{Work2();

}Work3();

}

U dvoprocesorskom sistemu, ovo će pokrenuti Work1() dva puta, Work2() jedanput te Work3() dva puta. Postoji implicitna barijera na kraju direktive single, no ne i na početku.Napomena: Nemojte misliti da single odjeljak izvršava ona dretva koja tamo dođe prva. Prema standardu, odluka o tome ovisi o implementaciji OpenMP mehanizma, te s time donošenje preuranjenih zaključaka može dovesti do neočekivanog ponašanja programa. U GCC-u, odluka je sakrivena u logici libgomp biblioteke izvođenja (engl. runtime library).Direktiva master je po ponašanju vrlo slična, osim što se naredba/blok izvršava od strane glavne dretve (engl. master thread) te ne postoji implicitna barijera – ostale dretve preskaču ovaj konstrukt i nastavljaju bez čekanja.#pragma omp parallel{

Work1();

// Ovo...#pragma omp master

Work2();

// ...je praktički identično ovom:if(omp_get_thread_num() == 0)

Work2();

Work3();}

24

Page 28: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

Osim ukoliko se ne koristi klauzula threadprivate, jedina bitna razlika između single, nowait i master je ta da ukoliko imate višestruke master blokove unutar parallel odjeljka, vama je zajamčeno da će ti blokovi biti izvršeni od strane jedne te iste dretve svaki put, te će stoga i vrijednosti private varijabli (lokalne po dretvama) biti jednake, dok u ostalim slučajevima to ne mora biti tako.

2.8. Očuvanje konzistentnosti podataka

Čak i ukoliko su varijable korištene u dretvama pretpostavljeno dijeljene (engl. shared), programski prevodioc može uzeti slobodu i pri optimizaciji ih pretvoriti u registarske varijable. U tom slučaju može doći do ometanja konkurentnog pristupanja sadržaju varijable iz različitih dretvi. Direktiva flush se upotrebljava kako bi se osigurala konzistentnost podataka – promatrana vrijednost varijable u dretvi zajamčeno postaje ista ona vrijednost koja se može pročitati i iz ostalih dretvi.Primjer iz OpenMP specifikacije: // pretpostavka: int a = 0, b = 0; /* Prva dretva */ /* Druga dretva */ b = 1; a = 1; #pragma omp flush(a,b) #pragma omp flush(a,b) if(a == 0) if(b == 0) { { /* Kritični odjeljak */ /* Kritični odjeljak */ } }

U ovom je primjeru zadano da u vrijeme pristupa vrijednostima varijabli a ili b, druga mora sadržavati svoj aktualni sadržaj. Time se praktički osigurava to da ni u jednom trenutku obje dretve ne ulaze u kritični odjeljak (engl. critical section). Napomena: Može se također dogoditi da nijedna dretva ne može ući u kritični odjeljak.Nužno je koristiti direktivu flush kad se isti podatak čita i piše iz različitih dretvi (poslije pisanja i prije čitanja).Ukoliko program naizgled radi ispravno bez ove direktive, to ne znači da ona nije potrebna. Može se vrlo lako dogoditi da trenutni prevodioc ne dozvoljava sve slobode koje OpenMP specifikacija navodi, te vlastitim internim mehanizmom sprečava problem konkurentnosti. U tom slučaju problemi vjerojatno nastupaju korištenjem drugog programskog prevodioca.

25

Page 29: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

2.9. Ugniježđivanje petlji

2.9.1. Problem

Ukoliko malo bolje proučite sljedeći programski kod, vrlo brzo ćete uvidjeti da se prilikom izvršavanja ne ponaša onako kao što se od njega očekuje:#pragma omp parallel forfor(int y=0; y<25; ++y){#pragma omp parallel for

for(int x=0; x<80; ++x){

tick(x,y);}

}

Početnik očekuje N tick() aktivnih poziva u isto vrijeme (gdje N predstavlja broj procesora). Iako je to istina, unutrašnja petlja se zapravo ne paralelizira – paralelizira se jedino vanjska petlja. Unutrašnja petlja se izvodi čisto sekvencijalno, kao da je cijela unutrašnja pragma linija izostavljena.Na ulasku u unutrašnji paralelni blok, OpenMP knjižnica izvođenja detektira postojanje tima, te umjesto kreiranja novog tima s N novih dretvi, stvara tim koji se sastoji od samo pozivajuće dretve.Prepravljanje koda u ovaj oblik neće ispravno raditi:#pragma omp parallel forfor(int y=0; y<25; ++y){#pragma omp for // GREŠKA, ugniježđivanje poput ovoga nije dozvoljeno

for(int x=0; x<80; ++x){

tick(x,y);}

}

Ovaj programski kod je neispravan radi nedopuštene vrste ugniježđivanja te će uzrokovati neispravno izvođenje programa.

2.9.1.1. Rješenje (OpenMP 3.0)

U OpenMP verziji 3.0, problem ugniježđenih petlji se može riješiti korištenjem klauzule collapse unutar direktive petlje for.

26

Page 30: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

Primjer:#pragma omp parallel for collapse(2)for(int y=0; y<25; ++y)for(int x=0; x<80; ++x){

tick(x,y);}

Broj naveden u klauzuli collapse predstavlja broj ugniježđenih petlji koje će biti prepoznate i na pravilan način obrađene od strane prevodioca.

2.9.1.2. Moguća rješenja (OpenMP 2.5)

Nažalost, do verzije 4.3, GCC još uvijek ne podržava specifikaciju OpenMP 3.0. No moguća rješenja problema postoje za OpenMP 2.5. Naime, problem se može riješiti raspetljavanjem ugniježđene petlje u običnu petlju:#pragma omp parallel forfor(int pos=0; pos<(25*80); ++pos){

int x = pos%80;int y = pos/80;tick(x,y);

}

No, prepisivanje koda u ovaj oblik može biti iznimno naporno.Alternativa je omogućavanje stvaranja ugniježđenih dretvi.omp_set_nested(1); //postavljanje zastavice za ugniježđivanje#pragma omp parallel forfor(int y=0; y<25; ++y){#pragma omp parallel for

for(int x=0; x<80; ++x){

tick(x,y);}

}

U ovom je primjeru unutrašnja petlja također paralelizirana. No, umjesto N dretvi, program izvršava N*N dretvi, stoga što korištenjem ugniježđene direktive parallel započinje novi tim od N dretvi, umjesto tima sa samo jednom dretvom. Ovo često

27

Page 31: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

2.Osnove

može biti neželjeno ponašanje programa, tako da je zastavica za ugniježđivanje uobičajeno isključena.Stoga bi bilo poželjno pisati kod na sljedeći način:for(int y=0; y<25; ++y){#pragma omp parallel for

for(int x=0; x<80; ++x){

tick(x,y);}

}

U ovom se slučaju samo unutrašnja petlja izvodi u paraleli. Ova metoda je (najčešće) samo malo manje optimalna od metode raspetljavanja ugniježđenih petlji.

2.9.1.3. Performanse

Korisnik bi se mogao zabrinuti za brzinu izvođenja programa zbog kreiranja novih dretvi unutar unutarnje petlje. No, knjižnica izvođenja libgomp u GCC-u je dovoljno "pametna" da kreira dretve samo jedanput. Jednom kad se posao završi dretve se stavljaju u stanje pripravnosti čekajući na izvršavanje novog zadatka.Drugim riječima, broj zvanja sistemskog poziva clone je upravo onoliki koliki je maksimalni broj konkurentnih dretvi – direktiva parallel se ne prevodi u doslovnu kombinaciju poziva funkcija pthread_create i pthread_join za upravljanje dretvama.

No, svejedno ostaje problem zaključavanja/odključavanja zbog implicitnih barijera – ovaj se problem ne može izbjeći na jednostavan način.

2.9.2. Ograničenja

Sam popis ograničenja kod ugniježđivanja je pomalo nepovezan tako da se za daljnje informacije savjetuje korištenje službene OpenMP dokumentacije.

3. Nedostaci

3.1. OpenMP i fork()Kod pisanja OpenMP programa potrebno je voditi posebnu brigu kod korištenja sistemske funkcije fork(). Valja napomenuti da ovaj problem zahvaća samo razvojni paket GCC, dok Intelov ICC ne pati od navedenih boljki.Ukoliko vaš program namjerava postati pozadinski proces (korištenjem sistemskog poziva daemonize() ili sl.), OpenMP se ne smije koristiti prije istog. Nakon korištenja

28

Page 32: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

3.Nedostaci

OpenMP-a, stvaranje novog procesa je dopušteno jedino ukoliko se u procesu djetetu ne koristi OpenMP ili ukoliko se OpenMP koristi u potpuno neovisnom procesu (kao u slučaju sistemskog poziva exec()).

Evo primjera jednog takvog "grešnog" programa:#include <stdio.h>#include <sys/wait.h>#include <unistd.h>

void a(){#pragma omp parallel num_threads(2)

{puts("para_a"); // dvostruki ispis

}puts("a ended"); // jednostruki ispis

}

void b(){#pragma omp parallel num_threads(2)

{puts("para_b");

}puts("b ended");

}

int main() {a(); // korištenje OpenMP funkcionalnosti (u procesuint p = fork(); // roditelj)if(!p){

b(); // GREŠKA: OpenMP se ponovno koristi, no_exit(0); // ovaj put u procesu djeteta

}wait(NULL);return 0;

}

29

Page 33: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

3.Nedostaci

Kad se pokrene, ovaj program otkazuje poslušnost, bez ispisa znakovnog niza "b ended".

Trenutno nema rješenja za ovaj problem – GCC libgomp knjižnica izvođenja ne sadrži funkciju koja bi ju pripremila na izvođenje poziva fork().

3.2. Prekid izvođenja dretvi (i izlaženje iz petlji)

Recimo da želimo optimizirati sljedeću funkciju korištenjem paralelizacije:// vraća poziciju na kojem se unutar zadanog polja 'haystack' može naći dati// znak 'needle', ili vraća NULL ukoliko takva pozicija nije pronađenaconst char* FindAnyNeedle(const char* haystack, size_t size, char needle){

for(size_t p = 0; p < size; ++p)if(haystack[p] == needle){

return haystack + p;}return NULL;

}

Prvi korak bi vjerojatno bio stavljanje konstrukcije #pragma parallel for prije petlje for, no to neće raditi stoga što OpenMP zahtjeva procesiranje svake iteracije unutar petlje pod njegovom kontrolom. Eksplicitno izlaženje iz petlje (korištenjem naredbi return, goto, break, throw ili sl.) nije dozvoljeno.

Kako bi se riješio problem izvršavanja ovakvog tipa zadatka, u kojem N dretvi traži na rješenje, te kad se jednom nađe u jednoj od njih da sve ostale završe s daljnjim traženjem, moraju se koristiti druge metode:

• Korištenjem sistemskog dretvenog sustava (pthread_cancel) umjesto OpenMP-a

• Korištenjem zastavice

Primjer korištenja zastavice (u ovom slučaju done):const char* FindAnyNeedle(const char* haystack, size_t size, char needle){

const char* result = NULL;bool done = false;

#pragma omp parallel forfor(size_t p = 0; p < size; ++p){

30

Page 34: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

3.Nedostaci

#pragma omp flush(done)if(!done){

// nastavi s poslom samo ukoliko nijedna druga dretva nije // pronašla traženi znakif(haystack[p] == needle){

// obavijesti ostale dretve da je traženi znak // uspješno pronađendone = true;

#pragma omp flush(done)result = haystack+p;

}}

}return result;

}

Ova verzija ima problema s vremenom izvođenja – ukoliko dretve detektiraju da je pretraga gotova (zastavica done je postavljena), one svejedno nastavljaju s daljnjim iteracijama u petlji. Kako bi se to spriječilo zastavica done se može izostaviti u cijelosti:const char* FindAnyNeedle(const char* haystack, size_t size, char needle){

const char* result = NULL;

#pragma omp parallel forfor(size_t p = 0; p < size; ++p)

if(haystack[p] == needle)result = haystack+p;

return result;}

Na ovaj se način ne štedi na čitanju memorije; svaka pozicija polja haystack se svejedno pretražuje. Nadalje, vrijednost varijable result se može postaviti više puta i to svaki puta kod pronalaska tražene vrijednosti polja. Ukoliko dođe do više takvih pronalazaka, može se dogoditi da izvođenje bude puno sporije od neparalelizirane sekvencijalne verzije algoritma.Jedini način kako da se izbjegnu dodatne iteracije je izbjegavanje korištenja OpenMP direktive for i ručno oponašanje njezinog ponašanja:

31

Page 35: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

3.Nedostaci

const char* FindAnyNeedle(const char* haystack, size_t size, char needle){

const char* result = NULL;bool done = false;

#pragma omp parallel{

int this_thread = omp_get_thread_num(), num_threads = omp_get_num_threads();

size_t beginpos = (this_thread+0) * size / num_threads;size_t endpos = (this_thread+1) * size / num_threads;

for(size_t p = beginpos; p < endpos; ++p){

// zaustavi petlju ukoliko je neka druga dretva pronašla // traženi znak.

#pragma omp flush(done)if(done) break;

if(haystack[p] == needle){

// obavijesti ostale dretve da je traženi znak // uspješno pronađen.done = true;

#pragma omp flush(done)result = haystack+p;break;

}}

}

return result;}

U ovoj se verziji suvišne iteracije ne izvode, no još uvijek stoji nezgodna činjenica da se čitanje zastavice done sinkronizira direktivom flush prilikom izvođenja svake iteracije. Također postoji i "srednji" put u kojem se petlja može podijeliti na manje dijelove, u kojima se neće vršiti sinkronizacija zastavice već će se samo provjeravati njezina vrijednost između izvođenja pojedinih dijelova:

32

Page 36: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

3.Nedostaci

const char* FindAnyNeedle(const char* haystack, size_t size, char needle){

const char* result = NULL;bool done = false;size_t beginpos = 0, n_per_loop = 4096 * omp_get_max_threads();while(beginpos < size && !done){

size_t endpos = min(size, beginpos + n_per_loop);#pragma omp parallel for reduction(|:done)

for(size_t p = beginpos; p < endpos; ++p)if(haystack[p] == needle){

// znak je uspješno pronađendone = true;result = haystack+p;

}beginpos = endpos;

}return result;

}

Korištenjem ove metode se ne garantira da se neće izvršiti koja usporedba više; vrijednost varijable result se može postaviti više puta u funkciji.

Ukoliko vam je i ovo neprihvatljivo, tada OpenMP ne nudi više ništa dodatnog. U tom slučaju možete probati koristiti sistemski dretveni sustav, odnosno pozive pthread_create kako bi kreirali timove te pthread_cancel kako bi prekidali njihovo izvođenje. Bitno je napomenuti da to rješenje nije prenosivo na različite platforme te je izvan teme ovog uratka.

3.3. Ostalo

Od ostalih nedostataka, problema i ograničenja navode se:• Potrebna je uporaba programskog prevoditelja s eksplicitnom podrškom za

OpenMP• Trenutno se prevedeni programi izvode učinkovito samo na višeprocesorskim

platformama s dijeljenom memorijom• Skalabilnost je ograničena memorijskom arhitekturom• Nedostaje podrška za učinkovito rukovanje pogreškama• Nedostaje mehanizam za kontrolu dretvi na niskoj razini

33

Page 37: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

4.Praktični rad

4. Praktični radU ovom će dijelu biti prezentirana metodologija, testno okruženje i rezultati obavljenog praktičnog rada. Osnovna ideja je bila dobivanje i iznošenje empirijskih rezultata dobivenih testiranjima kojima bi se predstavila korisnost uporabe OpenMP-a, odnosno dobiveno ubrzanje, u programima s visokim stupnjem paralelizacije.

4.1. Metodologija

Za potrebe ovog rada su napisana četiri testna programa čija su vremena izvođenja bilježena na tri različita računala, pri čemu svako računalo posjeduje procesorsku jedinicu drukčije generacije s obzirom na mogućnost izvođenja paralelnih programa.Svaki se program u osnovi sastoji od dvije petlje – vanjske koja zadaje trenutni broj dretvi i vrši mjerenje izvođenja druge, unutarnje petlje koja je paralizirana korištenjem OpenMP-a. U osnovi se vrši mjerenje izvođenja paraleliziranog dijela programa s obzirom na podešavanje postavki OpenMP-a, u ovom slučaju broja dretvi. Kako će se i vidjeti u rezultatima, optimalni broj dretvi i time ukupno ubrzanje rada paraleliziranog dijela programa strogo ovisi o broju procesorskih jezgri korištenog računala.

4.2. Testno okruženje

Za potrebe testiranja su korištena četiri programa, pri čemu se dva manje više mogu naći u većini testova paralelnih okruženja, dok su još dva dodana kako bi testiranje dobilo na težini. Svaki od programa posjeduje određenu vremenska kompleksnost s obzirom na zadatak koji izvršavaju, niti premalu niti preveliku, kako bi se dobili što vjerodostojniji rezultati s minimaliziranim uticajem računalne entropije.Korišteni programi i pripadne veličine kojima su određene pripadne vremenske kompleksnosti:

• mm.c – Množenje matrica (veličina matrica)

• pi.c – Iterativna aproksimacija broja Pi (broj iteracija, odnosno decimala)

• it.c – Numerička integracija po trapezoidnom pravilu (veličina segmenta)

• mb.c – Mandelbrot fraktal (veličina fraktala)

Za testiranje navedenih programa su korištena tri platforme (računala), svaka s različitom mogućnošću izvođenja paralelnih programa s obzirom na generaciju pripadne procesorske jedinice. Također, valja napomenuti da su korišteni različiti operacijski sustavi i različiti programski prevodioci:

• Doma : AMD Athlon 2000+ 1.67GHz (1 CPU jezgra), 1.5GB RAM, Windows XP SP3 (32 bit), Microsoft Visual Studio 2005

34

Page 38: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

4.Praktični rad

• Laptop : AMD Turion 64 X2 Dual Core TL60 2.0 Ghz (2 CPU jezgre), 2GB RAM, Ubuntu 9.04 (64 bit), GCC 4.3.3

• Posao : Intel Core 2 Quad Q6600 2.4GHz (4 CPU jezgre), 2GB RAM, Ubuntu 8.04 (32 bit), GCC 4.2.3

Rezultati ovog praktičnog rada su namijenjeni prvenstveno usporedbi vremena izvođenja svakog pojedinog programa s obzirom na broj dretvi na korištenom računalu. Programski kod korištenih programa se može naći u dodatku na kraju ovog uratka.

4.3. Rezultati

35

Tablica 1: Tablični prikaz vremena izvođenja (u sekundama) na svim platformama

#dretviDoma Laptop Posao

it.c mb.c mm.c pi.c it.c mb.c mm.c pi.c it.c mb.c mm.c pi.c1 20,88 8,29 14,30 11,01 17,09 9,16 10,12 30,53 12,23 5,54 4,65 15,762 20,66 8,14 14,07 10,94 8,37 4,59 5,04 15,51 6,07 2,91 2,36 8,263 20,57 8,10 14,52 10,86 9,13 4,58 5,44 17,08 4,05 1,85 1,60 5,334 20,53 8,08 13,94 10,84 8,37 4,59 5,10 15,41 3,23 1,39 1,21 4,095 20,40 8,03 13,83 10,85 8,44 4,62 5,05 15,47 3,66 1,38 1,43 4,916 20,28 8,01 13,88 10,79 8,34 4,59 5,05 15,47 3,33 1,41 1,28 4,307 20,39 8,01 13,78 10,80 8,44 4,58 5,04 15,50 3,14 1,41 1,22 4,138 20,37 7,96 13,74 10,76 8,34 4,59 5,04 15,45 3,12 1,43 1,18 3,999 20,31 7,95 13,81 10,77 8,41 4,58 5,05 15,45 3,19 1,39 1,18 4,0710 20,28 7,79 13,67 10,80 8,35 4,58 5,09 15,46 3,09 1,39 1,24 3,9911 20,12 7,86 13,71 10,90 8,36 4,59 5,05 15,54 3,08 1,40 1,22 3,9912 20,21 7,96 13,66 10,83 8,39 4,63 5,04 15,48 3,15 1,38 1,23 4,0413 19,99 7,96 13,45 10,72 8,33 4,59 5,05 15,48 3,04 1,42 1,17 4,0114 20,34 8,01 13,63 10,75 8,35 4,58 5,05 15,44 3,05 1,44 1,18 4,0415 20,20 7,91 13,63 10,79 8,34 4,98 5,06 15,40 3,10 1,40 1,18 3,9416 20,19 7,82 13,60 10,76 8,33 4,58 5,07 15,44 3,04 1,39 1,17 4,0117 20,20 7,96 13,61 10,64 8,33 4,59 5,06 15,40 3,06 1,38 1,17 3,9718 20,25 7,88 13,46 10,68 8,36 4,63 5,04 15,45 3,06 1,38 1,21 4,0119 20,06 7,82 13,40 10,66 8,35 4,60 5,04 15,42 3,04 1,40 1,21 3,9620 20,21 7,82 13,49 10,65 8,34 4,58 5,03 15,40 3,09 1,40 1,22 4,0021 20,08 7,85 13,60 10,65 8,36 4,59 5,06 15,41 3,08 1,41 1,22 4,0322 20,22 7,90 13,66 10,65 8,33 4,58 5,07 15,41 3,03 1,38 1,18 3,9823 20,23 7,87 13,57 10,71 8,33 4,58 5,06 15,46 3,14 1,38 1,19 4,0424 20,19 7,77 13,55 10,63 8,34 4,59 5,04 15,42 3,09 1,38 1,19 3,9825 20,32 7,92 13,45 10,67 8,36 4,63 5,06 15,41 3,07 1,39 1,23 4,0626 20,24 7,82 13,57 10,66 8,32 4,59 5,03 15,35 3,15 1,44 1,21 3,9527 20,26 7,81 13,64 10,66 8,34 4,59 5,05 15,51 3,08 1,44 1,22 4,0328 20,12 7,81 13,84 10,69 8,33 4,59 5,06 15,45 3,12 1,39 1,19 3,9529 20,23 7,82 13,86 10,63 8,75 4,58 5,06 15,45 3,12 1,38 1,19 3,9830 20,19 7,75 13,74 10,70 8,34 4,59 5,04 15,41 3,08 1,38 1,20 4,0031 20,23 7,75 13,63 10,61 8,33 4,61 5,05 15,47 3,05 1,38 1,19 3,9832 20,16 7,81 13,79 10,75 8,35 4,61 5,03 15,41 3,09 1,41 1,19 4,02

Page 39: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

4.Praktični rad

Slika 1: Grafički prikaz rezultata za platformu Doma

Slika 2: Grafički prikaz rezultata za platformu Laptop

36

5 10 15 20 25 30Broj dretvi

5

10

15

20

Vrijeme izvođenja s Doma

pi.cmm.cmb.cit.c

Program

5 10 15 20 25 30Broj dretvi

5

10

15

20

25

30

Vrijeme izvođenja s Laptop

pi.cmm.cmb.cit.c

Program

Page 40: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

4.Praktični rad

Slika 3: Grafički prikaz rezultata za platformu Posao

4.4. Rasprava

Rezultati praktičnog rada predstavljeni su u dvije forme – tabličnoj i grafičkoj. U tablici se mogu naći vremena izvođenja svih testiranih programa na korištenim platformama, pri čemu svaki stupac predstavlja program za pojedinu platformu dok svaki redak predstavlja broj korištenih dretvi. Rezultati su zaokruženi na dvije decimale stoga što daljnja preciznost nije moguća zbog entropije računalnog sustava.Za svaku platformu je dan poseban grafički prikaz radi lakše interpretacije rezultata. Na os apcise je postavljen broj dretvi, os ordinate predstavlja vrijeme izvođenja programa, dok je svaki program predstavljen zasebnim obojenim grafom.Kao što se vidi iz priloženog, na platformama s višejezgrenim procesorima se vidi korisnost uporabe OpenMP-a. U početku, prilikom povećanja broja dretvi, dobivaju se značajnija ubrzanja programa, koje nakon nekog određenog vremena stagnira.Na platformi Doma se ne vide značajnija odstupanja vremena izvođenja programa s povećanjem broja dretvi što je i očekivano s obzirom da se radi o običnom jednoprocesorskom sustavu.Na platformi Laptop se vidi dvostruko ubrzanje za broj dretvi veći od jedan, što je također očekivano s obzirom da se radi o sustavu s dvije procesorske jezgre.

37

5 10 15 20 25 30Broj dretvi

5

10

15

Vrijeme izvođenja s Posao

pi.cmm.cmb.cit.c

Program

Page 41: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

4.Praktični rad

Na platformi Posao se vidi linearno ubrzanje za broj dretvi veći od jedan, dok se vrijeme izvođenja ustaljuje na broju dretvi većem ili jednakom broju četiri, što je opet također očekivano s obzirom da se radi o sustavu s četiri procesorske jezgre.Iz dobivenih se rezultata praktičnog rada mogu zaključiti tri bitne stvari:

1. Uporabom OpenMP-a je doista moguće dobiti N-struko ubrzanje podatkovno neovisnih dijelova koda, u ovom slučaju iteracija petlje, na platformama s N procesorskih jezgri.

2. Korištenjem broja dretvi koji je veći od broja jezgri ubrzanje naglo stagnira, odnosno, daljnjim povećanjem broja dretvi se ne mogu uočiti vidljive promjene u vremenima izvođenja.

3. Moderni višejezgreni procesori doista posjeduju posjeduju potencijal ekvivalentnih i bitno skupljih višeprocesorskih sustava te se kod paralelne obrade podataka ponašaju u potpunosti neovisno. Da tome nije tako kod iščitavanja rezultata bi se primjetili skriveni vremenski troškovi pri uporabi više dretvi na testiranim višejezgrenim platformama.

5. ZaključakVišejezgrene računalne platforme s dijeljenom memorijom postaju uobičajene na tržištu, dok OpenMP na jednostavan način nudi paralelizaciju programa svih veličina i kompleksnosti u svrhu što lakšeg iskorištavanja cjeloukupnog potencijala. Prednosti, poput jednostavnosti i preglednosti dobivenog paraleliziranog koda, privlače sve veći broj malih i velikih korisnika među koje spadaju i velika imena poput: IBM-a, HP-a, Intel-a i Sun-a, što uz konstantan razvoj dodatno jamči uspjeh i ozbiljnost ovog standarda.U praktičnom su se dijelu ovog uratka mogli vidjeti konkretni rezultati dobiveni korištenjem OpenMP-a, no valja ih uzeti s oprezom. Za potrebe testiranja su namjerno uzeti programski primjeri koji su se mogli u cjelosti paralelizirati, što se u stvarnom svijetu vrlo rijetko može susresti. Najčešće je riječ o programima u kojima se većina programskog koda mora izvršavati sekvencijalno, dok je ostatak kod koji se samo djelomično izvodi u paraleli. Ukoliko imamo sreće, taj potencijalni dio koda će biti upravo onaj mali kritični dio, obično neka vremenski zahtjevna petlja, koju ćemo moći ubrzati u onoj mjeri koliko su joj podatkovno neovisne pojedine naredbene cjelina.Kako je OpenMP postao de facto standard za sustave s dijeljenom memorijom, tako postoji i MPI (engl. krat. za Message Passing Interface) koji mu je pandan kod sustava s distribuiranom memorijom. Kombinacijom ova dva standarda se minimaliziraju pojedinačne slabosti i ističu samo dobre osobine, pa se njihovom zajedničkom uporabom mogu dobiti nadasve korisni rezultati, no ova tema neka ostane za neki drugi uradak.

38

Page 42: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

6.Literatura

6. Literatura1. Michael Sub, Claudia Leopold: “A User’s Experience with Parallel Sorting and

OpenMP“, University of Kassel2. Barbara Chapman, Gabriele Jost, Ruud Van Der Pas: “Using OpenMP –

Portable shared memory parallel programming“, The MIT Press, Cambridge, Massachusetts, 2008.

3. OpenMP Architecture Review Board, “OpenMP C and C++ Application Program Interface - Version 2.0“, http://www.openmp.org/mp-documents/cspec20.pdf, March 2002

4. OpenMP Architecture Review Board, “OpenMP Application Program Interface - Version 3.0“, http://www.openmp.org/mp-documents/spec30.pdf, May 2008

5. Rohita Chandra, Leonardo Dagum, Dave Kohr, Dror Maydan, Jeff McDonald, Ramesh Menon: “Parallel Programming in OpenMP“, Morgan Kaufmann Publishers, 2001

6. Kang Su Gatlin, Pete Isensee: “OpenMP and C++ - Reap the Benefits of Multithreading without All the Work“, http://msdn.microsoft.com/en-us/magazine/cc163717.aspx

7. Joel Yliluoma: “Guide into OpenMP: Easy multithreading programming for C++”, http://bisqwit.iki.fi/story/howto/openmp/, December 2008

8. Dr. Dobb's Journal: “Getting Started with OpenMP“, http://www.ddj.com/212501973, December 2008

9. M.D.Jones, Ph.D.: “Shared Memory Programming With OpenMP“, http://www.ccr.buffalo.edu/download/attachments/65681/Omp-I-handout-2x2.pdf?version=2, Center for Computational Research, University at Buffalo, State University of New York, 2007

10.Mark Bull: “Parallel Programming with OpenMP”, http://www.fzu.cz/activities/schools/epsschool13/presentations/bull.ppt, EPCC, University of Edinburgh

11.UBC Calculus Online: ”Numerical Integration”, http://www.ugrad.math.ubc.ca/coursedoc/math101/notes/techniques/numerical.html, Department of Mathematics, University of British Columbia

12.Ronald Blaschke: ”OpenMP Directives”, http://www.rblasch.org/studies/cs580/pa5/index.html, February, 2009

39

Page 43: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

7.Dodatak

7. Dodatak

7.1. Programski kod

7.1.1. it.c

#include <stdio.h>#include <stdlib.h>#include <math.h>#include <omp.h>#define DELTA 0.0000001#define A 1.0#define B 20.0#define MAX_THREADS 32

int main(int argc, char *argv[]){ double t0, t1, sum; long i, N = (B-A)/DELTA; int j; double results[MAX_THREADS];

for (j = 1; j <= MAX_THREADS; j++) { omp_set_num_threads(j); sum = 0; t0 = omp_get_wtime();#pragma omp parallel for default(shared) private(i) reduction(+:sum) schedule(static) for (i = 0; i < N; i++) sum += DELTA*log(A+DELTA*i); t1 = omp_get_wtime(); results[j-1] = t1-t0; //broj proteklih sekundi } for (j = 0; j < MAX_THREADS; j++) printf("(%d, %f), ", j+1, results[j]);

40

Page 44: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

7.Dodatak

return 0;}

7.1.2. mb.c

#include <stdio.h>#include <stdlib.h>#include <omp.h>#define WIDTH 1600#define HEIGHT 1200#define X_MIN -2.0#define X_MAX 2.0#define Y_MIN -2.0#define Y_MAX 2.0#define MAX_ITERATIONS 2048#define MAX_THREADS 32

typedef struct{ float real, imag;} complex;

int screen[WIDTH][HEIGHT];double results[MAX_THREADS];

int main(int argc, char *argv[]){ double t0, t1; int i, j, k, l; complex z, c; float lengthsq, temp;

for (l = 1; l <= MAX_THREADS; l++) { omp_set_num_threads(l); t0 = omp_get_wtime();#pragma omp parallel for default(shared) private(i,j,z,c,k,temp,lengthsq) schedule(dynamic)

41

Page 45: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

7.Dodatak

for (i=0; i < HEIGHT; i++) for (j=0; j < WIDTH; j++) { z.real = z.imag = 0.0; c.real = X_MIN + j * (X_MAX - X_MIN)/WIDTH; c.imag = Y_MAX - i * (Y_MAX - Y_MIN)/HEIGHT; k = 0; do // iteriranje kako bi se našla pripadna boja piksela { temp = z.real*z.real - z.imag*z.imag + c.real; z.imag = 2.0*z.real*z.imag + c.imag; z.real = temp; lengthsq = z.real*z.real+z.imag*z.imag; k++; } while (lengthsq < 4.0 && k < MAX_ITERATIONS); if (k >= MAX_ITERATIONS) screen[i][j] = 0; else screen[i][j] = k; } t1 = omp_get_wtime(); results[l-1] = t1-t0; //broj proteklih sekundi } for (i = 0; i < MAX_THREADS; i++) printf("(%d, %f), ", i+1, results[i]);

return 0;}

7.1.3. mm.c

#include <stdio.h>#include <stdlib.h>#include <omp.h>#define RANK 800#define MAX_THREADS 32#define SEED 27628143

42

Page 46: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

7.Dodatak

int A[RANK][RANK], B[RANK][RANK], C[RANK][RANK];

int main(int argc, char *argv[]){ int i, j, k, l, sum; double t0, t1; double results[MAX_THREADS];

srand(SEED); for (i = 0; i < RANK; i++) for (j = 0; j < RANK; j++) { A[i][j] = rand() & 0xff; B[i][j] = rand() & 0xff; } for (l = 1; l <= MAX_THREADS; l++) { omp_set_num_threads(l); t0 = omp_get_wtime();#pragma omp parallel for default(shared) private(i, j, k, sum) schedule(static) for (i = 0; i < RANK; i++) for (j = 0; j < RANK; j++) { for (sum = 0, k = 0; k < RANK; k++) sum += A[i][k]*B[k][j]; C[i][j] = sum; } t1 = omp_get_wtime(); results[l-1] = t1-t0; //broj proteklih sekundi } for (i = 0; i < MAX_THREADS; i++) printf("(%d, %f), ", i+1, results[i]);

return 0;}

43

Page 47: OpenMP: Višedretveno programiranje u programskom jeziku C/C++

7.Dodatak

7.1.4. pi.c

#include <stdio.h>#include <omp.h>#include <math.h>#define DEFAULT_PRECISION 1000000000#define MAX_THREADS 32

int main(int argc, char *argv[]){ int i, j; double t0, t1; double results[MAX_THREADS]; long double local, pi; const double w = 1.0 / DEFAULT_PRECISION;

for (j = 1; j <= MAX_THREADS; j++) { omp_set_num_threads(j); t0 = omp_get_wtime(); pi = 0.0;#pragma omp parallel for default(shared) private(i, local) reduction(+:pi) schedule(static) for (i = 0; i < DEFAULT_PRECISION; i++) { local = (i + 0.5) * w; pi += 4.0 / (1.0 + local * local); } pi *= w; t1 = omp_get_wtime(); results[j-1] = t1-t0; //broj proteklih sekundi } for (i = 0; i < MAX_THREADS; i++) printf("(%d, %f), ", i+1, results[i]);

return 0;}

44