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

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

Przetwarzanie Równoległe i Rozproszone

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

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

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

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

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

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

Wprowadzenie do OpenMP

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

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

Przygotowanie kilku wersji kodu zgodnie z wymogami wersji zadania,

Wprowadzenie do zrównoleglania aplikacji z wykorzystaniem standardu OpenMP

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

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

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

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

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

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

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

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

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

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

Literatura. 11/16/2016 Przetwarzanie równoległe - wstęp 1

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

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

Programowanie współbieżne Wykład 2. Iwona Kochańska

Programowanie Rozproszone i Równoległe

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

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

10/14/2013 Przetwarzanie równoległe - wstęp 1. Zakres przedmiotu

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

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

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

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

1 Podstawy c++ w pigułce.

Programowanie - wykład 4

Niezwykłe tablice Poznane typy danych pozwalają przechowywać pojedyncze liczby. Dzięki tablicom zgromadzimy wiele wartości w jednym miejscu.

Wyklad 11 Języki programowania równoległego

Model pamięci. Rafał Skinderowicz

Procesory wielordzeniowe (multiprocessor on a chip) Krzysztof Banaś, Obliczenia wysokiej wydajności.

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

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

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.

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

OpenMP część praktyczna

Liczby losowe i pętla while w języku Python

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

1. Liczby i w zapisie zmiennoprzecinkowym przedstawia się następująco

Programowanie procesorów graficznych GPGPU. Krzysztof Banaś Obliczenia równoległe 1

Zaawansowane programowanie w C++ (PCP)

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Nowoczesne technologie przetwarzania informacji

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

O superkomputerach. Marek Grabowski

procesów Współbieżność i synchronizacja procesów Wykład prowadzą: Jerzy Brzeziński Dariusz Wawrzyniak

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

- - Ocena wykonaniu zad3. Brak zad3

Pętle i tablice. Spotkanie 3. Pętle: for, while, do while. Tablice. Przykłady

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

for (inicjacja_warunkow_poczatkowych; wyrazenie_warunkowe; wyrazenie_zwiekszajace) { blok instrukcji; }

Metody Metody, parametry, zwracanie wartości

Algorytmy dla maszyny PRAM

Bloki anonimowe w PL/SQL

PROJEKT 3 PROGRAMOWANIE RÓWNOLEGŁE. K. Górzyński (89744), D. Kosiorowski (89762) Informatyka, grupa dziekańska I3

Wskazówki dotyczące zmiennych, tablic i procedur 1

Wykład 4: Klasy i Metody

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

Pytania sprawdzające wiedzę z programowania C++

Wstęp do programowania

PODSTAWY INFORMATYKI 1 PRACOWNIA NR 6

Wstęp do programowania

ANALIZA EFEKTYWNOŚCI MNOŻENIA MACIERZY W SYSTEMACH Z PAMIĘCIĄ WSPÓŁDZIELONĄ

for (inicjacja_warunkow_poczatkowych(końcowych); wyrazenie_warunkowe; wyrazenie_zwiekszajace(zmniejszające)) { blok instrukcji; }

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

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

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

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

System obliczeniowy laboratorium oraz. mnożenia macierzy

Pętle. for, while, do... while, foreach. Materiał pomocniczy do kursu Podstawy programowania Autor: Grzegorz Góralski ggoralski.

1 Podstawy c++ w pigułce.

Skalowalność obliczeń równoległych. Krzysztof Banaś Obliczenia Wysokiej Wydajności 1

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

Podstawy programowania. Wykład 6 Wskaźniki. Krzysztof Banaś Podstawy programowania 1

Pętla for. Wynik działania programu:

Lekcja : Tablice + pętle

Przykład MPI: zbiór Mandelbrota

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

Analiza efektywności przetwarzania współbieżnego. Wykład: Przetwarzanie Równoległe Politechnika Poznańska Rafał Walkowiak Grudzień 2015

> C++ wskaźniki. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki 26 kwietnia 2017

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

