Programowanie maszyn z pamięcią wspólną w standardzie OpenMP.

Podobne dokumenty
Programowanie maszyn z pamięcią wspólną w standardzie OpenMP.

Programowanie maszyn z pamięcią wspólną w standardzie OpenMP ciąg dalszy.

Przetwarzanie Równoległe i Rozproszone

Programowanie systemów z pamięcią wspólną specyfikacja OpenMP. Krzysztof Banaś Obliczenia równoległe 1

Wprowadzenie do OpenMP

OpenMP. Programowanie aplikacji równoległych i rozproszonych. Wykład 2. Model programowania. Standard OpenMP. Dr inż. Tomasz Olas

Elementy składowe: Przenośność oprogramowania Model SPMD Szczegółowe wersje (bindings) dla różnych języków programowania

Open MP wer Rafał Walkowiak Instytut Informatyki Politechniki Poznańskiej Wiosna

Wprowadzenie do zrównoleglania aplikacji z wykorzystaniem standardu OpenMP

Open MP wer Rafał Walkowiak Instytut Informatyki Politechniki Poznańskiej Jesień 2014

Programowanie współbieżne Wstęp do OpenMP. Rafał Skinderowicz

Wsparcie dla OpenMP w kompilatorze GNU GCC Krzysztof Lamorski Katedra Informatyki, PWSZ Chełm

Komputerowe Obliczenia Równoległe: Wstęp do OpenMP i MPI

Równoległość i współbieżność

Równoległość i współbieżność

Open MP. Rafał Walkowiak Instytut Informatyki Politechniki Poznańskie Jesień 2011

Programowanie Rozproszone i Równoległe

Wyklad 11 Języki programowania równoległego

KURS C/C++ WYKŁAD 2. char znak; znak = a ; Program 2 #include<stdio.h> void main() { char znak; while( (znak = getchar() )!= t ) putchar(znak); }

1 Podstawy c++ w pigułce.

PRZETWARZANIE RÓWNOLEGŁE I ROZPROSZONE. Mnożenie macierzy kwadratowych metodą klasyczną oraz blokową z wykorzystaniem OpenMP.

Komputerowe Obliczenia Równoległe: Wstęp do OpenMP i MPI

OpenMP część praktyczna

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

Programowanie współbieżne OpenMP wybrane wydajność. Rafał Skinderowicz

Procesy i wątki. Krzysztof Banaś Obliczenia równoległe 1

Wskaźniki w C. Anna Gogolińska

Podstawy programowania skrót z wykładów:

Przygotowanie kilku wersji kodu zgodnie z wymogami wersji zadania,

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

1 Podstawy c++ w pigułce.

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Metody Metody, parametry, zwracanie wartości

Zmienne, stałe i operatory

Wykład 4: Klasy i Metody

Tworzenie programów równoległych cd. Krzysztof Banaś Obliczenia równoległe 1

Podstawy programowania. Wykład: 5. Instrukcje sterujące c.d. Stałe, Typy zmiennych c.d. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

PODSTAWY INFORMATYKI 1 PRACOWNIA NR 6

Programowanie współbieżne Wykład 7. Iwona Kochaoska

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk. Wydział Inżynierii Metali i Informatyki Przemysłowej

Modele programowania równoległego. Pamięć współdzielona Rafał Walkowiak dla III roku Informatyki PP

Programowanie - wykład 4

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

Języki C i C++ Wykład: 2. Wstęp Instrukcje sterujące. dr Artur Bartoszewski - Języki C i C++, sem. 1I- WYKŁAD

Wstęp do programowania

Programowanie komputerowe. Zajęcia 3

Programowanie Współbieżne

ALGORYTMY. 1. Podstawowe definicje Schemat blokowy

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Wstęp do Programowania, laboratorium 02

Programowanie równoległe i rozproszone. Praca zbiorowa pod redakcją Andrzeja Karbowskiego i Ewy Niewiadomskiej-Szynkiewicz

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Literatura. 3/26/2018 Przetwarzanie równoległe - wstęp 1

ZASADY PROGRAMOWANIA KOMPUTERÓW

Podstawy Programowania C++

Część 4 życie programu

Podstawy Programowania Podstawowa składnia języka C++

Lab 9 Podstawy Programowania

