MPI-2 (pojawiła się w 1997r.)
l W 1995 przeprowadzono ankietę wśród użytkowników MPI, pytając co chcieliby widzieć w kolejnej wersji standardu. Najczęściej padały prośby o dodanie:
l dynamicznego zarządzania procesami
(startowanie nowych procesów w trakcie działania programu, etc.),
l wsparcia dla C++ (już z niego korzystamy),
l komunikacji jednostronnej (wszystkie informacje potrzebne do przesłania wiadomości dostarcza jedna strona, drugiej nie zawraca się głowy).
Operacje typu Put i Get, pozwalają pisać i czytać z węzła bez jego udziału.
l równoległego we/wy.
l Nie wszystkie implementacje MPI dysponują rozszerzeniami z MPI-2, ale większość tak.
l Po co nam równoległe we/wy?
l Najczęściej mamy do czynienia z sytuacją, w której każdy węzeł pracuje na porcji danych (dekompozycja danych). W pamięci masowej chcielibyśmy jednak mieć wszystkie dane w (uporządkowanej) całości.
<---> Bez równoległego we/wy – metoda 1
l Każdy węzeł może zapisywać dane do oddzielnego pliku.
l Łatwe do zrobienia, ale ma szereg wad:
l Podczas obróbki danych (post-processing) niewygodnie jest pracować z wieloma plikami zamiast z jednym.
l Co zrobić, jeśli liczba węzłów zmieni się pomiędzy kolejnymi wywołaniami programu? Przykładowo program pracował na 4 węzłach, wyprodukował 4 pliki wyjściowe, a jutro chcemy by pracował na 5 węzłach.
l Potrzeba dodatkowych użytków, które sklejałyby pliki do jednego po tym, jak program zakończy się → strata czasu i przestrzeni dyskowej (co if pliki po 4 GB?).
l Zapis do wielu plików jest niewydajny (seek,fragmentacja).
<---> Bez równoległego we/wy – metoda 2
l Można zebrać dane na 1 węźle korzystając z przesyłania wiad.i zapisać je z tego węzła.
l Wady tego rozwiązania:
l Nierówny podział pracy – węzeł, na którym piszemy do pliku jest zajęty wykonując we/wy, podczas gdy pozostałe węzły nudzą się.
l Na węźle odpowiedzialnym za we/wy potrzebujemy mnóstwo dodatkowej pamięci, żeby pomieścić naraz dane ze wszystkich węzłów (jeśli korzystamy z Gather).
l Można przesyłać po kawałku i pisać po kawałku, ale wtedy pozostałe węzły są dłużej zajęte (czekają z przesłaniem reszty aż master zapisze poprzednią porcję).
l Obciążenie sieci – teraz wszystkie dane muszą przejść przez węzeł master, a być może bezpośrednie połączenia węzeł-serwer plików są krótsze.
MPI-2 oferuje równoległe we/wy – wszystkie węzły piszą jednocześnie do jednego pliku
l Wygodne (dostajemy jeden plik).
l Dobry podział pracy (wszystkie węzły zajęte).
l Efektywne (MPI i SO mogą optymalizować zapisy i odczyty).
l Podobnie dla odczytu.
Równoległe we/wy – jak?
l Skoncentrujmy uwagę na najbardziej pospolitej czynności – wczytaniu danych
z jednego pliku na wiele węzłów tak, że każdy z nich dostaje fragment danych.
l Trzy metody:
l przesuń wskaźnik pliku i czytaj,
l czytaj-od-miejsca (read_at),
l widoki plików.
<---> Równoległe we/wy – otwarcie pliku
l Zaczynamy od równoległego otwarcia pliku:
static MPI::File MPI::File::Open(const MPI::Intracomm& comm,
const char *filename, int amode, const MPI::Info& info);
l To jest operacja zbiorowa – wszystkie węzły komunikatora muszą ją wywołać.
<---> Równoległe we/wy – zamykanie pliku
l Gdy skończymy pracę z plikiem, trzeba go zamknąć:
void MPI::File::Close();
l Zatem wołamy metodę Close() na rzecz obiektu klasy MPI::File, który reprezentuje nasz plik i który otrzymaliśmy jako wynik działania funkcji MPI::File::Open().
l To też jest operacja zbiorowa – wszystkie węzły muszą jednocześnie zamykać plik.
l Musimy zamknąć wszystkie pliki zanim wywołamy MPI::Finalize().
l W momencie zamknięcia pliku musimy zagwarantować, że wszystkie nieblokujące operacje we/wy zakończyły się (my mówimy tylko o blokujących operacjach we/wy).
<---> Równoległe we/wy – czytamy z pliku
l Odczyt wygląda podobnie do odbierania wiadomości:
void MPI::File::Read(void* buf, int count,const MPI::Datatype& datatype, PI::Status& status);
l NIE jest operacją grupową (!) – nie wszystkie węzły komunikatora muszą czytać.
Jeśli chcemy czytać na niektórych węzłach, wywołujemy MPI::File::Read() tylko na tych.
l Istnieje wersja: MPI::File::Read_all(), która służy do odczytu na wszystkich węzłach.
l Jeśli czytamy na wszystkich węzłach, korzystniej jest zastosować wersję grupową z uwagi na możliwość optymalizacji odczytu przez MPI i SO.
<---> Równoległe we/wy – uwagi dot. odczytu
l Wskaźniki pozycji pliku są niezależne na wszystkich węzłach.
l Oznacza to, że odczyt na jednym węźle nie ma wpływu na stan pliku
(w szczególności na wskaźnik pozycji pliku) na pozostałych węzłach
l Po tym jak węzeł A dokonał odczytu jego wskaźnik pozycji pliku przesuwa się, ale wskaźnik pozycji na B pozostaje niezmieniony.
l Istnieje przeciążona wersja operacji Read() pozbawiona ostatniego argumentu (status) – korzystamy z niej jeśli status nas nie interesuje.
l Liczbę odczytanych elementów możemy wydobyć za pomocą metody Get_count().
l Kontrolą błędów musimy się zająć sami.
<---> Równoległe we/wy – zapis do pliku
l Analogicznie do odczytu (i podobnie do wysłania wiadomości):
void MPI::File::Write(const void* buf, int count,
const MPI::Datatype& datatype, MPI::Status& status);
l Podobnie jak poprzednio: NIE jest operacją zbiorową (!) – nie wszystkie węzły komunikatora muszą pisać.
l Jeśli jednak piszemy na wszystkich węzłach, warto skorzystać ze zbiorowej operacji MPI::File::Write_all() – pozwoli ona MPI optymalizować zapis.
l Argumenty – jak dla MPI::File::Read(), tylko bufor jest const.
l Jak przy odczycie – każdy węzeł ma swój, niezależny wskaźnik zapisu.
l Sygnalizacja problemu: co ...
inf4