Download - Algorytmy Równoległe (d. Algorytmy Rozproszone) · na poprawę wydajności już wykorzystaliśmy (potokowanie, jednoczesne wykonywanie operacji arytmetycznych i I/O, cache, szybsze

Transcript

Algorytmy Rozproszone

Jacek Dziedzic

FTiMS, PG

2004-2017

v2.14 2017.11.29.

Informacje organizacyjne• Prowadzący: Jacek Dziedzic,

s. 106 GG, [email protected].

• Konsultacje: środa, 13:15-14:00. s. 106 GG.

• Forma zaliczenia: kolokwium zaliczeniowe na ostatnich

zajęciach. Na kolokwium składa się kilka pytań

opisowych i kilkanaście testowych.

• Obecności nie są obowiązkowe, ale będą punktowane

dodatkowo. Każda obecność na wykładzie: +2 pkt.

Zatem samą obecnością można zdobyć ca. 26-28 pkt,

testem zaliczeniowym ca. 94 pkt, razem ca. 130 pkt,

zalicza 65 pkt (na oko).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 2

Informacje organizacyjne 2• Slajdy z wykładu będą dostępne na stronie przedmiotu:

http://tiny.pl/rq8m (będę uaktualniał co jakiś czas).

• Warto bywać na wykładach!

• Na zaliczeniu specjalnie pytam o niektóre szczegóły,

które były na wykładzie wspomniane, ale o których nie

ma ani słowa na slajdach dodatkowa motywacja!

• Warto robić notatki.

• Nie brać slajdów z zeszłych lat, bo trochę będzie zmian

– przedmiot ewoluuje.

• W następnym semestrze będą poważne laborki z tego

przedmiotu – tym bardziej warto robić dobre notatki.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 3

Czym się zajmujemy• Rys historyczny – jak zmienia się wydajność komputerów na

przestrzeni lat, jak ją mierzyć. Dlaczego pojawiły się komputery

wektorowe i na czym polega ich wyjątkowość.

• Na czym polega przetwarzanie równoległe i rozproszone. Podział

komputerów ze względu na sposób przetwarzania.

• Przetwarzanie równoległe, prawo Amdahla, schematy podziału

programu na zadania równoległe (dekompozycja).

• Message Passing Interface (MPI)w szczegółach (40% semestru). Podstawy MPI, komunikacja punktowa,

komunikacja zbiorowa. Efekty synchroniczne, gwarancje. Komunikacja

blokująca (w szczegółach) i nieblokująca (w skrócie). Zakleszczenie.

Pułapki komunikacji zbiorowej.

• Przetwarzanie z pamięcią współdzieloną na przykładzie OpenMP.

• Przetwarzanie GPGPU (karty graficzne) i MIC (koprocesory

Intel Xeon Phi).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 4

Zacznijmy od podstaw

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 5

Mierzenie wydajności obliczeniowej: MIPS

• Million Instructions Per Second – milion instrukcji na

sekundę.

• MIPS≠MHz (dlaczego?).

• Nie uwzględnia wydajności innych komponentów

(przede wszystkim pamięci).

• Nieporównywalny pomiędzy różnymi architekturami.

• Producenci (przez marketingowców) na ogół podają

wydajność szczytową (peak) trochę oszustwo.

• W konsekwencji miara ta popadła w niełaskę

(MIPS – Meaningless Indicator of Processor Speed).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 6

Mierzenie wydajności obliczeniowej: FLOPS

• FLoating-point Operations Per Second (liczba operacji

zmiennoprzecinkowych na sekundę).

• Podobna do MIPS, tyle że koncentruje się na operacjach

zmiennoprzecinkowych ( wydajność FPU nie CPU).

• Lepsza miara wydajności na potrzeby „naukowe”.

• I tak kiepska, wady podobne jak dla MIPS.

• Do tego różnice w wydajności w zależności od precyzji.

[email protected] 7

...

Jak zmienia się moc obliczeniowa?

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 8

Jak zmienia się moc obliczeniowa?

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 9

Kroki podejmowane przez procesor

przy wykonywaniu pojedynczej instrukcji

• Pobranie (wczytanie) instrukcji z pamięci (instruction fetch).

• Dekodowanie. Jeśli instrukcja ma argumenty, być może

trzeba je pobrać z pamięci, a wcześniej obliczyć spod jakiego

adresu.

• Wykonanie (i ew. odesłanie wyniku do pamięci).

• Często kroki te (zwłaszcza dekodowanie) można podzielić

na drobniejsze. Pentium-1 przetwarza instrukcje w 5

etapach, a potok instrukcji Pentium-4 ma aż 24 etapy!

Sposób na zwiększenie wydajności:

• Pobierać i dekodować kolejną instrukcję, gdy bieżąca jeszcze

się wykonuje (pipelining, potokowanie).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 10

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 11

Potokowanie c.d.

• Pozwala znacznie zwiększyć szybkość przetwarzania

instrukcji.

• Stosowane praktycznie we wszystkich nowoczesnych

architekturach (u Intela począwszy od Pentium-1).

• Trudność: gdy instrukcja n zależy od wyników

instrukcji n-1.

• Trudność: gdy instrukcja n jest skokiem warunkowym,

to co będzie instrukcją n+1?

• Koszt: znaczna komplikacja procesora.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 12

Komputer szeregowy vs. wektorowy

• Dodawanie 1M liczb przez komputer szeregowy:

– i=0.

– Pobierz instrukcję (a[i] = a[i] + b[i]).

– Wczytaj po jednej liczbie z pamięci do rejestrów

(pobierz a[i], b[i]).

– Dodaj je do siebie.

– Zapisz wynik do pamięci, do a[i].

– Zwiększ i.

– Czy i<1000000? Jeśli tak, to zapętl się.

• Nie musi to wyglądać dokładnie tak, bo zależy to od architektury

maszyny, ale ważny jest ogólny [email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 13

Komputer szeregowy vs. wektorowy

• Dodawanie 1M liczb przez komputer wektorowy:

– i=0.

– Pobierz instrukcję (a[i..i+63] = a[i..i+63] + b[i..i+63]).

– Wczytaj 64 kolejne liczby z pamięci do długich

rejestrów (pobierz a[i], b[i] i kolejne).

– Dodaj je do siebie parami.

– Zapisz wynik do pamięci, do a[i] i kolejnych.

– Zwiększ i o 64.

– Czy i<1000000? Jeśli tak, to zapętl.(zadbaj o końcówkę, pozostałą po podzieleniu 1000000 przez 64).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 14

Powody zwiększonej wydajności

komputerów wektorowych

• W naszym przykładzie pętla kręci się 64-krotnie razy

mniej. Mamy zatem mniej pobrań instrukcji, mniej

dekodowań, mniej razy sprawdzamy warunek "czy to już

koniec pętli", mniej razy skaczemy na początek.

• Jest bardzo prawdopodobne, że operacja "dodaj do

siebie 64 kolejne liczby" będzie pracowała na wszystkich

liczbach jednocześnie, tj. w komputerze będzie

wyspecjalizowana jednostka dodająca w jednej instrukcji

całe bloki (wektory) liczb.

• To jest główna cecha komputerów wektorowych:są one zdolne wykonywać proste operacje na wieludanych jednocześnie.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 15

Pierwszy komputer wektorowy: ILLIAC IV ('72)

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 16

Pierwszy komputer wektorowy: ILLIAC IV ('72)

• 1 procesor (CPU), ale aż 64 jednostki arytmetyczne

(FPU).

• Ta sama instrukcja arytmetyczna była kierowana do

wszystkich FPU, ale każdy dostawał inną daną do

przetworzenia.

• Dzięki temu długie bloki operacji na ciągach liczb

(wektory, macierze) mogły wykonywać się (prawie)

64 razy [email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 17

Inny znany komputer wektorowy: Cray-1 ('76)

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 18

Inny znany komputer wektorowy: Cray-1 ('76)

• Długie, wektorowe rejestry. Każdy rejestr mógł mieścić

64 liczby 64-bitowe.

• Dzięki temu mógł dodawać, mnożyć, odejmować, etc.

64 (duże) liczby na raz!

• Oddzielne potoki dla różnych instrukcji – np. dodawanie

i odejmowanie realizowane było przez oddzielne układy.

Dzięki temu niektóre instrukcje mogły być wykonywane

współbieżnie – można było jednocześnie dodawać

i odejmować (i to 64 liczby).

• Na tamte czasy – ogromny sukces, był to pierwszy

„udany” komputer wektorowy.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 19

Wektorowość jako jeden ze sposobów zwiększenia mocy obliczeniowej

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 20

Komputery wektorowe

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 21

MS XBox – wykazuje pewne cechy komputera

wektorowego. Potrafi operować jednocześnie na 4

liczbach zmiennoprzecinkowych. Wydajność operacji

na liczbach całkowitych również imponująca dzięki

zastosowaniu 64-bitowej technologii MMX. Główne

zastosowanie to grafika, a tę łatwo przetwarzać

wektorowo. Stąd jest to (było) doskonałe narzędzie do

gier.

Komputery wektorowe

Wektorowość jako jeden ze sposobów zwiększenia mocy obliczeniowej

GPGPU – comeback technik wektorowych

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 22

• General-Purpose computing on Graphics Processing Units.

• Stosunkowo nowy trend: wykorzystanie dedykowanych kartgraficznych do przetwarzania danych niekoniecznie mającychnaturę graficzną.

Emerald (Oxford) 2012

372 GPU NVIDIA Tesla M2090

(114 TFLOPS).

NVIDIA Tesla V100 2017

(7.5 TFLOPS).

Prawo Moore'a

• Skomplikowanie procesorów podwaja się co 2 lata (niektórzy mówią o

18 miesiącach, niektórzy o 3 latach).

• Skomplikowanie ≈ liczba tranzystorów.

• Inne ujęcie prawa Moore'a (patrz wykres): Liczba obliczeń na sekundę

za 100$ włożonych w sprzęt rośnie w sposób wykładniczy.

• „Prawo” działa od 1965 do dziś, w teorii nawet [email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 23

Prawo Moore'a – uwagi

• "Niedługo komputery będą tak szybkie, że nie trzeba będzie ich już dalej

przyspieszać" – to nieprawda! Apetyt rośnie w miarę jedzenia. Są

zastosowania komputerów (choćby meteorologia, molekularna chemia

białek, wojsko, kryptografia), w których dostępna moc obliczeniowa

zawsze będzie za mała. Zapotrzebowanie na moc rośnie co najmniej tak

szybko jak możliwości…

• Nie tylko procesor trzeba przyspieszać, także I/O i pamięć. Prędkości

pamięci i dysków nie rosną tak szybko (choć pojemności – tak).

• Przyspieszenia nie osiąga się tylko za pomocą szybszego zegara (dwa inne

mechanizmy już poznaliśmy: potokowanie i wektoryzacja). Ciepło

wydzielane w procesorze drastycznie rośnie z częstotliwością taktowania,

więc dużo szybciej taktowanych procesorów nie da się (na razie?)

zbudować.

• Nie samym sprzętem user żyje: jakość oprogramowania zdecydowanie

nie polepsza się wykładniczo. "Software gets slower faster than hardware gets faster".

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 24

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 25

Postępująca miniaturyzacja układów scalonych

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 26

Prawo Moore'a – co będzie?

• Podstawowe pytanie – czy prawo Moore'a będzie nadal w

mocy za 20 lat? Za 100 lat?

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 27

?

Prawo Moore'a – co będzie?

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 28

Prawo Moore'a – czy będzie nadal w mocy?

• Na NIE: Dochodzimy do granic technologii półprzewodnikowej (teraz jest≈2.3mln tranzystorów na mm2 (Intel Core i7), 2017r: przechodzimy ztechnologii 14 nm na 10 nm). Dalej zmniejszać układy będzie niezwykle trudno(może nawet się da, ale może się to nie opłacać). Technika fotolitograficzna zapomocą której wykonuje się układy jest u granic możliwości. Zmorą stają sięprądy upływu w tak małych tranzystorach, rosną elektryczne pojemnościpasożytnicze oraz wydzielające się ciepło. Efekty kwantowe, ziarnistość materii.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 29

Prawo Moore'a – czy będzie nadal w mocy?

• NIE: Przewidywana granica opłacalności: 22 nm. 14 nm. 10 nm?

• Lewy wykres: inne ujęcie prawa Moore’a: jak rozmiar elementów w układziescalonym maleje w czasie.

• Prawy wykres: Koszt [mld $] budowy fabryki układów scalonych.

• Dla porównania: budżet UE ~180 mld $.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 30

Prawo Moore'a – czy będzie nadal w mocy?

• TAK: Takie same problemy były u kresu epoki lamp i epoki tranzystorów.Jeśli uwzględnić działanie wstecz, prawo Moore'a działa już piątą generacjęurządzeń.

• Prawdopodobne jest, że epoka układów scalonych skończy się za kilkanaścielat – i tak trwa już długo. Przewiduje się, że przed 2030 rokiem elektronikazostanie zastąpiona nową technologią. Kandydaci: komputery kwantowe,optyczne lub optoelektroniczne, komputery molekularne, spintronika…

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 31

Prawo Moore'a – co przez najbliższe 20 lat?

• Wspomniane technologie, które mogłyby zastąpić elektronikę są w

fazie eksperymentalnej, na pewno nie trafią na rynek w ciągu 20 lat.

• Co robić, żeby utrzymać wzrost wydajności? Większość sposobów

na poprawę wydajności już wykorzystaliśmy (potokowanie,

jednoczesne wykonywanie operacji arytmetycznych i I/O, cache,

szybsze taktowanie). Wektoryzacja jest droga.

• Odpowiedzią wydają się komputery równoległe – układy wielu

(na ogół identycznych) jednostek, pracujących wspólnie.

• Łatwiej zrobić sto wolnych komputerów i je połączyć, niźli

jeden sto razy szybszy.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 32

Równoległość jako jeden ze sposobów zwiększenia mocy obliczeniowej

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 33

Komputery równoległe

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 33

Komputer równoległy, system rozproszony

• Równoległy system komputerowy - zbiór co najmniej dwóch

procesorów zdolnych do wspólnego rozwiązywania złożonego

zadania obliczeniowego, na ogół o tej samej architekturze i pod

kontrolą tego samego systemu operacyjnego.

• Rozproszony system komputerowy - zbiór samodzielnych

komputerów połączonych za pomocą sieci, z rozproszonym

oprogramowaniem systemowym, często o różnych

architekturach i systemach operacyjnych.

• Podział nie jest precyzyjny. Typowe systemy równoległe są na

ogół zwarte geograficznie (jeden pokój, budynek) i połączone

bardzo szybkim łączem (np. gigabit ethernet albo i szybszym,

dedykowanym), systemy rozproszone na ogół będą rozległe

geograficznie i połączone wolnym łączem (np. komputery

domowe użytkowników w kilku krajach, połączone

Internetem).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 34

Komputer równoległy – przykład:

tryton w centrum TASK

• 1607 serwerów = 3214 procesorów = 38568 rdzeni (Intel Xeon E5 @2.3GHz).• Pamięć: 128 lub 256 GB na serwer (DDR4).• Pamięć dyskowa: 240 TB i w trakcie rozszerzania.• To wszystko połączone siecią InfiniBand FDR @56 Gb/s.• Wydajność teoretyczna 1.48 PFLOPS.• +48 akceleratorów(Xeon Phi, NVIDIA Tesla).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 35

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 36

Komputer równoległy – przykład:

tryton w centrum TASK

na liście top500

Komputer równoległy – przykład:

TITAN

• Oak Ridge National Lab, #4 na liście top500 (2017.06).• 560 640 rdzeni.• Całkowita pamięć RAM: 710 TB.• Interconnect: Cray Gemini @160 Gb/s.• Wydajność teoretyczna: 27.1 PFLOPS.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 37

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 38

System rozproszony – przykład:

projekt SETI@home

Setki tysięcy komputerów niezależnych od siebie użytkowników, głównie PC,połączonych Internetem, wykonują ten sam program w czasie wolnym (naogół jako wygaszacz ekranu).

Średnio 900k użytkowników. Przy ok. 1M użytkowników osiąga łączną wydajność 281 TFLOPS. Różne systemy operacyjne (Windows, Mac OS, Linux). Rozciągłość geograficzna: cały glob.

Taksonomia Flynna

• Podział komputerów ze względu na sposób przetwarzania

danych.

• Cztery główne sposoby:– SISD (single-instruction, single-data) - w każdym kroku wykonuje jedną

instrukcję na jednej danejtypowy komputer szeregowy,

– SIMD (single-instruction, multiple-data) - w każdym kroku wykonuje jedną

instrukcję, ale na wielu danych jednocześnie typowy komputer

wektorowy,

– MISD (multiple-instruction, single-data) - w każdym kroku wykonuje wiele

instrukcji jednocześnie, ale wszystkie pracują na jednej danej w

praktyce raczej nie spotykane, komputery eksperymentalne,

– MIMD (multiple-instruction, multiple-data) - w każdym kroku wykonuje wiele

instrukcji jednocześnie, na wielu danych typowy komputer

równoległy, ale o tym później.

• Analogia z celnikami.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 39

Taksonomia Flynna – uwagi• W nowoczesnych komputerach szeregowych można spotkać

rozwiązania podpadające pod SIMD – np. technologia SSE

(streaming SIMD extension) w Pentium-3 i nowszych umożliwia

ograniczone przetwarzanie wektorowe.

• Obecnie tradycyjne komputery SIMD (wektorowe) są na

wymarciu, zastępowane przez komputery równoległe. Spotyka

się natomiast rozwiązania wektorowe na mniejszą skalę, dzięki

temu, że technologia tanieje, np. PowerPC AltiVec bądź moduły

graficzne (znowu XBox). Operacje graficzne doskonale nadają

się do przetwarzania wektorowego (dlaczego?).

• Podobnie: GPGPU, MIC.

• Komputery SIMD przetwarzają dane synchronicznie, wszystkie

procesory w danej chwili wykonują tę samą instrukcję.

• W architekturze MIMD najczęściej wszystkie procesory

wykonują ten sam program, ale ścieżka wykonania jest funkcją

numeru procesora ("każdy jest gdzie indziej w programie").40

Jak wygląda dostęp do pamięci w komputerach

równoległych?

• Jedno podejście – pamięć współdzielona (shared memory, SM)

• Wszystkie procesory mają dostęp do tej samej pamięci, która jest

współdzielona – dysponują tą samą przestrzenią adresową.

• Komunikacja między procesorami może odbywać się poprzezpamięć, w konsekwencji jest łatwa i szybka.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 41

CPU#1

cache

pamięć operacyjna

CPU#2

cache

CPU#3

cache

CPU#N

cache

...

Pamięć współdzielona – trudności

• Co jeśli dwa procesory chcą jednocześnie pisać w to samo miejsce pamięci?Co jeśli jeden pisze, a drugi w tej samej chwili czyta? Trudności zsynchronizacją.

• Problemy natury technologicznej – potrzebne specjalne układy (krosownice)pozwalające łączyć jednocześnie wiele procesorów z jedną pamięcią.Trudności te rosną drastycznie dla nproc>8. Dla nproc>20 niewykonalnepraktycznie albo niepraktycznie drogie.

• Przy większej liczbie procesorów pogarsza się też wydajność ze względu nakonieczność synchronizacji. Dostęp do pamięci nie może być prawdziwiejednoczesny, zawsze wszystkie procesory oprócz jednego muszą chwilępoczekać.

• Bardzo wygodne dla programisty, koszmar dla inżyniera projektującegokomputer. Droga.

42

CPU#1

cache

pamięć operacyjna

CPU#2

cache

CPU#3

cache

CPU#N

cache

...

krosownica (crossbar switch)

Jak wygląda dostęp do pamięci w komputerach

równoległych?

• Drugie podejście – pamięć rozproszona (distributed memory, DM).

• Każdy procesor dysponuje swoją własną pamięcią (przestrzeniąadresową).

• Procesor n „nie wie” co dzieje się w pamięci procesora m, dla m ≠ n.

• Względnie łatwe do zbudowania dla inżyniera – bierze się oddzielnekomputery, każdy ze swoją pamięcią i łączy się razem szybkim łączem.

• Trudne dla programisty – jak sprawić, żeby procesory mogły siękomunikować, wymieniać dane? Potrzebny jest sposób na wymianęwiadomości. 2004-2017 Jacek Dziedzic, FTiMS, PG 43

CPU#1

cache

pamięć operacyjna #1 ...

interconnect (sieć)

CPU#2

cache

pamięć operacyjna #2

CPU#3

cache

pamięć operacyjna #3

CPU#N

cache

pamięć operacyjna #N

Pamięć rozproszona – trudności

• Skoro nie mogą zajrzeć do nieswojej pamięci, procesory muszą

wymieniać wiadomości za pośrednictwem łącza (sieci), którym

są połączone. W ten sposób przekazują sobie dane nawzajem.

• Programista musi to uwzględnić w sposób jawny w programie.

• N procesorów, gdyby połączyć każdy z każdym, to mamyN(N-1)/2 ≈ N2/2 połączeń – niepraktyczne dla większych N,dlatego łączymy tylko wybrane procesory – topologie.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 44

CPU#1

cache

pamięć operacyjna #1 ...

interconnect (sieć)

CPU#2

cache

pamięć operacyjna #2

CPU#3

cache

pamięć operacyjna #3

CPU#N

cache

pamięć operacyjna #N

Przykładowe topologie

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 45

a) drzewo (tree), b) gwiazda (star), c) siatka (grid),

d) pierścień (ring), e) hipertorus (hypertorus), f) hipersześcian (hypercube).

Jak wygląda dostęp do pamięci w komputerach

równoległych?

• Podejście pośrednie – rozproszona pamięć współdzielona (distributed shared

memory, DSM).• Próbuje połączyć zalety obydwu modeli pamięci (łatwość budowy DM i łatwość

oprogramowania SM).

• Fizycznie mamy do czynienia z pamięcią rozproszoną, a pamięć współdzieloną emuluje

się, najczęściej na poziomie systemu operacyjnego, jako uogólnienie pamięci wirtualnej.

• Wszystkie procesory widzą jedną logiczną przestrzeń adresową, dostęp do tych jej części,

które są nielokalne ("leżą na innych procesorach") realizowany jest za pomocą sieci, w

sposób niejawny, tj. program nie wie, że transmisja taka ma miejsce.

• Wada: wydajność drastycznie spada, jeśli lokalność jest słaba. Programista musi o tym

pamiętać.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 46

obrazek © Christian Schaubschläger

Cechy połączeń między węzłami w komputerze równoległym

• Węzły łączymy siecią – jeśli komunikacji między nimi będzie

niemało, to wydajność komputera równoległego będzie zależeć

od parametrów sieci.

• Dwa istotne parametry:

• przepustowość (bandwidth) – prędkość z jaką przesyłane są dane

[MB/s] albo [Mbit/s=Mbps=Mb/s].

• opóźnienie związane z rozpoczęciem komunikacji (latency,

zwłoka) – narzut wymuszony koniecznością przygotowania sieci

do transmisji danych. Najczęściej składa się na niego część stała i

część rosnąca liniowo wraz z rozmiarem danych, czyli

tlat=a∙size + b. Na ogół jest rzędu 1-500 µs. Czy to dużo, czy mało?

Dla człowieka mało: 100µs to 0.001 mgnienia oka, dla maszyny

1GHz 100µs to ≈100000 cykli.

• Oczywiście interesuje nas jak największa przepustowość i jak

najmniejsza zwłoka, ogranicza nas cena.47

Połączenia między węzłami

– szybkie porównanie

• Częściej spotykane są rozwiązania dedykowane (dla komputerów

równoległych), np.

• Avici: 20 Gbps, niezłe latency (≈ 10µs).

• Infiniband: do 300 Gbps, małe latency. (<1 µs).

• Na tryton.task.gda.pl: Infiniband FDR 56 Gbps.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 48

rozwiązanie maksymalna przepustowość

przeciętna cena 2017r. (2007r.)

modem 0.0003-0.056 Mbps 50 (20) PLN

Ethernet '79-'85 10 Mbps 22 (8) PLN karta

Fast Ethernet '95 100 Mbps 22 (20) PLN karta35-150 (30-250) PLN switch

Gigabit Ethernet '98(latency ~ 50 µs)

1 000 Mbps 40-100 (50-500) PLN karta60-600 (250-150k) PLN switch

10 Gigabit Ethernet '2002 10 000 Mbps 300-1200 PLN karta700-8k PLN (300k-750k) PLN switch

100 Gigabit Ethernet '2015 100 000 Mbps 3k-300k PLN karta

Przetwarzanie równoległe MIMD-DM

• Tym rodzajem przetwarzania będziemy się zajmować przez większość

semestru.

• Najważniejsze do zapamiętania: taki superkomputer to nie monolit,

tylko zespół oddzielnych jednostek połączonych siecią, a każda z nich

ma swoją, odrębną pamięć.

• "Same z siebie" nie są w stanie pracować wspólnie nad jednym

problemem, to programista musi zadbać o rozłożenie zadania

obliczeniowego na poszczególne węzły ( dekompozycja problemu).

• "Jeden robotnik potrzebuje 10 minut na wykopanie otworu pod znak drogowy, więc

600 robotników zrobi to w sekundę" to ta sama pomyłka co "Komputer

szeregowy potrzebuje roku na wykonanie obliczenia, więc komputer równoległy o

365 procesorach wykona je w jeden dzień".

• Program musi być napisany w sposób uwzględniający

równoległość, w ostateczności trzeba przerobić (zrównoleglić) gotowy

program szeregowy.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 49

Przetwarzanie równoległe MIMD-DM vs MIMD-SM

• Czasami w pisaniu programu równoległego może pomóc

kompilator. Istnieją kompilatory zrównoleglające (np. HPF –

High Performance Fortran, Intel C++ compiler, kompilatory

PGI), które potrafią same zrównoleglić najprostsze konstrukcje

(np. trywialne pętle), a z pomocą człowieka (specjalne dyrektywy

dodawane w kodzie) są w stanie zrównoleglić konstrukcje

średnio-skomplikowane.

#pragma omp parallel for

for (i=0; i < numPixels; i++) {

pGrayScaleBitmap[i] = (unsigned BYTE)

pRGBBitmap[i].red * 0.299 +

pRGBBitmap[i].green * 0.587 +

pRGBBitmap[i].blue * 0.114);

}

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 50

Specjalna dyrektywa kompilatora dodana przed

pętlą instruuje go, aby rozdzielił iteracje pętli

między wiele wątków. Jeśli pętla jest odpo-

wiednio prosta (z góry znana liczba iteracji, brak

zależności obiegu n od obiegu n-1, etc.),

kompilator automatycznie wystartuje dodat-

kowe wątki, rozdzieli im pracę, zbierze wyniki

po czym pouusuwa wątki. Przykład nie ilustruje

programowania równoległego MIMD-DM,

tylko MIMD-SM – taki podział jest ograniczony

do procesorów na jednej maszynie (brak

komunikacji po sieci, a pamięć współdzielona).

OpenMP (MIMD-SM)

Fragment programu korzystającego z kompilatora

zrównoleglającego (HPF)

PROGRAM main

IMPLICIT NONE

INTEGER N

PARAMETER (N=1000)

INTEGER i, procnum(N), procsum(N), sum1, sum2

!HPF$ DISTRIBUTE PROCNUM(BLOCK)

!HPF$ ALIGN PROCSUM(I) WITH PROCNUM(I)

FORALL (i = 1:N) procnum(i) = i

sum1 = SUM(procnum)

PRINT *, 'Sum using global reduction is ', sum1

procsum = 0

DO i = 1, N

procnum = CSHIFT(procnum,1)

procsum = procsum + procnum

END DO

sum2 = procsum(1)

PRINT *, 'Sum using local shifts is ', sum2

FORALL (i = 1:N)

procnum(i) = procsum(i) - procsum(1)

END FORALL

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 51

Dodatkowe dyrektywy i rozszerzenia języka

służą do realizacji równoległości.

To też podejście MIMD-SM.

Dekompozycja problemu

• Zdolności kompilatora do "auto-zrównoleglania" są

ograniczone, człowiek wciąż potrafi zrobić to lepiej.

• Na ogół, w poważnych zastosowaniach, musimy sami

rozdzielić zadanie obliczeniowe tak, aby nadawało się do

przetwarzania równoległego. Proces ten nazywa się

dekompozycją problemu.

• Poznamy następujące sposoby dekompozycji:– dekompozycja trywialna,

– dekompozycja funkcjonalna,

– dekompozycja danych:• geometryczna,

• rozproszona przestrzenna (ssd),

• farma zadań (task farm).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 52

Dekompozycja trywialna

• Występuje, gdy da się podzielić dane na niezależne od siebie

części, z których każdą da się potraktować tym samym

algorytmem.

• Inna nazwa: embarassingly parallel problem, czyli "zagadnienie

żenująco proste do zrównoleglenia".

• Każdy procesor dostaje porcję danych do przetworzenia i zajmuje

się nią. Brak zależności → brak komunikacji (co najwyżej

rozesłanie danych na początku i zebranie wyników na końcu).

• Przykład: zwiększanie jasności bitmapy 1000x1000.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 53

1000

1000procesor #1

procesor #3

procesor #2

procesor #4

Dekompozycja trywialna

• Przykład: obliczanie sił działających między atomami z podziałem

składowe-sił-na-procesory.

• Przykład: numeryczne obliczanie całki.

• Częste zastosowanie: metody Monte Carlo.

• Czas wykonania = czas potrzebny najdłuższemu procesowi, czyli

istotne jest równe rozkładanie obciążenia (load balancing).

• Replikacja danych (data replication) – szczególny przypadek

dekompozycji trywialnej. Na czym polega?

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 54

procesor #1 procesor #3procesor #2 procesor #4

Dekompozycja funkcjonalna

• Nie dzielimy danych, tylko algorytm.

• Dzielimy zadanie na niezależne sekcje, każdy procesor zajmuje się

jedną sekcją, wyniki z sekcji n idą do sekcji n +1, a sekcja n może

zajmować się kolejnymi danymi.

• Przetwarzanie taśmowe (pipelining), bo dane płyną wzdłuż taśmy –

podejście analogiczne do potokowania (też pipelining), o którym

mówiliśmy już w kontekście jednego procesora.

• Analogia z fabryką samochodów: jedna sekcja montuje silnik i

bebechy, jedna podwozie, jedna karoserię, jedna klamki.

Oczywiście nie da się zamontować klamek przed zamontowaniem

karoserii, ale gdy montujemy klamki w samochodzie k, można

zakładać karoserię w samochodzie k+1, podwozie w

samochodzie k+2, a silnik w samochodzie k+3.

• Przestoje na początku i na końcu (dlaczego?), wobec czego musi

być dużo danych, żeby było to wydajne.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 55

Dekompozycja funkcjonalna c.d.

• Nie da się bardziej zrównoleglić niż

wynosi liczba sekcji.

• Ale można bardziej pracochłonne

sekcje podzielić na podsekcje albo

przydzielić kilka procesorów do jednej

sekcji (jeśli np. samą sekcję można

zdekomponować trywialnie – w

analogii samochodowej: czterech

pracowników może jednocześnie

zakładać klamki, każdy po jednej).

• Równomierny podział obciążenia

jeszcze istotniejszy – jeśli jedna sekcja

zwleka, wszystkie następne muszą na

nią czekać.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 56

źródło: pcc.qub.ac.uk

Dekompozycja danych

• Dzielimy dane na procesory, podobnie jak w dekompozycji

trywialnej, ale tym razem mogą występować zależności między

danymi przypisanymi różnym procesorom.

• Zależności potrzeba komunikacji.

• Bardzo często występująca dekompozycja.

• Trzy często spotykane odmiany: dekompozycja danych-

geometryczna, rozproszona dekompozycja przestrzenna

(scattered spatial decomposition, ssd) i farma zadań (task farm).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 57

#1 #2

#3 #4

Dekompozycja danych-geometryczna

• Każdy procesor dostaje porcję danych do przetworzenia, porcje

określamy dokonując geometrycznego podziału dziedziny.

• Przykład: drastyczne pomniejszanie bitmapy, gdzie każdy

procesor dostaje fragment bitmapy do przetworzenia, ale

potrzebuje danych od sąsiadów.

• Przykład: obliczanie sił działających między cząstkami.

• Przykład: zilustrowany poniżej.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 58

procesor #1 procesor #2 procesor #3

Dekompozycja danych-geometryczna

• Mogą występować problemy z bilansowaniem pracy – trzeba

pilnować, żeby żaden procesor nie został bez pracy (np. gdyby

cząstki zebrały się w jednym rogu pudła).

• Pracę można bilansować dynamicznie, np. przesuwając granice

podziału na procesory. Wiąże się to jednak ze skomplikowaniem

algorytmu (np. przenoszenie cząstek z jednego procesora na

drugi, gdy granica się przesuwa) oraz…

• … trzeba pilnować, żeby komunikacja nie zdominowała czasu

przetwarzania – aby wszystko szło sprawnie obliczenia powinny

być prawie-lokalne – przyda się dobra topologia.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 59

#1 #2

#3 #4

#1 #2

#3 #4

Rozproszona dekompozycja przestrzenna

• A.k.a. scattered spatial decomposition, ssd.

• Dzielimy dane na małe ziarna ("kawałki pracy").

• Każdemu procesorowi przydzielamy losowo trochę ziaren do

obliczeń.

• W naszym przykładzie z dynamiką molekularną ziarnem może

być "obliczenie siły działającej na jedną cząstkę, pochodzącej od wszystkich

pozostałych cząstek".

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 60

#1 #2

#3 #4

#1 #2

#3 #4

To jest jedno ziarno To jest drugie ziarno

Rozproszona dekompozycja przestrzenna

• Istotna sprawa: ziarna muszą być bardzo małe, tak aby każdy

procesor dostał wiele ziaren. Gdy tak jest, Centralne Twierdzenie

Graniczne gwarantuje nam, prawie-doskonały rozkład pracy.

• Niestety, kosztem jest potencjalnie mała lokalność obliczeń

(bo nie przydzielamy procesorom zwartych "stref danych", tylko

wiele drobnych, rozproszonych kawałeczków).

• Przykład: (na granicy dekompozycji trywialnej) rozjaśnianie dużej

bitmapy bloczkami 32x32 px.

• Przykład (poprzedni slajd): obliczanie sił działających na cząstki z

przypisaniem losowe-cząstki-do-procesorów.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 61

Task-farm

• Farma zadań, fabryka zadań (obie nazwy mało popularne).• Blisko związane z koncepcją master-slave.• Dzielimy procesory na dwie grupy: nadzorców i robotników, w

najprostszym przypadku jest tylko jeden nadzorca (master) irobotnicy (slaves).

• Nadzorca dzieli zadanie na ziarna i wrzuca je do puli ziaren doprzetworzenia.

• Każdy z robotników, do skutku:– prosi o przydzielenie ziarna pracy,– otrzymuje ziarno,– przetwarza ziarno,– wysyła wyniki do odbiorcy.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 62

zadanie obliczeniowe

pula ziarenrobotnicy (slaves)

nazdorca (master)ziarno

Task-farm c.d.

• Odbiorca składa wyniki w całość. Odbiorcą często jest nadzorca,

żeby nie nudził się tak drastycznie.

• Znowu CTG gwarantuje nam świetny rozkład pracy, jeśli ziarna

są drobne, nawet lepszy niż w ssd, bo obciążenia bilansują się

dynamicznie (kto skończył wcześniej, dostaje nową pracę).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 63

Task-farm c.d.

• Analogia: hurtowa pracownia lutnicza, gdzie każdy z

pracowników potrafi wykonywać każdą z czynności (przygotować

deskę, założyć struny, założyć klucze, ustawić menzurę, nastroić).

Jeśli jest dużo gitar do przygotowania, to każdy z pracowników

przychodzi do szefa po coś do zrobienia, a jak skończy,

przychodzi po nowe zadanie.

• Przestojów nie ma w ogóle, najwyżej na końcu pracy, gdy brakuje

ziaren.

• Niestety nie może być zależności między ziarnami – wtedy

robotnicy musieliby się komunikować.

• Przykład: rozjaśnianie bitmapy bloczkami 32x32.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 64

Dekompozycje – podsumowanie

• Najważniejszy morał – na nas, jako programistach, spoczywa

problem takiego postawienia problemu (zdekomponowania

zadania), żeby nadawał się do pracy na komputerze równoległym.

• Jest wiele sposobów na dekompozycję – poznaliśmy najbardziej

podstawowe, często różne dekompozycje łączy się.

W domu:• Przeczytać: http://tiny.pl/qs49, punkty 1-3.

• Przeczytać: http://tiny.pl/qspc.

• Przeczytać pobieżnie: http://tiny.pl/qs4r.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 65

Przyspieszenie, efektywność

• Przyspieszenie (speed-up), S(N), mówi nam ile razy szybciej program

równoległy działa na N rdzeniach w porównaniu z wersją szeregową.

• Oczywiście przyspieszenie rośnie wraz z N (poza przypadkami

patologicznymi).

• Wgląd w to, jak dobrze zrównoleglony jest program daje efektywność

(efficiency), E(N).

• Zatem efektywność jest "przyspieszeniem odniesionym do liczbyprocesorów, na których to przyspieszenie uzyskano".

• Zobaczmy jak duże przyspieszenie możemy osiągnąć w praktyce. NiechF oznacza procent zrównoleglenia naszego programu (w sensie czasudziałania). Przykładowo, jeśli 95% nakładu programu udało sięzrównoleglić, a 5% pozostało szeregowe, to F=0.95.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 66

Przyspieszenie, efektywność. Prawo Amdahla

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 67

Zatem nasz przykładowy program nigdy nie osiągnieprzyspieszenia lepszego niż 20-krotne, bez względu na to ilomaprocesorami dysponujemy! Powodem tego jest to 5% programu,które pozostało szeregowe.

Zatem przyspieszenie:

Czas pracy programu po zrównolegleniu:

Gdy mamy dowolnie dużo procesorów (tj. N dąży do

nieskończoności):

Przyspieszenie, efektywność. Prawo Amdahla

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 68

Jeśli można zrównoleglić F-tą część programu, pozostawiając

(1-F)-tą część szeregową, to maksymalne przyspieszenie

wyniesie 1/(1-F) (niezależnie od liczby procesorów).

Prawo Amdahla (Amdahl's Law)

Krytyka prawa Amdahla

• Nie wszyscy wierzą w prawo Amdahla!

• Ważny argument przeciwko temu prawu:

• Prawo Amdahla po cichu zakłada, że ułamek zrównoleglenia, F,

nie zależy od N i jest stały. W rzeczywistości często jest tak, że

im więcej procesorów, tym większą część programu można

zrównoleglić, czyli F rośnie z N.

• Na ogół jest tak, że słabo zrównoleglonych zadań nie

uruchamiamy na dużej liczbie procesorów – na ogromne

maszyny trafiają tylko dobrze zrównoleglone (massively parallel)

programy.

• Podsumowując krytykę: najczęściej ułamek zrównoleglenia, F,

zależy od rozmiaru problemu, x, tj. F rośnie wraz z x ("duże

problemy lepiej się zrównoleglają"). Na wielkich komputerach

rozwiązujemy wielkie problemy, zatem x rośnie z N. Zatem F też

rośnie z N, a nie jest stałe, jak postuluje Amdahl.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 69

Prawo Gustafsona

• Formalnie ujmuje krytykę prawa Amdahla.• Przyjmijmy czas wykonania programu zrównoleglonego za

jednostkowy i rozłóżmy go na dwie części – szeregową, a,i równoległą, b,:

• Obie wartości: a, b zależą od rozmiaru problemu (x).• Na komputerze szeregowym ten sam program będzie się

wykonywał w ciągu

• A zatem przyspieszenie:

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 70

a bproc. 1proc. 2proc. 3proc. 4

a b b b…

Prawo Gustafsona

• Wiemy, że przyspieszenie dane jest przez:

• Zakładamy, że wraz ze wzrostem rozmiaru problemu, x, część

szeregowa, a(x) maleje – zatem przyspieszenie S dąży do N

– tego właśnie chcemy – żeby program przyspieszał liniowo wraz

z liczbą procesorów!

• Dużo bardziej optymistyczny pogląd w porównaniu z prawem

Amdahla.

• Analogia samochodowa (za wikipedią):

– prawo Amdahla: jeśli przejechałeś połowę drogi z prędkością 40 km/h, to

choćbyś jechał drugą połowę nieskończenie szybko, nie osiągniesz lepszej

średniej prędkości niż 80 km/h.

– prawo Gustafsona: jeśli jechałeś przez godzinę z prędkością 40 km/h, to

możesz osiągnąć nawet i średnią prędkość 100 km/h, jeśli odpowiednio

długo będziesz jechał 120 km/[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 71

Inny czynnik, który daje nadzieję,

mimo prawa Amdahla

• Załóżmy, że stosujemy dekompozycję danych. Wówczas, wraz ze

wzrostem liczby procesorów maleje ilość danych przypadających

na procesor. Często zdarza się, że dla pewnej odpowiednio dużej

liczby procesorów N, całe dane zaczynają mieścić się w pamięci

cache procesora, co skutkuje dramatycznym wzrostem

wydajności.

• Zjawisko to może skutkować przyspieszeniem superliniowym

i, w konsekwencji, efektywnością E(N)>1, która jest –

teoretycznie – niemożliwa do osiągnięcia.

[email protected] 72

podział danych

przy 1 procesorzepodział danych

przy 4 procesorachpodział danych

przy 16 procesorach

rozmiar

cache L2

nagły skok

wydajności

Jak skalują się obliczenia wraz z liczbą procesorów –

przykłady z życia wzięte

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 73

kod dobrze

zrównoleglony –

skaluje się

praktycznie

liniowo do 250

rdzeni

Jak skalują się obliczenia wraz z liczbą procesorów –

przykłady z życia wzięte

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 74

kod beznadziejnie

zrównoleglony –

niezależnie od liczby

procesorów nie

udało się osiągnąć

przyspieszenia

większego niż 4.

Jak skalują się obliczenia wraz z liczbą procesorów –

przykłady z życia wzięte

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 75

kod w miarę

dobrze

zrównoleglony,

ale powyżej

kilkudziesięciu

rdzeni nie będzie

się skalował.

UWAGA: to nie

jest wykres S(N),

tylko czasu

wykonania!

Jak skalują się obliczenia wraz z liczbą procesorów –

przykłady z życia wzięte

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 76

Jak skalują się obliczenia wraz z liczbą procesorów –

przykłady z życia wzięte

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 77

Jak skalują się obliczenia wraz z liczbą procesorów –

przykłady z życia wzięte

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 78

Skalowanie programu ONETEP: K. Wilkinson, N.D.M. Hine, C.-K. Skylaris, JCTC 10 (11) 2014.

Jak skalują się obliczenia wraz z liczbą procesorów –

przykłady z życia wzięteEfektywność, E(N).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 79

Metryka Karpa-Flatta

• Inna miara zrównoleglenia.

• Opisuje "eksperymentalnie wyznaczony ułamek szeregowy", czyli jest

sposobem na empiryczne wyznaczenie (1-F).

• Dana jest wzorem:

dla N>1.

• Oczywiście im mniejsze e(N), tym lepiej. Empiryczność metody

polega na tym, że stojące we wzorze przyspieszenie mierzymy,

nie obliczamy.

• Pozwala zdiagnozować wąskie gardło zrównoleglenia na podsta-wie zachowania e(N) wraz ze wzrostem N:– gdy e(N) rośnie: mamy do czynienia z narzutami równoległości

(komunikacja, źle zbilansowana praca).– gdy e(N) pozostaje stałe: spora część programu pozostaje szeregowa, tj. nie

została zrównoleglona.– gdy e(N) maleje: mamy przyspieszenie superliniowe (na ogół: efekty cache).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 80

MPI – the Message Passing Interface

• Na początku lat '90 popularność komputerów równoległych szybko

rosła. Każdy z producentów proponował swoje rozwiązania

umożliwiające programowanie równoległe.

• Chaos, brak standardu.

• 1993: IBM, Intel, Express, p4 i inni tworzą szkic standardu.• http://www.netlib.org/utk/papers/mpi-book/mpi-book.ps

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 81

MPI – charakterystyka• Kod jest napisany w „zwyczajnym” języku programowania (Fortran 77,

Fortran 90, C, C++); przesyłanie wiadomości jest realizowane poprzezwywołanie odpowiednich procedur lub funkcji.

• Wszystkie zmienne są lokalne dla danego procesora; inny procesor madostęp do ich wartości tylko poprzez wymianę wiadomości.

• Najczęstsze podejście: każdy procesor realizuje ten sam programwykonywalny, jednak występuje podział na procesor (procesory)nadzorujące (master) oraz „robotników” (workers) lub „niewolników”(slaves); realizują one inne fragmenty kodu, niż master.

• if(i_am_master) {

// do what a master does

}

else {

// do what a slave does

}

• Każdy procesor ma własny identyfikator (0, 1, ..., NPROC-1).

• Zatem niby wszystkie procesory ten sam program realizują, ale ścieżkawykonywania jest funkcją numeru procesora.

fragmenty© Adam Liwo

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 82

MPI – jak skonstruowane są wiadomości

• Wiadomość: pakiet danych przemieszczających się między procesorami.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 83

• Podobnie jak list czy faks, oprócz właściwych przesyłanych danych musiona być opakowana w „kopertę” (zawierać nagłówek) umożliwiający jejdostarczenie do właściwego odbiorcy.

• „Koperta” musi zawierać następujące informacje dla systemu przesyłaniawiadomości:

• Nadawca (procesor wysyłający),

• Typ przesyłanych danych,

• Długość przesyłanych danych (liczba elementów),

• Odbiorca (procesor(y) odbierające),

• Identyfikator („tag”), który pozwala odróżnić wiadomość od innych.

fragmenty© Adam Liwo

MPI – c.d.• Procesory możemy podzielić na grupy, tzw. komunikatory.

• Wiadomości są przesyłane zawsze w obrębie jednego komunikatora – pozwala touniknąć wysyłania wiadomości zbiorczych ("list otwarty" – do wszystkich) doprocesorów, które nie mają ich odczytać.

• Pozwala to na izolację "grup zadaniowych" od innych.

• W prostych przypadkach mamy tylko jeden komunikator zwanyMPI::COMM_WORLD, który grupuje wszystkie dostępne procesory.

Dwa sposoby wysyłania wiadomości:

• Wstrzymujące (blocking send) - nadawca wstrzymuje dalszą akcję do czasupotwierdzenia dotarcia wiadomości (można to porównać do wysyłania faksu lubrozmową telefoniczną). Łatwiejsza do wykonania, ale mniej wydajna.

• Niewstrzymujące (nonblocking send) - nadawca po wysłaniu wiadomości możewykonywać coś innego (obliczenia, I/O), po czym w innym momencie sprawdza,czy wiadomość dotarła (można to porównać do wysłania e-maila). Trudniejsza dowykonania, ale lepsza wydajność.

fragmenty © Adam Liwo

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 84

MPI – jak wygląda wysłanie wiadomości?

• Skoncentrujemy się na C++ – amatorzy Fortrana mogą rzucić okiem do

"MPI Complete Reference".

• Symbole (funkcje, stałe, typy) wprowadzane przez MPI umieszczone są w

przestrzeni nazw MPI.

• Do komunikacji punktowej (wysyła jeden procesor i odbiera jeden procesor)

korzystamy z klasy Comm (reprezentującej komunikator), która daje metody

realizujące komunikację.

• Metoda wysyłająca wiadomość nazywa się Send i ma następujący prototyp:

void MPI::Comm::Send(const void* buf, int count,

const Datatype& datatype,

int dest, int tag);

• Zatem metodę Send wołamy na rzecz konkretnego komunikatora, np:

MPI::COMM_WORLD.Send(...);

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 85

MPI – jak wygląda wysłanie wiadomości?

void MPI::Comm::Send(const void* buf, int count,

const Datatype& datatype, int dest, int tag);

Kolejne parametry reprezentują:buf – wskaźnik do bufora nadawcy (adres w pamięci danych, które

chcemy wysłać),count – liczba elementów, która jest w buforze (na ogół przesyłamy

tablice, nie pojedyncze elementy – trzeba więc powiedzieć ile elementówmamy w tej tablicy),

datatype – stała symboliczna określająca typ danych, które przesyłamy.MPI definiuje kilka stałych, które odpowiadają podstawowym typomdanych, np. MPI::CHAR, MPI::INTEGER, MPI::DOUBLE, etc.

dest – numer procesora-odbiorcy,tag – znacznik wiadomości. Jest to liczba całkowita, za pomocą której

nadawca może dowolnie oznaczyć wiadomość. Pozwala ona naidentyfikację wiadomości po stronie odbiorcy. "Kod kreskowy".

W odróżnieniu od wersji fotranowskiej i od wersji C, wersja C++ nie zwracakodu błędu, w razie niepowodzenia zgłasza wyjątek MPI::Exception.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 86

MPI – jak wygląda odebranie wiadomości?

void MPI::Comm::Recv(void* buf, int count,

const Datatype& datatype, int source, int tag,

Status& status);

Podobnie, jak przy wysyłaniu wiadomości:buf – wskaźnik bufora odbiorcy, tu trafiają odebrane dane. Odbiorca musi

sam zadbać o przydzielenie bufora i o to, by pomieściły się w nimodebrane dane,

count – maksymalna liczba elementów, jaką jesteśmy gotowi przyjąć(rozmiar bufora w elementach),

datatype – stała symboliczna określająca typ danych, które odbieramy.Powinna zgadzać się z typem określonym po stronie nadawcy,

source – numer procesora-nadawcy. Gdy podamy konkretny, zaznaczamy,że interesuje nas wiadomość tylko od tego procesora. PodającANY_SOURCE godzimy się na przyjęcie wiadomości od dowolnegonadawcy.

tag – znacznik wiadomości, podobnie jak przy wysyłaniu. Jeśli podamykonkretny, zaznaczamy że interesuje nas wiadomość o tym konkretnymznaczniku. Podając ANY_TAG, godzimy się na przyjęcie wiadomości zdowolnym znacznikiem. 87

MPI – jak wygląda odebranie wiadomości?

void MPI::Comm::Recv(void* buf, int count,

const Datatype& datatype, int source, int tag,

Status& status);

status – zawiera dodatkowe informacje o odebranej wiadomości.Przy odbiorze przekazujemy tu obiekt klasy MPI::Status, którymetoda Recv wypełnia. Korzystając z wypełnionego obiektu będziemożna sprawdzić– ile faktycznie elementów otrzymaliśmy:

MPI::Status::Get_count(const Datatype&);

– od kogo przyszła wiadomość (jeśli godziliśmy się na wiadomości oddowolnego nadawcy):MPI::Status::Get_source();

– jaki był znacznik wiadomości (jeśli godziliśmy się na odbiór wiadomości zdowolnym znacznikiem).MPI::Status::Get_tag();

• Jeśli nie interesuje nas status wiadomości, MPI daje nam dodyspozycji przeciążoną wersję MPI::Comm::Recv pozbawionąostatniego z parametrów.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 88

MPI – kiedy kończy się Send()?

• Send() jest przykładem komunikacji blokującej, a zatem kończy się

dopiero po przesłaniu wiadomości.

• Ale co dokładnie to oznacza?

– czy po tym jak została pomyślnie wysłana?

– czy po tym jak dotarła siecią na drugą stronę?

– czy po tym jak odbiorca pomyślnie odebrał ją za pomocą Recv()?

• Żadna z powyższych!

• Send() kończy się, gdy nadawca może bezpiecznie zamazać

bufor nadawczy.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 89

MPI – kiedy kończy się Send()?

• Send() kończy się, gdy nadawca może bezpiecznie zamazać bufor

nadawczy.

• Nie mówi nam to jednak nic o tym, czy odbiorca dostał wiadomość, ani nawet

czy podróżuje ona już siecią. System MPI może dokonywać buforowania

wiadomości, tj. mógł skopiować naszą wiadomość w bezpieczne miejsce, zwrócić

kontrolę do nadawcy, mówiąc "bufor jest Twój, rób z nim co chcesz", a dopiero

za chwilę zajmie się faktycznym przesyłaniem wiadomości.

• Podobieństwo do wysyłania faksu – urządzenie zeskanowało kartkę, którą

przesyłamy, więc mamy pewność, że możemy z nią bezpiecznie zrobić co

chcemy (podrzeć), ale nie wiemy nic o losie przesyłanych danych – ani czy

doszły, ani nawet czy już rozpoczęło się wysyłanie.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 90

czas

MPI::Send na proc. A

|

MPI::Send kończy się

|

bufory MPI

MPI::Recv kończy się|

sieć bufory MPI

MPI – "paradoksy" przesyłania wiadomości

• Ponieważ oba procesory (nadawca i odbiorca) pracują niezależnie, to mamy do

czynienia z pewnymi komplikacjami, które powodują że niektóre zachowania

wydają się nieintuicyjne:

• MPI::Recv() po stronie odbiorcy może zakończyć się zanim MPI::Send()

zakończy się po stronie nadawcy.

• Nie oznacza to, że "wiadomość została odebrana przed nadaniem" tylko że

"wiadomość została odebrana, zanim stwierdzono zakończenie nadania". Proces

nadawcy mógł na przykład zostać przyblokowany czymś (np. wywłaszczony przez

SO na rzecz innego procesu) w momencie między nadaniem wiadomości a

powrotem z funkcji MPI::Send().

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 91

czas

początekMPI::Send na proc. A

|

MPI::Send prawie się zakończyło, ale proces został wywłaszczony

|

bufory MPI

MPI::Recv kończy się

|

sieć bufory MPI

|||||||

kontrola wraca do procesu na A,

MPI::Send kończy się

← proces na A jest nieaktywny aż dotąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąd →

MPI – "paradoksy" przesyłania wiadomości

• Globalnie nie wiemy nic o kolejności, w jakiej przychodzą

wiadomości. Może się zdarzyć tak, że procesor A wysyła

wiadomość o rozmiarze 10MB do procesora B na drugim końcu

sieci, w tym samym czasie procesor C wysyła wiadomość o

rozmiarze 40 bajtów do procesora D, który jest "tuż obok", a

pierwsza wiadomość dojdzie szybciej. Nie ma zatem mowy o

jakimkolwiek determinizmie.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 92

A

B

C

D

MPI – gwarancja kolejności

• Gwarancję "kolejności" mamy tylko dla dwóch konkretnych

procesorów, tj. jeśli procesor A wyśle do B wiadomości w1, w2, w3,

…, wn, to do B dojdą one w tej samej kolejności. Normalnie

wybieramy wiadomości u odbiorcy dzięki możliwości zadania

źródła i znacznika wiadomości, której odebranie nas interesuje, ale

gdy korzystamy z ANY_SOURCE i ANY_TAG, to dostajemy wiadomości

"jak leci", więc dobrze jest wiedzieć że są w tej samej kolejności, w

której wysłał je nadawca. Jeśli wysyła dwóch lub więcej nadawców

– nie mamy żadnych gwarancji co do tego, jak wiadomości od nich

będą się przeplatać.

• Krótko mówiąc, wiadomości nie wyprzedzają się, ale tylko

wiadomości konkretnego nadawcy skierowane do

konkretnego odbiorcy.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 93

MPI – inicjalizacja i deinicjalizacja

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 94

Zanim skorzystamy z jakiejkolwiek funkcji MPI (z wyjątkiem jednej),

musimy zainicjować środowisko MPI.

Inicjalizacji dokonujemy za pomocą MPI::Init().

void MPI::Init(int& argc, char**& argv);

Przekazujemy do MPI::Init te same parametry, które otrzymuje

main(). Przesyłane są one przez referencję, dzięki czemu

MPI::Init może je zmodyfikować, jeśli to konieczne.

Nie oszukujemy! Przekazujemy dokładnie te same parametry, które

otrzymuje main() – w przeciwnym wypadku mamy zachowanie

niezdefiniowane.

• Przed wywołaniem MPI::Init() nie wolno wykonywać żadnych

operacji we/wy (czytanie i pisanie do plików, wypisywanie komunikatówna konsolę, czytanie znaków z klawiatury, etc.). Nie próbuj tego robić –może udawać, że działa a stać się powodem trudnych do znalezieniabłędów.

• W C sprawa jest prosta – wystarczy wywołać MPI::Init() na samympoczątku funkcji main().

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 95

MPI – inicjalizacja i deinicjalizacja [2]

• W C++ sprawa jest trochę trudniejsza – trzeba pamiętać, że obiektyglobalne będą, a statyczne mogą być zainicjowane przed main().

Musimy mieć pewność, że konstruktory tych obiektów nie próbująwykonywać operacji we/wy, nawet tak trywialnych jak wypisywaniewiadomości diagnostycznych na konsolę.

// prosimy się o kłopoty

class kłopot {

public:

kłopot() {

cerr << "To ja, konstruktor\n";

}

};

kłopot tutaj; // konstruktor przed MPI::Init

int main(int argc, char** argv) {

MPI::Init(argc,argv);

}

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 96

MPI – inicjalizacja i deinicjalizacja [2]

MPI – inicjalizacja i deinicjalizacja [3]• Aby posprzątać po środowisku MPI gdy program zakończył się,

należy wywołać MPI::Finalize().

• Po MPI::Finalize() nie wolno wołać żadnych funkcji MPI, nawetMPI::Init().

• Należy wywołać MPI::Finalize() na każdym procesorze (tzw.operacja kolektywna (zbiorowa, grupowa)) – to niezwykle istotne!

• Po wejściu do funkcji MPI::Finalize(), każdy procesor czeka napozostałe, opuszczając tę funkcję dopiero gdy wszystkie procesorydotrą do tego miejsca programu. Zachowanie takie nazywa siębarierą (więcej o barierze przy operacjach kolektywnych).

• Musimy zagwarantować, że w chwili wywołania MPI::Finalize()

wszystkie wysłane uprzednio wiadomości zostały odebrane.

• Z powyższych powodów funkcja ta nie nadaje się do awaryjnegokończenia programu. W razie wystąpienia błędu krytycznego możebyć trudno zapewnić odebranie wszystkich wiadomości, a tymbardziej wywołać MPI::Finalize() na wszystkich procesorach.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 97

MPI – awaryjne kończenie programu (abort)

• W przypadkach nagłych możemy zakończyć program korzystając zMPI::Comm::Abort().

MPI::Comm::Abort(int error);

• Funkcję tę wołamy na rzecz komunikatora, którego procesychcemy zakończyć. Obecnie MPI gwarantuje poprawne działanietylko dla komunikatora MPI::COMM_WORLD.

• error – kod błędu, który chcemy zwrócić do systemu op..

• Zatem np. MPI::COMM_WORLD.Abort(42);

• Funkcja ta natychmiast zamyka środowisko MPI, po czym kończy procesy komunikatora za pomocą abort().

• Nie dostajemy szansy na jakiekolwiek posprzątanie po programie.Nie zostaną wywołane destruktory, zasoby nie zostaną zwolnione,pliki nie zostaną zamknięte, etc... Musimy liczyć na to, że posprzątaSO. Ekran zostanie zalany serią komunikatów o błędach.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 98

MPI – uwagi nt. we/wy

• Niedozwolone przed MPI::Init().

• Standard MPI nie precyzuje powiązań strumieni standardowych

(stdout, stdin, stderr) – nie wiadomo dokąd prowadzą. W praktyce

stdout i stderr są na ogół przekierowane do plików wyspecyfiko-

wanych przy uruchamianiu programu.

• W praktyce nie ma dostępu do klawiatury (większość węzłów

obliczeniowych nawet nią nie dysponuje). Dane wejściowe

będziemy pobierać z plików. Nie będzie interakcji z użytkownikiem

w rodzaju zapytań "czy na pewno T/N?".

• Nie ma gwarancji, że każdy węzeł może wykonywać operacje

we/wy. Może się zdarzyć, że tylko niektóre węzły (albo jeden, albo

żaden) dysponują we/wy. Można wybadać sytuację sprawdzając

atrybut MPI::IO. Na laboratorium będziemy zakładać, że wszystkie

procesory mają dostęp do we/wy.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 99

MPI – uwagi nt. we/wy [2]

• Nie można oczekiwać jakiegokolwiek uporządkowania wiadomości

wysyłanych na wyjście standardowe. Komunikaty wypisywane przez

procesory będą się przeplatać. Co więcej tekst wypisany przez jeden

procesor w czasie t2 może dotrzeć na wyjście przed tekstem

wypisanym przez inny procesor w czasie t1, nawet gdy t2>t1.

Buforowanie wyjścia przez SO pogarsza jeszcze sprawę.

• Próba pisania do tego samego pliku z więcej niż jednego procesora

nie powiedzie się (uda się to wykonać, ale nie ma gwarancji co do

tego, co znajdzie się w pliku). Należy albo pisać do oddzielnych

plików, pisać tylko na jednym procesorze albo (najlepiej) użyć

równoległego we/wy, którym dysponuje MPI-2.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 100

MPI – jak sprawdzić ile jest procesorów

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 101

Zdobycie tej informacji jest na ogół pierwszą czynnością

wykonywaną po MPI::Init(). Służy do tego funkcja

MPI::Comm::Get_size().

int MPI::Comm::Get_size();

Funkcja zwraca liczbę procesorów w komunikatorze. Jeśli wywołamy

ją na rzecz MPI::COMM_WORLD dostaniemy całkowitą liczbę

procesorów, na których pracuje program.

Analogicznie, przy użyciu MPI::Comm::Get_rank() możemy

sprawdzić numer bieżącego procesora.

int MPI::Comm::Get_rank();

Zwrócony numer należy do przedziału <0, N-1>, gdzie N jest liczbą

procesorów zwróconą przez MPI::Comm::Get_size().

MPI – kompletny program przykładowy

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 102

// czyta 10 liczb z pliku wejscie.txt na procesorze 0, przesyla liczby do

// procesora 1, zapisuje do pliku wyjscie.txt na procesorze 1.

#include <mpi.h>

#include <iostream>

#include <fstream>

using namespace std;

int main(int argc, char** argv) {

MPI::Init(argc, argv);

cout << "Po inicjalizacji!" << endl;

int size=MPI::COMM_WORLD.Get_size();

int rank=MPI::COMM_WORLD.Get_rank();

cout << "Start na processorze " << rank << " z " << size << endl;

int tab[10];

if(rank==0) {

ifstream we("wejscie.txt");

for(int i=0; i<10; ++i) we >> tab[i];

cout << "Procesor 0 – wysyłam" << endl;

MPI::COMM_WORLD.Send(tab,10,MPI::INTEGER,1,0);

}

if(rank==1) {

cout << "Procesor 1 – odbieram" << endl;

MPI::Status status;

MPI::COMM_WORLD.Recv(tab,10,MPI::INTEGER,0,0,status);

ofstream wy("wyjscie.txt");

for(int i=0; i<10; ++i) wy << tab[i];

}

if(rank>1) cout << "Procesor " << rank << " – nudzę się" << endl;

cout << "Kończę pracę na procesorze " << rank << endl;

MPI::Finalize();

}

MPI – przykładowe wyjście programu

przykładowego

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 103

LAM 7.1.1/MPI 2 C++/ROMIO – Indiana University

uruchamiam na: h98, h110, h125, h126.

Po inicjalizacji.

Po inicjalizacji.

Start na procesorze 1 z 4

Po inicjalizacji

Start na proStart na procesorze 0 z 4

cesorze 2 z 4

Procesor 1 - odbieram

Procesor 0 – wysylam

Po inicjalizacji

Start na procesorze 3 z 4

Procesor 3 – nuProcesor 2 – nudze się

Kończę pracę na procesorze 2

dze sięKończę pracę na procesorze 3

Kończę pracę na procesorze 1

Kończę pracę na procesorze 0

Widać, że wyjście może się przeplatać

Ten sam kod wykonuje się na każdym z 4 procesorów – jeśli czegoś

nie otoczymy warunkiem typu if(mój_numer_to…), to wykona się to

na każdym z procesorów.

MPI – kompilacja i wiązanie

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 104

#include <mpi.h>

Przy kompilacji trzeba podać ścieżkę do plików nagłówkowych, żeby kompilator

wiedział gdzie znaleźć "mpi.h". Przykładowo

g++ -I/apl/mpi/lam/ia64/include –c test.cpp

Przy linkowaniu trzeba podać ścieżkę, gdzie kompilator może znaleźć plik

biblioteki mpi (libmpi.a) oraz włączyć bibliotekę mpi (czasem też inne, np. pthread).

Przykładowo

g++ -L/apl/mpi/lam/ia64/lib test.o \

–lmpi –lpthread –llam –o test

Czasem środowisko daje nam skrypty albo programy, które dodają wszystko za nas.

Kompilujemy i linkujemy wtedy posługując się nazwą skryptu/programu zamiast

nazwy kompilatora. Przykładowo (skrypt nazywa się mpic++)

mpic++ test.cpp -o test

MPI – uruchamianie

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 105

Trzeba uruchomić kopię programu na każdym z procesorów, ponadto każdy musi

wiedzieć z kim ma się komunikować (numery/nazwy pozostałych węzłów).

Robi to za nas "skrypt do uruchamiania" dostarczany przez środowisko, często

nazywa się on mpirun.

Przykładowa składnia (uruchamia program test na sześciu procesorach,

przekierowując standardowe wyjście do pliku "stdout.txt" a standardowe wyjście

błędów do "stderr.txt"):

mpirun –np 6 –o stdout.txt –e stderr.txt ./test

MPI – próbujemy wymiany wiadomości// Spróbujmy wymienić zawartość dwóch tablic między dwoma procesorami. Zakładamy,

// że tablice są już przydzielone, wypełnione, mój_numer zawiera numer procesora (0 lub 1),

// a do przesłania jest n elementów typu int.

int jego_numer=1-mój_numer;

if(mój_numer==0) {

// wyślij

MPI::COMM_WORLD.Send(moje_dane, n, MPI::INTEGER, jego_numer, 1234);

// odbierz

MPI::Status status;

MPI::COMM_WORLD.Recv(jego_dane, n, MPI::INTEGER, jego_numer, 1234, status);

// tu możemy sprawdzić status

}

if(mój_numer==1) {

// wyślij

MPI::COMM_WORLD.Send(moje_dane, n, MPI::INTEGER,

jego_numer, 1234);

// odbierz

MPI::Status status;

MPI::COMM_WORLD.Recv(jego_dane, n, MPI::INTEGER,

jego_numer, 1234, status);

// tu możemy sprawdzić status

}

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 106

NIE zadziała tak, jak byśmy tego oczekiwali, z uwagi na buforowanie!

Dlaczego nie zadziała? Analogia.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 107

Rozważmy dwa oddziały firmy kurierskiej, które wymieniająprzesyłki korzystając z magazynu. Kurierzy z każdego oddziałówoznaczają przesyłki numerem odbiorcy i nadawcy, po czymzostawiają je w magazynie, aby odebrali je kurierzy z drugiegooddziału. Następnie zabierają przesyłki pozostawione dla nich.

Oba oddziały pracują zgodnie z dwiema wytycznymi:

Wytyczna #1 dla pracowników

Jeśli magazyn jest pełny, należy czekać. Bez wątpienia pracownicydrugiego oddziału pojawią się za moment, zabiorą trochę paczeki zrobi się miejsce.

Wytyczna #2 dla pracowników

Jeśli nie widać przesyłek, które możnaby zabrać, należy czekać.Bez wątpienia pracownicy drugiego oddziału pojawią się zamoment i przywiozą paczki.

Dlaczego nie zadziała? Analogia.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 108

Dokładnie takie samo zachowanie mamy w naszym

(niedziałającym) programie. Oddziały są reprezentowane przez

procesory, przesyłki to dane, a magazyn to wewnętrzne bufory

MPI.if(mój_numer==0) {

Send(…);

Recv(…);

}

if(mój_numer==1) {

Send(…);

Recv(…);

}

Algorytm pracowników firmy wygląda tak:if(jestem z oddziału A) {

zostaw przesyłki dla B

zabierz przesyłki adresowane do nas

}

if(jestem z oddziału B) {

zostaw przesyłki dla A

zabierz przesyłki adresowane do nas

}

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 109

Sprawdźmy, czy działa. Załóżmy, że magazyn mieści 12 przesyłek. Zobaczmy,

czy uda się wymienić 4.

Inicjalnie magazyn jest pusty.

O 1100 przybywają kurierzy z oddziału A i zostawiają 4 przesyłki dla B.

Nie widzą przesyłek adresowanych do nich, więc czekają, zgodnie z wytyczną

#2.

Send

→ Receive

→ Send

Receive

Dlaczego nie zadziała? Analogia.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 110

Kurierzy z oddziału B przybywają o 1115 (korki na drodze) i zostawiają 4

przesyłki dla A.

Następnie biorą przesyłki adresowane do nich i odjeżdżają. W tym samym

czasie kurierzy z A też biorą przesyłki adresowane do siebie i wyjeżdżają.

Wydaje się działać.

w końcu!

→ Send

Receive

Send

→ Receive

Send

→ ReceiveSend

→ Receive

Dlaczego nie zadziała? Analogia.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 111

Z 4 paczkami poszło. Zobaczmy, co się stanie, kiedy trzeba będzie wymienić 8paczek – teraz 16 paczek na raz nie zmieści się w magazynie.

Inicjalnie magazyn jest pusty.

O 1100 przybywają kurierzy z oddziału A i zostawiają 8 paczek dla B.

Nie widzą przesyłek adresowanych do siebie, wobec czego czekają, zgodnie zwytyczną #2.

→ Send

Receive

Send

→ Receive

Dlaczego nie zadziała? Analogia.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 112

Kurierzy z oddziału B znów się spóźniają i przyjeżdżają o 1130, przywożąc 8

paczek dla A. Mogą jednak zmieścić tylko 4 paczki nim magazyn się zapełnia.

Zgodnie z wytyczną #1 muszą czekać, aż znajdzie się miejsce.

Na szczęście pracownicy oddziału A skończyli zostawiać swoje paczki, więc

mogą zacząć odbierać i zabierają 4 paczki przyniesione właśnie przez oddział

B.

w końcu!

Send

→ Receive

→ Send

Receive

→ Send

ReceiveSend

→ Receive

Dlaczego nie zadziała? Analogia.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 113

Pojawiło się miejsce w magazynie, więc pracownicy z B przynoszą ostatnie 4

paczki i przechodzą do odbierania paczek adresowanych do siebie.

Kurierzy z A biorą ostatnie 4 paczki i po sprawie.

Wydaje się działać, nawet jak komplet paczek nie mieści się w magazynie.

Czyżby?

Send

→ Receive

Send

→ Receive

Send

→ Receive

Dlaczego nie zadziała? Analogia.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 114

Sprawdźmy, co się stanie przy wymianie 20 paczek.

Inicjalnie magazyn jest pusty.

O 1100 przyjeżdżają kurierzy z A i zostawiają pierwsze 12 paczek dla B, po

czym magazyn zapełnia się. Ponieważ nie mogą zostawić więcej paczek,

zgodnie z wytyczną #1 kurierzy z A czekają.

→ Send

Receive

Dlaczego nie zadziała? Analogia.

115

Kurierzy z oddziału B znowu tkwili w korku i przyjeżdżają o 1130, przywożąc

20 paczek adresowanych do A. Nie mogą jednak zostawić żadnej przesyłki,

ponieważ nie miejsca w magazynie. Zgodnie z wytyczną #1 muszą czekać, aż

pojawi się miejsce.

Oba oddziały czekają. Żaden z nich nie może wyjąć paczek z magazynu,

ponieważ wpierw muszą skończyć wkładanie paczek.

Sytuacja, w której procesy zablokują się w nieskończoność:

ZAKLESZCZENIE (DEADLOCK)

w końcu!

→ Send

Receive

→ Send

Receive

→ Send

Receive

→ Send

Receive

Dlaczego nie zadziała? Analogia.

Czyli ten program zakleszcza się...• Zatem nasz program będzie się zakleszczał, gdy tylko rozmiar przesyłanej

wiadomości będzie większy od rozmiaru wewnętrznych buforów MPI (a te nie

wiemy jak duże są).

• Przyczyną zakleszczenia jest fakt, że Send() nie może się zakończyć zanim nie

zostanie przetworzona cała wiadomość. Pełna wiadomość nie mieści się w

buforze i obydwa procesory blokują się na Send() zanim mogą zainicjować

odbiór.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 116

if(mój_numer==0) {

// wyślij

MPI::COMM_WORLD.Send(moje_dane, n, MPI::INTEGER,

jego_numer, 1234); // * blokuje

// odbierz

MPI::COMM_WORLD.Recv(jego_dane, n, MPI::INTEGER, jego_numer, 1234, status);

}

if(mój_numer==1) {

// wyślij

MPI::COMM_WORLD.Send(moje_dane, n, MPI::INTEGER,

jego_numer, 1234); // * blokuje

// odbierz

MPI::COMM_WORLD.Recv(jego_dane, n, MPI::INTEGER, jego_numer, 1234, status);

}

Ten program też się zakleszcza• Może należy najpierw odbierać, potem wysyłać?

if(mój_numer==0) {

Recv(…);

Send(…);

}

if(mój_numer==1) {

Recv(…);

Send(…);

}

• To też się zakleszcza i to niezależnie od rozmiaru buforów MPI, bo oba

procesory próbują odbierać wiadomości, podczas gdy żaden nic nie nadał.

Pamiętamy, że operacjia odbioru nie może się zakończyć zanim nie zostanie

odebrana cała wiadomość.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 117

→ Receive

Send→ Receive

Send

Rozwiązanie:

ten program NIE zakleszcza się

• Problem można rozwiązać wykonując najpierw nadanie, a potem

odbiór na pierwszym proce-sorze, a najpierw odbiór, a potem

nadanie na drugim procesorze:

if(mój_numer==0) {

Send(…);

Recv(…);

}

if(mój_numer==1) {

Recv(…);

Send(…);

}

• Przy więcej niż dwóch procesorach można robić odbiór-nadanie na

nieparzystych, a nadanie-odbiór na parzystych.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 118

Dlaczego teraz działa: analogia

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 119

Zobaczmy, co się dzieje gdy wymieniamy 20 paczek. Zgodnie z nowymi

zasadami, kurierzy z B najpierw odbierają przesyłki, a dopiero potem

zostawiają (send) przesyłki, które przywieźli.

Inicjalnie magazyn jest pusty.

O 1100 przybywają kurierzy z oddziału A i zostawiają pierwsze 12 paczek dla

B, po czym magazyn zapełnia się. Kurierzy nie są w stanie zostawić więcej

paczek, więc zgodnie z zasadą #1 oczekują.

→ Send

Receive

Dlaczego teraz działa: analogia

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 120

Kurierzy z oddziału B znowu się spóźniają i przyjeżdżają o 1130, przywożąc 20

paczek dla A. Tym razem jednak zaczynają pracę od odbioru paczek

przywiezionych przez oddział A.

Jak tylko oddział B zaczyna wynosić paczki, A może kontynuować zostawianie

paczek i ostatecznie umieszcza wszystkie 20 przesyłek w magazynie. Oddział

A skończył nadawanie, może więc przejść do odbioru. W magazynie nie ma

jednak żadnych paczek dla A, więc pracownicy A czekają.

nareszcie!wezmę to!

jeszcze tylko

kilka!

→ Send

Receive

→ Receive

Send

→ Receive

Send

Send

→ Receive

Dlaczego teraz działa: analogia

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 121

Po tym jak kurierzy z oddziału B zabiorą wszystkie paczki, zaczynają

zostawiać paczki dla oddziału A. Jak tylko zostawią 12 paczek, muszą

zaczekać, ponieważ magazyn zapełnił się.

Na szczęście pracownicy A są gotowi do odbioru – jak tylko zaczynają

wynosić paczki z magazynu, pracownicy B mogą kontynuować wnoszenie

paczek i ostatecznie umieszczają wszystkie 20 w magazynie, po czym natych-

miast odjeżdżają, a pracownicy A dokańczają zabieranie paczek.

Metoda ta działa niezależnie od rozmiaru wiadomości i rozmiaru

buforów wewnętrznych MPI.

Send

→ Receive

Receive

→ Send

Send

→ Receive

Po co unikać zakleszczenia?

• Nie wiemy jak pojemne są bufory wewnętrzne MPI (magazyn),

dlatego bezwzględnie musimy dbać o to, żeby zakleszczenie nigdy

nie było możliwe!

• Program, który się zakleszcza wydaje się działać dobrze, dopóki

rozmiar danych jest mały albo bufor duży.

• Ale bufor może być dowolnie mały…

• Czyli dla takiego programu zawsze można znaleźć taki system, na

którym zakleszczenie jednak nastąpi!

Program, w którym możliwe

jest zakleszczenie jest więc

programem niepoprawnym!… i na nic tłumaczenie "przecież widać że działa".

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 122

Procedura odbiór-i-nadanie to inny sposób zapobieżenia zakleszczeniu

void MPI::Comm::Sendrecv(const void *sendbuf,

int sendcount, const Datatype& sendtype, int dest

int sendtag, void *recvbuf, int recvcount,

const Datatype& recvtype, int source, int recvtag,

Status& *status)

• Pozwala na odebranie i odbiór za pomocą jednej operacji.

• Automagicznie zapewnia, że nie wystąpi zakleszczenie!

• Bufory: nadawczy i odbiorczy nie mogą się nakładać.

• Można wysyłać dane jednego typu, a odbierać innego (jeśli się

wie co się robi), byleby długość wiadomości się zgadzała.

if(mój_numer==0 || mój_numer==1) {

Sendrecv(…);

}

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 123

MPI – dygresja: proces pusty

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 124

• Czasami żeby nie gmatwać kodu przydaje się możliwość

wysłania wiadomości do nieistniejącego odbiorcy

(dlaczego?). Możemy wtedy skorzystać z identyfikatora

MPI::PROC_NULL, posługując się nim jako numerem

procesu odbiorcy. Wiadomość taka "wyśle się

błyskawicznie" i zostanie zignorowana.

• Podobnie możemy odebrać wiadomość od

nieistniejącego procesu – odbiór od MPI::PROC_NULL

zakończy się błyskawicznie, pomyślnie, nie modyfikując

bufora. Status będzie wówczas zawierał

source == MPI::PROC_NULL, count == 0,

tag == MPI::ANY_TAG.

MPI – komunikacja zbiorowa• Czasem (często) będziemy potrzebować czegoś więcej niż możliwości wysłania

wiadomości z procesora A do B. MPI daje nam do dyspozycji komunikacjęzbiorową (grupową, collective communication).

• Najważniejsze operacje zbiorowe:

– bariera (barrier) – synchronizuje procesy,

– rozesłanie (broadcast) – jeden procesor rozsyła jedną daną do wszystkich,

– zebranie (gather) – jeden procesor odbiera dane od wszystkich

– rozproszenie (scatter) – jeden procesor rozsyła dane do wszystkich

– redukcja (reduce) – jeden procesor odbiera przetworzone dane odwszystkich.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 125

MPI – komunikacja zbiorowa: bariera

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 126

• Czasem będziemy chcieli zsynchronizować jakieś działania. Za pomocą funkcji

Barrier() możemy zmusić każdy procesor komunikatora do poczekania na

wszystkie pozostałe (w konsekwencji wszyscy czekają na ostatniego).

void MPI::Intracomm::Barrier() const;

• Wywołujemy Barrier() na wszystkich procesorach w komunikatorze.

Funkcja ta blokuje wywołującego do czasu, aż wszystkie pozostałe procesory

też ją wywołają. W momencie, gdy ostatni procesor dotrze do Barrier(),

funkcja ta kończy się na wszystkich procesorach.

MPI – komunikacja zbiorowa

• Broadcast (rozesłanie) – jeden procesor (root) rozsyła dane do

pozostałych procesorów.

• Przykład użycia: Jeden procesor (root) czyta plik konfiguracyjny,

po czym za pomocą broadcast przekazuje pozostałym co

odczytał.

• Inny przykład: procesory muszą wymienić N wiadomości, ale

liczba N jest znana tylko jednemu z nich (root). Zanim zaczną

wymieniać wiadomości, root rozsyła do wszystkich wartość N.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 127

image: © APAC

MPI – komunikacja zbiorowa:

broadcast• Jeden procesor (root) rozsyła daną do wszystkich, łącznie ze sobą.

• Podobieństwo składniowe do zwykłego wysłania wiadomości, ale nie ma tag.

Druga różnica: tym razem liczba odebranych elementów musi być taka sama,

jak liczba wysłanych (nie można odebrać "z zapasem" jak w komunikacji

punktowej).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 128

void MPI::Intracomm::Bcast(void* buffer, int count,

const Datatype& datatype, int root) const;

buffer – bufor danych. Na procesorze root z niego pochodzą dane, na

pozostałych procesorach tu trafiają dane,

count – liczba rozsyłanych/odbieranych elementów,

datatype – stała symboliczna reprezentująca typ danych,

root – numer procesora root,

MPI – komunikacja zbiorowa:

broadcast• Bcast() trzeba wywołać na wszystkich procesorach, które mają

partycypować w komunikacji. Łatwo o tym zapomnieć i próbowaćnp. wywołać to tylko na procesorze root, dlatego że to on rozsyła.

• U wszystkich root musi mieć tę samą wartość.

• Typ danych i liczba elementów też muszą się zgadzać.(nie do końca prawda – musi się zgadzać tylko długość wiadomości)

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 129

MPI – komunikacja zbiorowa:

broadcast• Nie można odbierać wiadomości rozsyłanych za pomocą broadcast

przez punktowe receive (Recv()) – wszyscy, którzy chcąuczestniczyć w operacji rozesłania danych, muszą wykonaćbroadcast.

• Kiedy kończy się funkcja Bcast()? – kiedy dany procesorzakończył swój udział w operacji rozesłania (nadawca – możezniszczyć bufor, odbiorca – może bezpiecznie wyjąć odebranedane z bufora). Zakończenie się Bcast() nie daje żadnejinformacji o tym, co dzieje się na innych procesorach (np. może sięskończyć wcześniej u odbiorcy niż u nadawcy).

• Uwaga: Operacja rozesłania nie gwarantuje synchronizacji

między procesorami. Proces może być wstrzymany do momentu,

aż wszystkie wykonają operację broadcast, ale nie musi. Trzebazagwarantować poprawne działanie w obydwu sytuacjach.

• Operacje komunikacji zbiorowej mogą się wyprzedzać!

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 130

MPI – komunikacja zbiorowa:

pułapki// ten program zakleszczy się na niektórych systemach

if(mój_numer==0) {

Broadcast(nadawca==0);

Send();

}

if(mój_numer==1) {

Recv();

Broadcast(nadawca==0);

}

• Taki program będzie działał poprawnie albo nie, zależnie od tego

czy MPI na tym systemie synchronizuje operacje broadcast (a nie

mamy gwarancji ani synchronizowania, ani niesynchronizowania).

• W razie synchronizowania procesor 0 zatrzyma się na operacji

broadcast przez co nie dotrze do send. Drugi nigdy nie dotrze do

broadcast, bo recv nigdy się nie zakończy (bo send się nie wykonało).

Program zakleszczy się. Jeśli MPI nie synchronizuje operacji

broadcast, program będzie wydawał się działać poprawnie.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 131

MPI – komunikacja zbiorowa:

pułapki// ten program nie zakleszcza się, ale jest niedeterministyczny

if(mój_numer==0) {

Broadcast(nadawca==0);

Send(odbiorca==1);

}

if(mój_numer==1) {

Recv(nadawca==any);

Broadcast(nadawca==0);

Recv(nadawca==any);

}

if(mój_numer==2) {

Send(odbiorca==1);

Broadcast(nadawca==0);

}

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 132

Wyścigi (race condition/hazard). Sytuacja, w której

program staje się niedeterministyczny przez to,

że nie wiadomo który odbiór zostanie

sparowany z którym nadaniem. Wysłane

wiadomości (send-0 i send-2) "ścigają się" do

odbiorcy.

MPI – komunikacja zbiorowa:

scatter• Jeden procesor (root) rozsyła dane do wszystkich, łącznie ze sobą.

• Każdy odbiorca otrzymuje inną część danych.

• Podobne ograniczenia jak dla broadcast (nie ma tag, zgodność

długości danych, ten sam nadawca u wszystkich).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 133

MPI – komunikacja zbiorowa:

scatter

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 134

void MPI::Intracomm::Scatter(

const void* sendbuf, int sendcount, const Datatype& sendtype,

void* recvbuf, int recvcount, const Datatype& recvtype,

int root);

sendbuf, sendcount, sendtype – bufor danych, liczba elementów i typ

danych – wszystko po stronie nadawcy. Na pozostałych procesorach –

ignorowane.

recvbuf, recvcount, recvtype – bufor danych, liczba elementów i typ

danych – u procesorów odbierających. Uwaga – root (nadawca) też odbiera.

root – numer procesora root, ten sam u wszystkich.

Typy danych i liczba elementów mogą się różnić między nadawcą i odbiorcą, ale

tak, żeby długość wiadomości była taka sama. Można np. nadać 200 liczb

MPI::LONG a odebrać 400 liczb MPI::INTEGER, jeśli na naszym systemiesizeof(long int) = 2*sizeof(int).

MPI – komunikacja zbiorowa:

gather• Przeciwieństwo operacji scatter.

• Jeden procesor (root) odbiera dane do wszystkich, łącznie ze sobą.

• Każdy nadawca wysyła porcję danych, a root składa je wszystkie do

jednego bufora, jedna za drugą.

• Podobne ograniczenia jak dla broadcast i scatter (nie ma tag,

zgodność długości danych, ten sam odbiorca u wszystkich).

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 135

MPI – komunikacja zbiorowa:

gather

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 136

void MPI::Intracomm::Gather(

const void* sendbuf, int sendcount, const Datatype& sendtype,

void* recvbuf, int recvcount, const Datatype& recvtype,

int root);

sendbuf, sendcount, sendtype – bufor danych, liczba elementów i typ

danych – wszystko po stronie nadawców. Uwaga – root (odbiorca) też nadaje.

recvbuf, recvcount, recvtype – bufor danych, liczba elementów (w

jednej porcji, np. na rysunku poniżej 100, nie 300) i typ danych – u procesora

odbierającego. Na procesorach wysyłających – ignorowane.

root – numer procesora root, ten sam u wszystkich.

Typy danych i liczba elementów spełniają te same założenia, co przy scatter.

MPI – komunikacja zbiorowa:

reduce• Została nam jeszcze jedna istotna operacja zbiorowa – redukcja.

• Poznamy dwa warianty – reduce i allreduce (są jeszcze inne, podobne:reduce-scatter i scan – nie będziemy się nimi zajmować).

• reduce: Dane sumowane są po wszystkich procesorach, wynik trafiado procesora root.

• allreduce: Jak wyżej, ale wynik trafia do każdego z procesorów.• Tak naprawdę niekoniecznie musi chodzić o sumowanie – możemy

wykonać na danych też inne operacje (iloczyn, sumę logiczną,wyszukanie maksimum, etc.)

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 137

MPI – komunikacja zbiorowa: reduce

• Reduce (redukcja) – dane ze wszystkich procesorów zostają

zebrane na jednym.

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 138

procesor #0 procesor #2procesor #1 procesor #3

pole=30.24 pole=23.16 pole=34.52 pole=32.38

Reduce()

(+)

procesor #0

całkowite pole=120.3

MPI – komunikacja zbiorowa:

reduce• Przykłady (z jedną daną):

– Obliczamy całkę oznaczoną, każdy procesor dostał do obliczeń jeden

podprzedział. Na końcu chcemy wysumować cząstkowe odpowiedzi

każdego procesora, żeby dostać wynik. (op: +)

– Obliczamy prawdopodobieństwo jednoczesnego wystąpienia N zdarzeń

niezależnych. Każdy z procesorów zajmuje się n zdarzeniami z N. Na

końcu chcemy poznać całkowite prawdopodobieństwo, które jest

iloczynem prawdopodobieństw cząstkowych (op: *)

– Poszukujemy największej pośród N wartości. Każdy z k procesorów

przegląda (N/k) wartości z N i znajduje największą z nich. Na końcu

mamy k kandydatów do największej, spośród których trzeba wybrać

"naprawdę największą". (op: max)

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 139

MPI – komunikacja zbiorowa:

reduce

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 140

void MPI::Intracomm::Reduce(const void* sendbuf, void* recvbuf,

int count, const Datatype& datatype, const Op& op, int root)

sendbuf – bufor nadawczy danych, na każdym z procesorów biorących udziałw operacji.

recvbuf – bufor odbiorczy danych – istotny tylko na procesorze odbiorczym(root), na pozostałych ignorowany.

count – liczba rozsyłanych/odbieranych elementów. datatype – typ danych MPI. op – operacja, którą wykonujemy na danych. Dostępne są następujące operacje,

oraz operacje zdefiniowane przez użytkownika (wyższa szkoła jazdy)MPI::SUM suma +

MPI::PROD iloczyn *

MPI::MIN minimum std::min

MPI::MAX maksimum std::max

MPI::LOR suma logiczna ||

MPI::LAND il. logiczny &&

MPI::LXOR albo logiczne nie ma

MPI::BOR suma bitowa |

MPI::BAND iloczyn bitowy &

MPI::BXOR albo bitowe ^

MPI::MINLOC minimum wraz z

położeniem

nie ma

MPI::MAXLOC maksimum wraz z

położeniem

nie ma

MPI – komunikacja zbiorowa:

reduce

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 141

Przykład – sumowanie jednej zmiennej double po procesorach:

// uruchamiamy na 5 procesorach, zakładamy że

// mój_numer jest numerem procesora (0..4)

double send = 10*mój_numer;

double recv;

MPI::COMM_WORLD.Reduce(&send,&recv,1,MPI::DOUBLE,MPI::SUM,0);

cout << "Na procesorze " << mój_numer

<< " recv wynosi " << recv << endl;

Wyprodukuje coś w rodzajuNa procesorze 2 recv wynosi -3.1857e+66

Na procesorze 3 recv wynosi 2.4913e-293

Na procesorze 0 recv wynosi 100

Na procesorze 1 recv wynosi 1.3179e+11

Na procesorze 4 recv wynosi +inf

Na procesorze root w buforze odbiorczym recv dostaliśmy sumę zawartości

buforów send wszystkich procesorów (0+10+20+30+40=100).

Na pozostałych procesorach zawartość bufora odbiorczego jest niezdefiniowana

(bo nic nie odbierają), dostaliśmy więc śmieci.

MPI – komunikacja zbiorowa:

reduce – uwagi

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 142

Podobnie jak przy poprzednich operacjach komunikacji zbiorowej

w komunikacji muszą wziąć udział wszystkie procesory

komunikatora, tj. funkcję Reduce() trzeba wywołać na każdym z

procesorów w komunikatorze, inaczej zakleszczymy program.

Operacja redukcji czyni założenie, że operator #, którym

działamy na dane jest łączny, tj.

A # (B # C) == (A # B) # C

Na ogół jest to prawda, ale:

• przy operacjach definiowanych przez użytkownika –

niekoniecznie, wtedy będą kłopoty.

Dodawanie i mnożenie zmiennoprzecinkowe

nie są łączne.

Jak to dodawanie nie jest łączne?

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 143

Dodawanie jest łączne, to dodawanie liczb zmiennoprzecin-

kowych nie jest. #include <iostream>

#include <iomanip>

using namespace std;

int main() {

float a=4,b=4;

float c=100000000;

cout << setprecision(20) << (a+b)+c << endl

<< a+(b+c) << endl;

}

// wyprodukuje

// 100000008

// 100000000

Innymi słowy – wynik dodawania liczb zmiennoprzecinkowych zależy od

kolejności, w której są dodawane. a Reduce() może dowolnie wybrać kolejność

dodawania żeby mieć pole do optymalizacji. Problem pojawia się tylko, gdy liczby

mocno różnią się rzędami wielkości.

Przyczyna: skończona precyzja. Przy pracy z liczbami rzędu 100000000

najdrobniejszy wkład, który możemy dodać jest większy od 4 i mniejszy od 8.

W konsekwencji dodanie 4 nie skutkuje dodaniem czegokolwiek. Dodanie 8

działa. Próba dodania 9 również skończyłaby się wynikiem 100000008. Taki już

urok liczb zmiennoprzecinkowych.

MPI – komunikacja zbiorowa [2]:

allreduce

• Allreduce() realizuję operację analogiczną do Reduce(), z tą różnicą, że

wynik redukcji trafia do każdego z procesorów, nie tylko do root.

• W konsekwencji na każdym procesorze dostajemy ten sam wynik operacji (przy

założeniu łączności).

• Składnia – taka sama jak dla Reduce(), tylko parametr root nie jest już potrzebny,

bo wszyscy odbierają.

void MPI::Intracomm::Allreduce(const void* sendbuf, void* recvbuf,

int count, const Datatype& datatype, Op& op);

• Obowiązują te same założenia, co dla MPI_Reduce().

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 144

Koniec komunikacji zbiorowej

[email protected] 2004-2017 Jacek Dziedzic, FTiMS, PG 145