Programowanie współbieżne Wykład 10 Synchronizacja dostępu do współdzielonych zasobów. Iwona Kochańska

Widoczność zmiennych Czy wartości każdej zmiennej można zmieniać w dowolnym miejscu kodu? Czy można zadeklarować dwie zmienne o takich samych nazwach?

Język ludzki kod maszynowy

Wstęp do programowania

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Wskaźniki. Informatyka

Podstawy algorytmiki i programowania - wykład 4 C-struktury

Podstawy programowania komputerów

Materiał Typy zmiennych Instrukcje warunkowe Pętle Tablice statyczne Wskaźniki Tablice dynamiczne Referencje Funkcje

O superkomputerach. Marek Grabowski

Algorytmy dla maszyny PRAM

Wykład 8: klasy cz. 4

Język JAVA podstawy. Wykład 3, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

OpenMP środowisko programowania komputerów równoległych ze wspólną pamięcią

Programowanie strukturalne i obiektowe. Funkcje

Przetwarzanie wielowątkowe przetwarzanie współbieżne. Krzysztof Banaś Obliczenia równoległe 1

Tworzenie programów równoległych. Krzysztof Banaś Obliczenia równoległe 1

I - Microsoft Visual Studio C++

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 13 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 30

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

ALGORYTMY. 1. Podstawowe definicje Schemat blokowy

Rok akademicki: 2013/2014 Kod: JFT s Punkty ECTS: 5. Poziom studiów: Studia I stopnia Forma i tryb studiów: Stacjonarne

Równoległy algorytm wyznaczania bloków dla cyklicznego problemu przepływowego z przezbrojeniami

4. Funkcje. Przykłady

1. Pierwszy program. Kompilator ignoruje komentarze; zadaniem komentarza jest bowiem wyjaśnienie programu człowiekowi.

Stworzenie klasy nie jest równoznaczne z wykorzystaniem wielowątkowości. Uzyskuje się ją dopiero poprzez inicjalizację wątku.

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

4. Procesy pojęcia podstawowe

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 28 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 27

Elementy języka C. ACprogramislikeafastdanceonanewlywaxeddancefloorbypeople carrying razors.

4. Procesy pojęcia podstawowe

Instrukcje warunkowe i skoku. Spotkanie 2. Wyrażenia i operatory logiczne. Instrukcje warunkowe: if else, switch.

Wykład 3 Składnia języka C# (cz. 2)

Porównanie wydajności CUDA i OpenCL na przykładzie równoległego algorytmu wyznaczania wartości funkcji celu dla problemu gniazdowego

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

JAVA. Platforma JSE: Środowiska programistyczne dla języka Java. Wstęp do programowania w języku obiektowym. Opracował: Andrzej Nowak

Podstawy programowania. Wykład Pętle. Tablice. Krzysztof Banaś Podstawy programowania 1

Tablice, funkcje - wprowadzenie

Podstawy programowania. Wykład: 4. Instrukcje sterujące, operatory. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Jak napisać program obliczający pola powierzchni różnych figur płaskich?

Transkrypt:

Programowanie maszyn z pamięcią wspólną w standardzie OpenMP. 1

OpenMP Standard rozwinięty i zdefiniowany w latach 90 przez grupę specjalistów z przemysłu. Strona www: www.openmp.org Składa się ze zbioru dyrektyw dla kompilatora oraz z niewielkiego zbioru funkcji bibliotecznych. (+ kilka zmiennych środowiskowych). Dyrektywy i funkcje biblioteczne zdefiniowane są dla języków C/C++ oraz Fortran (bardzo ważny język w obliczeniach naukowo inżynierskich). Wymaga wsparcia kompilatora akceptującego standard. Z kompilatorów obsługujących OpenMP wspominamy. gcc w wersji 4.2 (standardowy kompilator open source na Linuksie, ale niestety większość dystrybucji oparta jest na starszej wersji niż 4.2). Do pobrania i skompilowania ze strony gcc.gnu.org. icc (Intel C Compiller) firmy Intel. Dla osób nie używających go do pracy zarobkowej dostępny za darmo (w wersji Linuksowej). Generuje bardzo dobry kod na procesorach Intela, znacznie szybszy od kodu generowanego na przez gcc. Sun Studio 12. Wersja Linuksowa dostępna za darmo. Generuje bardzo dobry kod dla procesorów 64-bitowych firmy AMD (Athlon 64, Opteron). 2