Programowanie w modelu równoległości danych oraz dzielonej globalnej pamięci wspólnej. Krzysztof Banaś Obliczenia równoległe 1

Wskaźniki w C. Anna Gogolińska

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

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

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

Zmienne, stałe i operatory

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk

Przykładem jest komputer z procesorem 4 rdzeniowym dostępny w laboratorium W skład projektu wchodzi:

Transkrypt:

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

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. 2

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. 3

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ść. 4

Przykład poprawnego użycia dyrektywy nowait w klauzuli for pragma omp parallel shared(a,b,n,m,y,z) { #pragma omp for nowait for (i=1; i<n; i++) b[i] = (a[i] - a[i-1]) / 2.0; #pragma omp for nowait for (i=0; i<m; i++) y[i] = sqrt(z[i]); Obydwie pętle for są niezależne od siebie Wątek może więc przystąpić do wykonywania swojej puli iteracji w drugiej pętli for bez czekania na inne wątki. Klauzula nowait w drugiej pętli for, to trywialna optymalizacja (zapewne i tak automatycznie wykona ją kompilator) w celu uniknięcia podwójnej operacji bariery. Na zakończenie pętli for i na zakończenie regionu równoległego. Uwaga: operacje bariery są bardzo kosztowne i poprawne użycie dyrektywy nowait może doprowadzić do znacznego zwiększenia wydajności. 5

Przykład użycia dyrektywy nowait w klauzuli for z subtelnym błędem. pragma omp parallel { #pragma omp for nowait for (i=0; i<n; i++) a[i]=i #pragma omp for for (i=0; i<n; i++) b[i]=2*a[i]; Można argumentować, że, wprawdzie istnieje zależność między pętlami, ale ten sam wątek który stworzył a[i] będzie tym samym który wykorzysta a[i] (Zakres iteracji obydwu pętli for jest identyczny). Ale nie pokazaliśmy kompilatorowi jak rozdzielić iteracje pomiędzy wątki (brak klauzli schedule). Nawet z klauzulą schedule można to zrobić na kilka sposobów. W związku z tym iteracje mogą być rozdzielone w dwóch pętlach w różny sposób standard OpenMP 2.5 nie gwarantuje identycznego podziału. W związku z tym druga pętla for może odczytać wartości z tablicy a, które nie zostały zapisane przez pierwszą pętlę for sytuacja wyścigu. W standardzie OpenMP 3.0 to ma się zmienić. 6

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 (operacja bariery), chyba że została podana klauzula nowait. Dyrektywa single nie określa, który wątek wykona blok instrukcji. Będzie to wątek dowolny. Podobną do 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. Z dyrektywą master należy uważać, gdyż nie ma wbudowanej synchronizacji barierowej. 7

Przykład błednego użycia konstruktu master #pragma omp parallel shared (a,b) private(i) { #pragma omp master a=10 #pragma omp for for (i=0; i<n; i++) b[i]=a; Chcemy aby zmienna a była inicjalizowana tylko raz. Ponieważ konstrukt master nie ma wbudowanej synchronizacji, to wątki wykonujące pętle for mogą rozpocząć wykorzystywanie zmiennej a w pętli zanim zostanie ona zainicjalizowana przez wątek główny. Sytuacja wyścigu. Należy wtawić #pragma omp barrier albo zmienić master na single. 8

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. 9

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. Wersje funkcji z końcówką _nest_lock umożliwiającą wielokrotne (rekurencyjne) wchodzenie do sekcji krytycznej przez ten sam wątek. Liczba wywołań funkcji set musi być równa liczbie wywołań funkcji unset. 10

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. 11

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. 12

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 inicjalizowaną wartością zmiennej sprzed regionu równoległego. przy wyjściu z regionu przez 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). 13

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). 14

