najczęstsze błędy i wypaczenia przy stosowaniu komunikacji zbiorowej
DESCRIPTION
Komunikacja zbiorowa: część II. Najczęstsze błędy i wypaczenia przy stosowaniu komunikacji zbiorowej Grupy procesów i podział komunikatorów Operacje na grupach procesów Komunikacja wewnątrzgrupowa i komunikatory wewnętrzne Komunikacja międzygrupowa i komunikatory zewnętrzne. - PowerPoint PPT PresentationTRANSCRIPT
Najczęstsze błędy i wypaczenia przy stosowaniu komunikacji zbiorowej
Grupy procesów i podział komunikatorów
Operacje na grupach procesów
Komunikacja wewnątrzgrupowa i komunikatory wewnętrzne
Komunikacja międzygrupowa i komunikatory zewnętrzne
Komunikacja zbiorowa: część II
Błędy i wypaczenia w komunikacji zbiorowej
1. Wywołanie procedury komunikacji zbiorowej tylko dla części procesorów zawartych w zaspecyfikowanym komunikatorze.
2. W szczególności zapomnienie o tym, że w przypadku MPI_Bcast zarówno procesor źródłowy jak i wszyscy odbiorcy muszą zawołać tę procedurę.
3. Założenie, że wszystkie procesory jednocześnie zakończą wykonywanie danej procedury komunikacji zbiorowej.
4. Użycie tego samego bufora wejściowego i wyjściowego w wywołaniu MPI_Reduce (MPI_Allreduce).
1 i 2. Wywołanie w zależności od procesora (BARDZO POWAŻNY błąd, bo nie powoduje zatrzymania programu i przez to znakomicie utrudnia jego odrobaczenie).
if (rank == 0) {MPI_Bcast(dane, ..., Master, ..., MPI_COMM_WORLD);do_masters_work();
else {do_work(dane,wyniki);MPI_Gather(wyniki,...,Master,...,MPI_COMM_WORLD);
}W takim przypadku master wyśle dane do pozostałych procesorów ale dane te nigdy nie zostaną odebrane, natomiast wyniki wysłane przez robotników nigdy nie zostaną odebrane przez mastera. Należy napisać tak:
MPI_Bcast(dane, ..., Master, ..., MPI_COMM_WORLD);if (rank == 0) {
do_masters_work();else
do_work(dane,wyniki);}MPI_Gather(wyniki,...,Master,...,MPI_COMM_WORLD);
4. Użycie tego samego bufora wejściowego i wyjściowego w MPI_Reduce
call MPI_Allreduce(a, a, 1, MPI_REAL, MPI_SUM, comm, ierr);
Spowoduje to natychmiastowy bład wykonania i przerwanie programu, więc błąd taki jest łatwy do wykrycia. Wymóg, aby bufor wyjściowy był różny od wyjściowego zapewnia kompatybilność ze standartem FORTRANu (przekazywanie zmiennej przez adres a nie przez wartość); w związku z tym ograniczenie obowiązuje również przy wołaniu MPI_Reduce w programach w C.
Poprawny kod:
call MPI_Allreduce(a, a_sum, 1, MPI_REAL, MPI_SUM, comm, ierr);
Grupy procesów i podział komunikatorów
Grupa procesów: zbiór procesorów uporządkowany według rzędu. W MPI zdefiniowano „pustą” grupę: MPI_GROUP_EMPTY oraz dwie stałe: MPI_GROUP_EMPTY (odnośnik do grupy pustej) oraz MPI_GROUP_NULL (odnośnik do grupy zdefiniowanej niewłaściwie).
Podział komunikatorów:
Komunikatory wewnętrzne (intrakomunikatory): służą do komunikacji w obrębie grupy; możliwa jest komunikacja punktowa i zbiorowa.
Komunikatory zewnętrzne (interkomunikatory): służą do komunikacji pomiędzy dwoma grupami. W standarcie MPI-1 możliwa jest jedynie komunikacja punktowa (MPI_Send i MPI_Receive).
Funkcjonalność Intrakomunikator Interkomunikator
Liczba grup 1 2
Bezpieczeństwo Tak Tak
Komunikacja zbiorowa Tak Nie
Topologie Tak Nie
Kaszowanie Tak Tak
Charakterystyka intra- i interkomunikatorów
Operacje na grupach
MPI_GROUP_SIZE(group, size): zwraca liczbę procesorów w grupie. MPI_Group_size(MPI_Group group, int *size) MPI_GROUP_SIZE(GROUP, SIZE, IERROR) INTEGER GROUP, SIZE, IERROR group - grupa size - liczba procesorów w grupie
MPI_GROUP_RANK(group, rank): zwraca rząd danego procesora w grupie; jeżeli procesor nie jest w grupie jest zwracany rząd MPI_UNDEFINED. group - grupa rank - rząd procesora w grupie. MPI_Group_rank(MPI_Group group, int *rank) MPI_GROUP_RANK(GROUP, RANK, IERROR) INTEGER GROUP, RANK, IERROR
MPI_GROUP_TRANSLATE_RANKS(group1, n, ranks1, group2, ranks2)
podaje rzędy procesorów z jednej grupy w innej grupie; w przypadku, gdy któryś z procesorów nie należy do drugiej grupy zwraca w tym miejscu MPI_UNDEFINED.
group1 - grupa pierwsza n - liczba procesorów w tablicach ranks1 i ranks2 ranks1 - tablica rzędów w grupie pierwszej group2 - grupa druga ranks2 - tablica rzędów w grupie drugiej
MPI_Group_translate_ranks (MPI_Group group1, int n, int *ranks1, MPI_Group group2, int *ranks2)
MPI_GROUP_TRANSLATE_RANKS(GROUP1, N, RANKS1, GROUP2, RANKS2, IERROR) INTEGER GROUP1, N, RANKS1(*), GROUP2, RANKS2(*), IERROR
MPI_GROUP_COMPARE(group1, group2, result)
porównuje dwie grupy zwracając wynik w zmiennej result:
• MPI_IDENT: grupy są identyczne;
• MPI_SIMILAR: grupy zawierają te same procesory ale w innym porządku;
• MPI_UNEQUAL: grupy są różne.
Kreatory grup
MPI_COMM_GROUP(comm, group)
uzyskiwanie wskaźnika grupy (group) odpowiadającej komunikatorowi comm.
MPI_Comm_group(MPI_Comm comm, MPI_Group *group)
MPI_COMM_GROUP(COMM, GROUP, IERROR) INTEGER COMM, GROUP, IERROR
Trzy standardowe operacje mnogościowe:
MPI_GROUP_UNION(group1, group2, newgroup) MPI_GROUP_INTERSECTION(group1, group2, newgroup) MPI_GROUP_DIFFERENCE(group1, group2, newgroup)
MPI_GROUP_INCL(group, n, ranks, newgroup)
tworzenie nowej grupy z elementów starej.
group - stara grupa; n - liczba procesorów ze starej grupy, które mają być włączone do
nowej; ranks - tablica zawierająca te rzędy; newgroup - nowa grupa.
MPI_Group_incl(MPI_Group group, int n, int *ranks, MPI_Group *newgroup) MPI_GROUP_INCL(GROUP, N, RANKS, NEWGROUP, IERROR) INTEGER GROUP, N, RANKS(*), NEWGROUP, IERROR
MPI_GROUP_EXCL(group, n, ranks, newgroup)
tworzy nową grupę ze starej poprzez wyłączenie n procesorów o rzędach podanej w tablicy ranks. Składnia analogiczna do MPI_GROUP_INCL.
MPI_GROUP_RANGE_INCL(group, n, ranges, newgroup) MPI_GROUP_RANGE_EXCL(group, n, ranges, newgroup)
Wygodniejsze formy poprzednich. Tablica ranges(3,*) zawiera zakresy włączanych/wyłączanych procesorów; ranges(1,i) i ranges(2,i) definiują odpowiednio pierwszy i ostatni procesorów w i-tym zakresie, ranges(3,i) mówi z jakim krokiem włącza/wyłącza się procesory (1 - każdy, 2 - co drugi, itd.).
Destruktor grupy
MPI_GROUP_FREE(group)
MPI_Group_free(MPI_Group *group) MPI_GROUP_FREE(GROUP, IERROR) INTEGER GROUP, IERROR
Komunikacja wewnątrzgrupowa i komunikatory wewnętrzne
Komunikacja wewnątrzgrupowa: wymiana informacji w obrębie procesorów należących do jednej grupy. Możliwe jest tutaj stosowanie zarówno procedur komunikacji punktowej jak i zbiorowej.
Operacje na komunikatorach
MPI_COMM_SIZE(comm, size) MPI_COMM_RANK(comm, rank) MPI_COMM_COMPARE(comm1, comm2, result)
Wartości zwracane przez MPI_COMM_COMPARE w zmiennej result są podobne jak w przypadku MPI_GROUP_COMPARE z wyjątkiem, że w przypadku identyczności grup odpowiadających komunikatorom comm1 i comm2 zwracana jest wartość MPI_CONGRUENT.
Standardowe komunikatory
MPI_COMM_WORLD: wszystkie procesory przydzielone zadaniu; MPI_COMM_SELF: dany procesor (zawsze ma rząd 0). MPI_COMM_NULL: pusty komunikator.
Kreatory komunikatorów
MPI_COMM_DUP(comm, newcomm)tworzenie duplikatu komunikatora.
comm - stary komunikator; newcomm - nowy komunikator.
MPI_Comm_dup(MPI_Comm comm, MPI_Comm *newcomm)
MPI_COMM_DUP(COMM, NEWCOMM, IERROR) INTEGER COMM, NEWCOMM, IERROR
MPI_COMM_CREATE(comm, group, newcomm)
tworzenie („pączkowanie”) nowego komunikatora.
comm - stary komunikator (może być MPI_COMM_WORLD); group - grupa procesorów, które mają utworzyć nowy komunikator; newcomm - nowy komunikator.
int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm)
MPI_COMM_CREATE(COMM, GROUP, NEWCOMM, IERROR) INTEGER COMM, GROUP, NEWCOMM, IERROR
MPI_COMM_SPLIT(comm, color, key, newcomm)
tworzenie nowych komunikatorów poprzez podział starego według przypisania zawartego w zmiennej color.
comm - stary komunikator; color - kolor; procesory o różnych kolorach będą tworzyły różne komunikatory; key - klucz; wskazuje, jak ma wzrastać rząd procesorów (jeżeli dla dwóch procesorów w nowej grupie jest taki sam, kolejność rzędów jest tak jak kolejność rzędów w starej grupie); newcomm - nowy komunikator, w którym znajdzie się wywołujący procesor.
Jeżeli nie chcemy, żeby dany procesor znalazł się w jakimkolwiek z nowych komunikatorów, nadajemy mu kolor MPI_UNDEFINED.
MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *newcomm)
MPI_COMM_SPLIT(COMM, COLOR, KEY, NEWCOMM, IERROR) INTEGER COMM, COLOR, KEY, NEWCOMM, IERROR
Rozważmy grupę procesorów a-j o rzędach (w starym komunikatorze) od 0-9 (┴ oznacza kolor MPI_UNDEFINED):
rząd 0 1 2 3 4 5 6 7 8 9
procesor a b c d e f g h i j
kolor 0 3 0 3 0 0 5 3
klucz 3 1 2 5 1 1 1 2 1 0
Wywołanie MPI_COMM_SPLIT dla tego układu spowoduje powstanie trzech nowych komunikatorów: • {f,g,a,d} (kolor 0); rzędy będą wynosiły odpowiednio 0, 1, 2,
3. • {e,i,c} (kolor 3); rzędy będą wynosiły 0, 1, 2. • {h} (kolor 5); rząd oczywiście 0.
Procesory b oraz j nie będą należały do żadnego z komunikatorów.
┴ ┴
Destruktor komunikatora
MPI_COMM_FREE(comm)
MPI_Comm_free(MPI_Comm *comm)
MPI_COMM_FREE(COMM, IERROR) INTEGER COMM, IERROR
Przykład: Obliczanie liczby metodą Monte Carlo
Algorytm obliczania liczby :losujemy pary liczb (x,y) należące do przedziału [-1..1] a następnie obliczamy przybliżenie ze stosunku liczby wylosowanych punktów leżących w kole scentrowanym w punkcie (0,0) i o promieniu 1 do liczby wszystkich wylosowanych punktów.
1. Dedykujemy procesor o najwyższym rzędzie jako serwer liczb losowych dla pozostałych procesorów.
2. Z pozostałych procesorów tworzymy grupę przy pomocy procedury MPI_GROUP_EXCL oraz MPI_COMM_CREATE grupę „robotników” i definiujemy odpowiedni komunikator workers.
3. „Robotnicy” obliczają ile przysłanych losowych punktów leży w kole (N_in) a ile poza nim (N_out), następnie wykorzystując procedurę MPI_ALLREDUCE działająca w obrębie komunikatora workers.
4. Jeżeli obliczone przybliżenie liczby różni się od wartości prawdziwej o mniej niż zadeklarowaną dokładność (pobierana z linii polecenia) lub przekroczono maksymalną zadeklarowaną liczbę kroków, program się kończy. Jeżeli nie, każdy z „robotników” wysyła do serwera żądanie inicjalizacji generatora liczb losowych i procedura jest powtarzana od punktu 3.
Kod źródłowy programu w języku C
Wyniki (4 procesory)
Przykład zastosowania procedury MPI_Comm_split
Źródło programu w C
Wyniki
Komunikacja międzygrupowa
Komunikacja międzygrupowa: wymiana informacji pomiędzy procesami należącymi do rozłącznych grup. Komunikację międzygrupową stosuje się dla zadań modularnych, gdzie informacje muszą przepływać pomiędzy kolejnymi grupami (rura) lub, w bardziej ogólnym przypadku, płynąć po grafie zdefiniowanym przez interkomunikatory.
Cechy komunikacji międzygrupowej:
• Można stosować jedynie procedury komunikacji punktowej.
• Pomiędzy grupami procesów definiuje się skierowane interkomunikatory; zbiór interkomunikatorów tworzy graf połączeń.
• Procesy identyfikuje się poprzez ich rzędy w lokalnych grupach.
Kreator interkomunikatorów
MPI_INTERCOMM_CREATE(local_comm, local_leader, bridge_comm, remote_leader, tag, newintercomm) local_comm - komunikator lokalny local_leader - rząd procesora będącego „bazą” komunikatora lokalnego; musi być w bridge_comm bridge_comm - komunikator mostkujący; musi zawierać procesory obu łączonych grup (na ogół MPI_COMM_WORLD) remote_leader - rząd procesora-bazy komunikatora odległego (w bridge_comm) tag – „pieczątka” interkomunikatora newintercomm - nowo postały interkomunikator
MPI_Intercomm_create(MPI_Comm local_comm, int local_leader, MPI_Comm bridge_comm, int remote_leader, int tag, MPI_Comm *newintercomm)
MPI_INTERCOMM_CREATE(LOCAL_COMM, LOCAL_LEADER, PEER_COMM, REMOTE_LEADER, TAG, NEWINTERCOMM, IERROR) INTEGER LOCAL_COMM, LOCAL_LEADER, PEER_COMM, REMOTE_LEADER, TAG, NEWINTERCOMM, IERROR
Tworzenie intrakomunikatora z dwóch interkomunikatorów
MPI_INTERCOMM_MERGE(intercomm, high, newintracomm) intercomm - interkomunikator high - kolejność procesorów w tworzonej grupie newintracomm - utworzony intrakomunikator MPI_Intercomm_merge(MPI_Comm intercomm, int high, MPI_Comm *newintracomm)
Sprawdzanie, czy komunikator jest intra- czy interkomunikatorem
MPI_COMM_TEST_INNER(comm, flag) comm - komunikator flag - flaga; true jeżeli komunikator jest interkomunikatorem
MPI_Comm_test_inter(MPI_Comm comm, int *flag)
Uzyskiwanie rozmiaru, rzędu procesora oraz grupy odpowiadającej „lokalnej” części interkomunikatora:
MPI_COMM_RANK (comm, rank) MPI_COMM_SIZE (comm, size) MPI_COMM_GROUP (comm, group)
Uzyskiwanie rozmiaru oraz grupy odpowiadającej „odległej” części interkomunikatora:
MPI_COMM_REMOTE_SIZE (comm, size) MPI_COMM_REMOTE_GROUP (comm, group)
Przykład: Symulacja układu ocean: atmosfera
call MPI_COMM_SIZE( MPI_COMM_WORLD, nprocs, ierr )call MPI_COMM_RANK( MPI_COMM_WORLD, rank, ierr )if (rank .lt. Size/2 ) then
color = OCEANelse
color = ATMOSendifcall MPI_COMM_SPLIT( MPI_COMM_WORLD, color, rank, ocean_or_atmos_comm, ierr)call MPI_INTERCOMM_CREATE( ocean_or_atoms_comm, 0, MPI_COMM_WORLD, 0, 0, intercomm, ierr)if (color .eq. OCEAN) then
ocean_comm = ocean_or_atmos_commcall do_ocean( ocean_comm )
elseatmos_comm = ocean_or_atmos_commcall do_atmos( atmos_comm )
endifcall ocean_and_atmos( intercomm )
Przykład bardziej konkretny: przepływ danych pomiędzy grupami procesorów („rura”)
Grupę 12 procesorów dzielimy na 3 podgrupy po 4 procesory w każdej, jak na rysunku.
Dane są wymieniane w obie strony pomiędzy grupami 0 i 1 oraz 1 i 2. W tym celu dla grup 0 i 2 musimy zdefiniować po jednym komunikatorze, natomiast dla grupy 1 musimy zdefiniować 2 interkomunikatory - odpowiednio do komunikacji z grupą 0 i 2.
Źródło programu w C
Wyniki (12 procesorów)