Model programowania OpenMP zmienne prywatne wątek wątek zmienne prywatne zmienne prywatne wątek Pamięć wspólna wątek zmienne prywatne Aplikacja składa się z wielu wątków mających dostęp do pamięci współdzielonej. Dane mogą być współdzielone (ang. shared) lub prywatne (ang. private). Dostęp do danych prywatnych ma dostęp jedynie ten wątek, który je posiada. Dostęp do danych współdzielonych mają wszystkie wątki. 3

Postać dyrektyw OpenMP Dla języków C/C++ dyrektywy OpenMP mają postać dyrektyw #pragma kompilatora. Dyrektywa #pragma preprocesora służy do przekazywania poleceń zależnych od implementacji. Dyrektywa #pragma nierozpoznawana przez kompilator powinna być ignorowana (być może wraz z ostrzeżeniem). Dyrektywy OpenMP mają zawsze postać: #pragma omp nazwa_dyrektywy parametry gdzie omp jest słowem kluczowym OpenMP. Niektóre dyrektywy wymagają, aby występował po nich blok instrukcji (lub pojedyncza instrukcja). Blok instrukcji w języku C/C++ ma postać instr1; instr2; instr3; Dyrektywa wraz z blokiem nazywana jest konstruktem. 4

Model wykonania OpenMP instrukcje szeregowe pojedynczy wątek (master thread) #pragma omp parallel blok instrukcji team wątków instrukcje szeregowe pojedynczy wątek (master thread) #pragma omp parallel blok instrukcji team wątków instrukcje szeregowe pojedynczy wątek (master thread) 5

Dyrektywa parallel Program OpenMP wykonuje się sekwencyjnie w jednym wątku, do momentu napotkania dyrektywy parallel. Ma ona postać: #pragma omp parallel [dodatkowe klauzule] /* blok instrukcji */ Dyrektywa parallel tworzy team wątków. Każdy wątek z teamu wykonuje blok instrukcji. Wątek który napotkał dyrektywę parallel staje się wątkiem głównym teamu i otrzymuje identyfikator 0. Jedna z możliwych dodatkowych klauzul ma postać if(wyrażenie). Jej użycie oznacza zrównoleglenie warunkowe: team wątków jest tworzony, wtedy i tylko wtedy gdy wyrażenie ma wartość różną od zera. W przeciwnym wypadku blok instrukcji wykonywany jest przez jeden wątek (wątek główny). 6

OpenMP - pierwszy program // Plik nagłówkowy funkcji bibliotecznych #include <omp.h> #include <stdio.h> int main() // Włącz dynamiczny wybór liczby wątków omp_set_dynamic(1); // Ustaw liczbę wątków na 15 omp_set_num_threads(15); // Wypisze się jeden raz printf("hello World! Kod szeregowy\n"); // Poniższy blok wykona się w 15 wątkach #pragma omp parallel printf("hello, jestem wątkiem nr %d\n",omp_get_thread_num()); Komunikat wypisze się 15 razy, kolejność wątków nie jest zdefiniowana. 7

Jak określić liczbę wątków w teamie Można to zrobić na trzy sposoby. Po pierwsze można nadać wartość zmiennej środowiskowej OMP_NUM_THREADS. Z linii poleceń shella wpisujemy: setenv OMP_NUM_THREADS 15 [dla shelli csh, tcsh] export OMP_NUM_THREADS=15 [dla shelli sh, bash,ksh] Po drugie można skorzystać z funkcji bibliotecznej OpenMP: void omp_set_num_threads(int numthreads). Funkcję tą można wywoływać wyłącznie na zewnątrz bloku #pragma omp parallel. Aby funkcja zadziałała wcześniej trzeba włączyć dynamiczne okręślanie liczby wątków wywołując funkcję: omp_set_dynamic(1) Po trzecie można skorzystać z dodatkowej klauzuli dyrektywy #pragma omp parallel: #pragma omp parallel num_threads(15) 8