Dyrektywa flush Kiedy zmiana zmiennej współdzielonej jest widoczna dla pozostałych wątków? Gdyby zmiana takiej zmiennej była widoczna natychmiast, to kompilator nie mógłby generować kodu przechowującego zmienne w rejestrach procesora. W przypadku zmiennych volatile (ulotnych) każda modyfikacja zmiennej wiąże się z zapisem do pamięci a odczyt z odczytem z pamięci. Standard OpenMP stwierdza, że wszelkie modyfikacje są zapisane do pamięci, a przez to widoczne innym wątkom w punktach synchronizacji. To samo dotyczy odczytów z pamięci. Niejawne punkty synchronizacji to Jawna i niejawna synchronizacja barrierowa Wejście i wyjście z regionu krytycznego Wejście i wyjście z funkcji operującej na zamkach Jawnie punkt synchronizacji możemy wstawić przy pomocy dyrektywy #pragma omp flush(lista_zmiennych) 15

Zapis zmiennej do pamięci a=... Zapis może nastąpić już w tym punkcie <inne obliczenia> Albo w tym #pragma omp flush(a) Ale na pewno nie po dyrektywie flush 16

Dyrektywa flush - przykład int data,flag #pragma omp parallel sections shared(data,flag) num_threads(2) { #pragma omp section { read(&data); #pragma omp flush(data) flag = 1; #pragma omp flush(flag) // Do more work. #pragma omp section { while (!flag) { #pragma omp flush(flag) #pragma omp flush(data) process(&data); 17

Wydajność - minimalizuj sekcje krytyczne #pragma omp parallel shared(a,b) private(c,d) { // tu jakiś kod #pragma omp critical { a+=2*c; b=d*d; // tu dalszy kod Operacja zapisu do zmiennej współdzielonej zmiennej a musi być wewnątrz sekcji krytycznej (niebezpieczeństwo wyścigu). Jednakże przypisanie wartości zmiennej prywatnej d możemy bezpiecznie przenieść poza sekcję krytyczną. Wtedy możemy zastąpić sekcję krytyczną dyrektywą atomic. #pragma omp parallel shared(a,b) private(c,d) { // tu jakiś kod #pragma omp critical a+=2*c; c=d*d; // tu dalszy kod 18

Wydajność - maksymalizuj regiony równoległe #pragma omp parallel shared(a,b) private(c,d) for(i=0;i<n;i++) for(j=0;j<n;j++) #pragma omp parallel for for(k=0;k<n;k++) {... Koszt wejścia do i wyjścia z regionu równoległego ponoszony n 2 razy. #pragma omp parallel private(i,j) for(i=0;i<n;i++) for(j=0;j<n;j++) #pragma omp for for(k=0;k<n;k++) {... Koszt wejścia do i wyjścia z regionu równoległego ponoszony 1 raz. Zresztą należy rozważyć czy nie możemy zrównoleglić którejś z zewnętrznych pętli for. 19

Wydajność unikaj współdzielenia (w tym fałszywego) połączonego z zapisem. Przypomnienie z drugiego wykładu: gdy wiele wątków wykonuje zapisy do współdzielonego wiersza pamięci cache następuje znaczne spowolnienie systemu (konieczność uruchomienia protokołu spójności). Niech nt jest liczbą wątków (przykład celowo źle skonstruowany) #pragma omp parallel for shared(nt,a) schedule(static,1) for(int i=0;i<nt;i++) a[i]+=i; Statyczny rozdział puli iteracji algorytmem round-robin po jednej między wątki: wątek zerowy aktualizuje a[0], pierwszy a[1], drugi a[2], etc... Z punktu widzenia języka programowania wątki nie współdzielą elementów a. Ale a[0],a[1],a[2] mają duże szanse na rezydowanie w jednym wierszu pamięci. Z punktu widzenia hardware'u może być to współdzielenie. 20