Zachowanie się zmiennych w bloku parallel Zmienne mogą być albo współdzielone (ang. shared) albo prywatne (ang. private). Klauzula shared(lista_zmiennych) dyrektywy parallel deklaruje zmienne wymienione na liście jako współdzielone. Klauzula private(lista_zmiennych) dyrektywy parallel deklaruje zmienne wymienione na liście jako prywatne. Istnieje jedna kopia zmiennej współdzielonej do której dostęp mają wszystkie wątki. W przypadku zmiennej prywatnej każdy wątek ma swoją własną kopię zmiennej niedostępną innym wątkom. Zmiany wartości zmiennej prywatnej nie mają wpływu na wartości widziane przez inne wątki. Zmienne prywatne po wejściu do dyrektywy parallel mają wartości nieokreślone. Jeżeli chcemy je zainicjalizować wartością oryginalnej zmiennej przed wejściem w dyrektywę parallel, to należy użyć klauzuli firstprivate(lista_zmiennych). Domyślny typ zmiennych jest określony klauzulą default(typ), gdzie typ może przyjąć jedną z wartości: shared, private, none. Wartość none oznacza, że musimy podać typ każdej ze zmiennych występującej w bloku parallel. C/C++ nie wspiera default(private). 9

Typy zmiennych - przykład double x=1.0; int i=2; float z=3.0f; #pragma omp parallel default(none) shared(x) private(i) firstprivate(z) // default none oznacza, że w bloku możemy się odwołać wyłącznie // do zmiennych wymienionych w trzech klauzulach tzn. x,i,z. // każdy wątek ma prywatną kopię zmiennej i, a jej wartość jest // nieokreślona. // każdy wątek ma prywatną kopię zmiennej z, o wartości // początkowej 3. // wszystkie wątki operują na jednej kopii zmiennej x. Gdyby wewnątrz bloku parallel miało miejsce wywołanie funkcji to wszystkie zmienne tej lokalne funkcji będą zmiennymi prywatnymi a zmienne statyczne współdzielonymi.. 10

Jakich typów zmiennych użyć Jeżeli wątek inicjalizuje i używa zmiennej (np. indeks pętli) których inne wątki nie używają, powinna ona być zadeklarowana jako prywatna. Jeżeli wątek często odczytuje zmienną, która została zainicjalizowana wcześniej w programie, korzystne jest utworzenie lokalnej kopii tej zmiennej i odziedziczenie wartości istniejącej w chwili utworzenia kopii. Należy użyć klauzuli firstprivate. W ten sposób każdy procesor będzie posiadał lokalną kopię zmiennej co prowadzi do lepszego wykorzystania pamięci cache. Jeżeli wiele wątków manipuluje pojedynczą zmienną, należy zbadać możliwość rozbicia tych manipulacji na lokalne operacje, po których nastąpi pojedyncza operacja globalna. Na przykład jeżeli wiele wątków zlicza jakieś zdarzenia, warto rozważyć utrzymywania lokalnych (dla każdego wątku) liczników i ich sumowanie po opuszczeniu regionu równoległego. Umożliwia to klauzula reduction. Dopiero na samym końcu należy deklarować zmienne jako współdzielone. Warto używać klauzli default(none) aby nie być zaskoczony domyślnym typem zmiennych. 11

Dyrektywa for Dyrektywa for występuje wewnątrz konstruktu parallel (obie dyrektywy można połączyć w jedną). Musi poprzedzać bezpośrednio pętlę for. Ma ona postać: #pragma omp for [dodatkowe klauzule] instrukcja for instrukcje wykonujące się w pętli Bez dyrektywy for każdy wątek wykonałby oddzielnie całą pętlę. Z dyrektywą for nastąpi wspólne (ang. work sharing) wykonanie całej pętli. Każdy wątek otrzyma część zakresu iteracji pętli. Cała pętla zostanie wykonana równolegle przez wątki teamu. 12

Dyrektywa for team wątków zakres iteracji pętli for Możliwy przykład (przy statycznej alokacji iteracji pętli) #pragma omp for for(i=0;i<100;i++) Jeżeli mamy 10 wątków to możliwe jest, że wątek 0 wykona iteracje od 0 do 10, wątek 1 od 10 do 19, wątek 2 do 20 do 29,..., wątek 9 od 90 do 99. 13

Warunki które musi spełniać pętla for w dyrektywie for W pętli for nie może wystąpić instrukcja break Zmienna sterująca pętli musi być typu całkowitego Wyrażenie inicjujące tą zmienną musi być typu całkowitego. Wyrażenie logiczne na kontynuacje pętli musi być jednym z <,>,,. Wyrażenie inkrementujące musi powodować inkrementacje zmiennej sterującej o stałe składniki. Kompilator powie nam (komunikat o błędzie), gdy nie spełniamy warunków. 14

Dyrektywa for - przykład #pragma omp parallel if (n>limit) default(none)\ shared(n,x,y) private(i) #pragma omp for for(i=0;i<n;i++) x[i]+=y[i]; Podział na wątki wykonywany jest tylko wtedy, gdy liczba iteracji n jest większa od wartości limit. W przeciwnym wypadku pętla for wykonywana jest przez jeden wątek. Iteracje pętli for dzielone są pomiędzy wątki w teamie. Każdy wątek wykonuje część iteracji. Kompilator nie rozpoznający OpenMP też skompiluje ten program (w wersji szeregowej) Dyrektywę parallel można połączyć z for #pragma omp parallel for if (n>limit) default(none) \ shared(n,x,y) private(i) for(i=0;i<n;i++) x[i]+=y[i]; 15

Alokacja iteracji pętli for wątkom (1) Sposób alokacji iteracji pętli for dla wątków możemy zmieniać przy pomocy klauzuli schedule dyrektywy pragma omp for. Ma ona następującą składnię schedule(static dynamic guided,size) schedule(runtime) schedule (static, size). Alokacja statyczna. Iteracje dystrybuowane są algorytmem round-robin wątkom w porcjach o rozmiarze size. Jeżeli size jest pominięte, wątek otrzymuje liczbę iteracji równą całkowitej liczbie iteracji pętli podzielonej przez liczbę wątków. Przykład. Pętla for z 16 iteracjami (0-15), 4 wątki id wątku brak size size=2 0 1 2 3 0-3 4-7 8-11 12-15 0-1 2-3 4-5 6-7 8-9 10-11 12-13 14-15 16

Alokacja iteracji pętli for wątkom (2) schedule (dynamic, size). Alokacja dynamiczna. Wykonywana jest w trakcie pracy pętli. Każdy wątek pobiera size iteracji z kolejki. Jeżeli wątek skończy swoje pobrane iteracje, proces powtarza się wątek pobiera znowu size iteracji z kolejki. Umożliwia dobre zrównoważenie obciążenia w sytuacji, gdy czas iteracji nie jest stały (przykład: generowanie zbioru Mandelbrota). schedule (guided, size). Podobnie jak dynamic, ale rozmiar porcji iteracji jest zmniejszany wykładniczo. Umożliwia lepsze zbalansowanie obciążenia niż dynamic. schedule (runtime). Określana w czasie wykonania programu na podstawie wartości zmiennej środowiskowej OMP_SCHEDULE. 17

Sieroca dyrektywa for void work() #pragma omp for for(i=0;i<n;i++) : work() // sekwencyjna pętla for w jednym wątku #pragma omp parallel work() // równoległa pętla for w teamie wątków Standard OpenMP pozwala na umieszczenie dyrektyw (np. omp for) poza kontekstem leksykalnym dyrektywy parallel. W przykładzie dyrektywa for jest umieszczona wewnątrz odrębnej funkcji. Jeżeli funkcja zostanie wywołana spoza dynamicznego kontekstu dyrektywy parallel dyrektywa for jest ignorowana. Jeżeli funkcja zostanie wywołana wewnątrz dynamicznego kontekstu dyrektywy parallel dyrektywa for jest wykonana (podział iteracji pętli for pomiędzy wątki teamu) 18

#pragma omp parallel #pragma omp sections #pragma omp section taska(); #pragma omp section taskb(); #pragma omp section taskc(); Dyrektywa sections Jeżeli w teamie są trzy wątki, to każda z funkcji taska(), taskb(), taskc() wykona się w odrębnym wątku. Bo zakończeniu dyrektywy sections nastąpi barierowa synchronizacja wątków, chyba że użyliśmy klauzuli nowait w dyrektywie sections. 19