Wydajność - uwaga na funkcje biblioteczne!!! Pewien student (obecny na wszystkich wykładach z obliczeń równoległych!!!) zaobserował spowolnienie zamiast przyspieszenia w następującym kodzie: #pragma omp parallel {... x=rand();... ; Co robi funkcja rand. Zwraca nową liczbę losową na podstawie zmiennej globalnej będącej stanem generatora liczb losowych i aktualizuje stan przechowywany w tej zmiennej. Mamy doczynienie ze współdzieleniem spowalniającym program a ponadto z wyścigiem (dlaczego z wyścigiem). Wyjście: zastosować funkcję która pozwala na jawne przekazanie wskaźnika na stan generatora; jedna instancja stanu generatora na jeden wątek. Ponadto każdy wątek powinien używać innego ziarna (dlaczego???). Co się dzieje gdy wołamy malloc/free/new/delete z wielu wątków? Na szczęście funkcje te wykonywane są w sekcjach krytycznych (są bezpieczne ze względu na wielowątkowść ang. thread safe). Na nieszczęście sekcje krytyczne serializują kod. Jeżeli wątki dużo czasu spędzają wewnętrz malloc/free to... 21

Wydajność - dbaj o zrównoważenie obciążenia void CalcImage(int Width,int Height) { #pragma omp parallel for schedule(runtime) for(int i=0;i<height;i++) CalcImgRow(Image[i],i,Width,Height); Omawiany już, przy okazji biblioteki MPI, przykład ze zbiorem Mandlebrota. Czas wykonania funkcji CalcImgRow silnie zależy od numeru wiersza i. Klauzula schedule(runtime) nakazuje podział iteracji pętli for w zależności od wartości zmiennej środowiskowej OMP_SCHEDULE. Zmienna ta przyjęła 3 wartości: static dynamic,1 guided,1 Obliczenia na klastrze Mordor2: dwa procesory czterordzeniowe; razem 8. 22

Przyspieszenie static dynamic,1 guided,1 1 2 3 4 5 6 7 8 liczba procesorów (rdzeni) Wersje (dynamic,guided) z dynamicznym równownoważeniem obciążenia spisują się o wiele lepiej. Uwaga: guided, dynamic nie zawsze lepsze niż static (dlaczego?) Widoczny wpływ prawa Amdahla - część sekwencyjna m.in zapis wynikowego obrazu do pliku 23

Poprawność - eliminuj wyścigi (ang. race) double a[n],b[n];... #pragma omp parallel for for(int i=0;i<n-1;i++) a[i]=a[i]+b[i] Ta pętla jest poprawnie zrównoleglona. Kolejna pętla zawiera zależność pomiędzy iteracjami (ang. loop carried dependence) #pragma omp parallel for for(int i=0;i<n-1;i++) a[i]=a[i+1]+b[i] Ta pętla jest zrównoleglona błędnie. Otóż różne wątki usiłują wykonać współbieżnie różne iteracje pętli jeżeli iteracja i+1 zostanie wykonana przed iteracją i, a[i+1] zostanie wyliczone wcześniej. A zatem do obliczenia a[i] użyjemy nowej" (a nie starej" jak w wersji sekwencyjnej) wartości a[i+1]. W przypadku wyścigu błędny wynik może, ale nie musi wystąpić; jest to niedeterministyczne. Fakt wystąpienia błędu może być uwarunkowany: (a) liczbą wątków (b) obciążeniem systemu (c) danymi wejściowymi 24

Eliminacja zależności między pętlami double c[n]; #pragma omp parallel for for(int i=0;i<n;i++) c[i]=a[i] #pragma omp parallel for for(int i=0;i<n-1;i++) a[i]=c[i+1]+b[i] Kod po przetransformowaniu do powyższej wersji został poprawnie zrównoleglony. 25

Inne pułapki Uwaga na domyślne typy zmiennych (zmienna staje się współdzielona, a myślimy że jest prywatna) - użycie default(none). Zmienne prywatne są niezainicjalizowane, a ich wynik po wyjściu z pętli jest nieokreślony - użycie lastprivate/firstprivate. Funkcje biblioteczne muszą być bezpieczne ze względu na wielowątkowość (ang. thread safe) - przykład z funkcją rand. Większość dyrektyw np. #pragma omp for, #pragma omp barrier musi być wykonana przez wszystkie wątki. Poniższy kod jest błędny: #pragma omp parallel if (omp_get_thread_num()>0) { #pragma omp parallel for for(int i=0;i<n;i++)... 26

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ąć. 27