Synchronizacja w OpenMP Synchronizacja barrierowa (dyrektywa barrier). Sekcje krytyczne (dyrektywy critical i atomic) Wykonanie bloku kodu przez jeden wątek (dyrektywy single i master) Opróżnienie rejestrów do pamięci (dyrektywa flush) Funkcje operujące na zamkach. 20

Sekcje krytyczne for(i=0;i<n;i++... sum+=a[i];... Jeżeli sum jest zmienną współdzieloną tylko jeden wątek może mieć do niej dostęp w danej chwili. (próba operacji na sum przez kilka wątków jednocześnie prowadzi do wyścigu). Należy zrównoleglić pętlę: #pragma omp parallel for for(i=0;i<n;i++... #pragma omp critical sum+=a[i];... Instrukcja lub blok po dyrektywie critical wykonują się ze wzajemnym wyłączaniem. Tylko jeden wątek przebywa w sekcji krytycznej w danej chwili. 21

Sekcja krytyczna - wizualizacja region krytyczny czas Sekcja krytyczna jest użyteczna dla uniknięcia wyściegu. Nie można określić kolejności wejścia wątków do sekcji, jest ona niedeterministyczna. Kod zawarty w sekcji krytycznej jest kodem szeregowym i w żaden sposób nie jest przyspieszany przez obliczenia równoległe. Nadużywanie sekcji krytycznych obniża skalowalność!!! Należy starać się zminimalizować rozmiar (czas spędzany w ) sekcji krytycznych. 22

Dyrektywa atomic Często sekcja krytyczna polega na uaktualnieniu jednej lokacji w pamięci. OpenMP dostarcza dyrektywę atomic dla tego typu sekcji krytycznych. Po dyrektywie atomic może wystąpić pojedyncza instrukcja uaktualniająca zmienną postaci: x op=expr, gdzie expr jest wyrażeniem, a op jest jednym z binarnych operatorów (dopuszczalne są również wyrażenia typu x++ albo --x). Dyrektywa atomic serializuje odczyt i zapis zmiennej x w jednej sekcji krytycznej. Obliczanie wyrażenia expr nie jest serializowane!!! Każda dyrektywa atomic może być zastąpiona dyrektywą critical. Jednakże większość maszyn posiada wsparcie sprzętowe (specjalne instrukcje), sprawiające że dyrektywa atomic może być o wiele wydajniej zaimplementowana niż critical. 23

Dyrektywa barrier Jest najprostszą dyrektywą synchronizacyjną. Ma postać: #pragma omp barrier Po napotkaniu tej dyrektywy wątek zostaje wstrzymany do momentu, gdy wszystkie inne wątki napotkają tę dyrektywę. Dyrektywa barrier jest wykonywana automatycznie po zakończeniu dyrektywy for, chyba że w dyrektywie for umieszczono klauzulę nowait. Oznacza to że po zakończeniu zrównoleglonej pętli for wątek czeka aż pozostałe wątki zakończą pętlę for. 24

Dyrektywa barrier - wizualizacja region bariery czas Kolorem białym oznaczono oczekiwanie wątku. Wątek zostaje wypuszczony z punktu bariery dopiero wtedy, gdy wszystkie inne wątki osiągnęły ten punkt. Należy pamiętać, że implementacja dyrektywy barrier ma złożoność czasową O(log(p)), gdzie p jest liczbą procesorów. Nadużycie dyrektywy barrier zmniejsza skalowalność. 25

Dyrektywa ta ma postać: #pragma omp single [klauzule] blok_instrukcji Dyrektywy single i master Po napotkaniu dyrektywy single tylko jeden wątek wykona blok instrukcji. Pozostałe wątki przeskakują na koniec bloku. Pozostałe wątki czekają na wątek wykonujący blok instrukcji, chyba że została podana klauzula nowait. Dyrektywa single nie określa, który wątek wykona blok instrukcji. Specjalną formą dyrektywy single jest dyrektywa: #pragma omp master [klauzule] blok_instrukcji Sprawia ona że blok instrukcji zostanie wykonany przez wątek nadrzędny (master) - o numerze 0. 26

Dyrektywa single - wizualizacja blok single czas blok single Kolorem białym oznaczono oczekiwanie wątku. Przykłady zastosowania: Obliczanie danych globalnych Wejście-wyjście z bloku równoległego. 27

Funkcje operujące na zamkach void omp_init_lock(omp_lock_t *lock); void omp_destroy_lock(omp_lock_t *lock); void omp_set_lock(omp_lock_t *lock); void omp_unset_lock(omp_lock_t *lock); int omp_test_lock(omp_lock_t *lock); Zamek realizuje wzajemne wykluczanie wątków. Przed użyciem należy go zainicjalizować poprzez omp_init_lock, a gdy staje się zbędny usunąć funkcją omp_destroy_lock. Funkcja omp_set_lock wchodzi, a omp_unset_lock wychodzi z sekcji krytycznej. Tylko jeden wątek może przebywać w sekcji krytycznej; pozostałe czekają w funkcji omp_set_lock na wejście. Funkcja omp_test_lock usiłuje wejść do sekcji krytycznej, i jeżeli w danej chwili nie jest to możliwe natychmiast powraca z wynikiem zero. Gdy udało się wejść do sekcji krytycznej natychmiast powraca z wynikiem różnym od zera. 28

Przykład: całkowanie metodą prostokątów f(x) h h= b a N x i =a h 2 i h a x 0 x 1 x 2 b b a N 1 f x h i=0 f x i Całka oznaczona przybliżana przy pomocy sumy ` pól powierzchni N (w przykładzie N=3) prostokątów. Obliczenia można obsłużyć jedną pętlą for. 29

Całkowanie: wersja niewydajna double Sum(int N,double a,double b) int i; double sum=0.0; double h=(b-a)/(double)n; #pragma omp parallel for default(none) firstprivate(n,h,a) shared(sum) for(i=0;i<n;i++) double func=f(a+i*h+h/2); #pragma omp critical sum+=func; return sum*h Ciekawostka: program skompiluje się do wersji jednowątkowej na kompilatorze nie obsługującym OpenMP sum jest zmienną współdzieloną w której gromadzona jest suma pól prostokątów. skoro dostęp do niej ma wiele wątków, które jednocześnie ją zwiększają, to musimy ją chronić sekcją krytyczną. Zastosowanie sekcji krytycznej obniża znacznie wydajność, ponieważ wątek będący w sekcji krytycznej i aktualizujący tę zmienną wstrzymuje pozostałe wątki chcące zrobić to samo. 30

Całkowanie: jak poprawić wydajność Idealnie byłoby gdyby każdy wątek posiadał prywatną kopię zmiennej sum, w której byłaby gromadzona suma pól prostokątów rozpatrywanych w iteracjach przydzielonych temu wątkowi. Te lokalne sumy mogłyby być zsumowane po wyjściu z pętli for. Takie rozwiązanie nie potrzebowałoby synchronizacji w każdej iteracji. Tak więc sum musi być zmienną prywatną. Pytanie: w jaki sposób zsumować prywatne zmienne? Odpowiedź: za pomocą kluzuli reduction(+:sum). Określa ona że: każdy wątek w teamie ma prywatną kopię zmiennej sum. po wyjściu z konstruktu wątek obliczana jest suma (znak +, możliwe są też inne operatory) prywatnych kopii i zapamiętywana w zmiennej sum wątku głównego (master). 31

Całkowanie: wersja z klauzulą reduction double Sum(int N,double a,double b) int Trials=N/size,i; double sum=0.0; double h=(b-a)/(double)n; #pragma omp parallel for default(none)firstprivate(n,h,a)reduction(+:sum) for(i=0;i<n;i++) sum+=f(a+i*h+h/2); return sum*h; Ciekawostka: program skompiluje się do wersji jednowątkowej na kompilatorze nie obsługującym OpenMP Brak sekcji krytycznej sprawia, że wątki w pętli wykonują się z maksymalną szybkością, a synchronizacja nastąpi dopiero gdy zakończą swoje iteracje (podczas dodawania prywatnych kopii zmiennej sum). 32

Podsumowanie OpenMP jest standardem programowania systemów z pamięcią wspólną. Określa standard programowania wielowątkowego i jest API wyższego poziomu niż POSIX threads. Opiera się na dyrektywach #pragma i wymaga wsparcia kompilatora. Jest rozpowszechniony w przemyśle software'owym (standard przemysłowy) i dlatego warto go poznać. Wraz z rozpowszechnianiem się procesorów wielordzeniowych jego znaczenie będzie rosnąć. 33