Interfejs przekazywania wiadomości

Message Passing Interface ( MPI ) to ustandaryzowany i przenośny standard przesyłania komunikatów zaprojektowany do działania w architekturach obliczeń równoległych . Standard MPI definiuje składnię i semantykę procedur bibliotecznych , które są przydatne dla szerokiego grona użytkowników piszących przenośne programy do przekazywania komunikatów w językach C , C++ i Fortran . Istnieje kilka implementacji MPI typu open source , co sprzyjało rozwojowi branży oprogramowania równoległego i zachęcało do opracowywania przenośnych i skalowalnych aplikacji równoległych na dużą skalę.

Historia

Prace nad interfejsem przekazywania wiadomości rozpoczęły się latem 1991 r., kiedy mała grupa badaczy rozpoczęła dyskusje w górskim ośrodku w Austrii. Z tej dyskusji zrodziły się warsztaty na temat standardów przekazywania wiadomości w środowisku pamięci rozproszonej, które odbyły się w dniach 29–30 kwietnia 1992 r. W Williamsburgu w Wirginii . Uczestnicy Williamsburga omówili podstawowe funkcje niezbędne dla standardowego interfejsu do przekazywania wiadomości i utworzyli grupę roboczą, aby kontynuować proces standaryzacji. Jack Dongarra , Tony Hey i David W. Walker przedstawili wstępny projekt propozycji „MPI1” w listopadzie 1992 r. W listopadzie 1992 r. w Minneapolis odbyło się spotkanie grupy roboczej MPI, na której postanowiono umieścić proces normalizacji na bardziej podstawa formalna. Grupa robocza MPI spotykała się co 6 tygodni przez pierwsze 9 miesięcy 1993 roku. Projekt standardu MPI został zaprezentowany na konferencji Supercomputing '93 w listopadzie 1993 roku. Po okresie publicznych komentarzy, które zaowocowały pewnymi zmianami w MPI, wersja 1.0 MPI zostało wydane w czerwcu 1994. Te spotkania i dyskusja e-mailowa razem stworzyły MPI Forum, do którego członkostwo było otwarte dla wszystkich członków wysokowydajnych komputerów .

W prace MPI zaangażowanych było około 80 osób z 40 organizacji, głównie ze Stanów Zjednoczonych i Europy. Większość głównych dostawców komputerów współbieżnych była zaangażowana w działania MPI, współpracując z naukowcami z uniwersytetów, laboratoriów rządowych i przemysłu .

MPI zapewnia dostawcom sprzętu równoległego jasno zdefiniowany podstawowy zestaw procedur, które można skutecznie wdrożyć. W rezultacie dostawcy sprzętu mogą wykorzystywać ten zbiór standardowych niskiego poziomu do tworzenia procedur wyższego poziomu dla środowiska komunikacyjnego z pamięcią rozproszoną dostarczanego z ich maszynami równoległymi . MPI zapewnia prosty w użyciu przenośny interfejs dla podstawowego użytkownika, a jednocześnie wystarczająco wydajny, aby umożliwić programistom korzystanie z wysokowydajnych operacji przekazywania komunikatów dostępnych na zaawansowanych maszynach.

Próbując stworzyć uniwersalny standard przekazywania wiadomości, badacze nie oparli go na jednym systemie, ale włączyli najbardziej przydatne funkcje kilku systemów, w tym zaprojektowanych przez IBM, Intel, nCUBE, PVM, Express , P4 i PARMACS . Paradygmat przekazywania komunikatów jest atrakcyjny ze względu na szeroką przenośność i może być używany w komunikacji dla multiprocesorów z pamięcią rozproszoną i współdzieloną, sieci stacji roboczych oraz kombinacji tych elementów. Paradygmat może mieć zastosowanie w wielu ustawieniach, niezależnie od szybkości sieci lub architektury pamięci.

Wsparcie dla spotkań MPI pochodziło częściowo od DARPA i amerykańskiej National Science Foundation (NSF) w ramach grantu ASC-9310330, NSF Science and Technology Center Cooperative Agreement numer CCR-8809615 oraz od Komisji Europejskiej za pośrednictwem Esprit Project P6643. University of Tennessee również wniósł wkład finansowy do Forum MPI.

Przegląd

MPI to protokół komunikacyjny do programowania komputerów równoległych . Obsługiwana jest zarówno komunikacja punkt-punkt, jak i komunikacja zbiorowa. MPI „to interfejs programisty aplikacji przekazujący komunikaty, wraz z protokołami i specyfikacjami semantycznymi dotyczącymi tego, jak jego funkcje muszą zachowywać się w dowolnej implementacji”. Cele MPI to wysoka wydajność, skalowalność i przenośność. MPI pozostaje obecnie dominującym modelem stosowanym w obliczeniach o wysokiej wydajności .

MPI nie jest usankcjonowane przez żaden główny organ normalizacyjny; niemniej jednak stał się de facto standardem komunikacji między procesami, które modelują program równoległy działający w systemie pamięci rozproszonej . Rzeczywiste superkomputery z pamięcią rozproszoną, takie jak klastry komputerów, często uruchamiają takie programy.

Główny model MPI-1 nie ma koncepcji pamięci współdzielonej , a MPI-2 ma tylko ograniczoną koncepcję rozproszonej pamięci współdzielonej . Niemniej jednak programy MPI są regularnie uruchamiane na komputerach z pamięcią współdzieloną, a zarówno MPICH , jak i Open MPI mogą wykorzystywać pamięć współdzieloną do przesyłania wiadomości, jeśli jest dostępna. Projektowanie programów wokół modelu MPI (w przeciwieństwie do jawnych pamięci współdzielonej ) ma zalety w przypadku uruchamiania na architekturach NUMA , ponieważ MPI zachęca do lokalizacji pamięci . Jawne programowanie w pamięci współdzielonej zostało wprowadzone w MPI-3.

Chociaż MPI należy do warstw 5 i wyższych modelu referencyjnego OSI , implementacje mogą obejmować większość warstw, z gniazdami i protokołem kontroli transmisji (TCP) używanymi w warstwie transportowej.

Większość implementacji MPI składa się z określonego zestawu procedur wywoływanych bezpośrednio z C , C++ , Fortran (tj. API) i dowolnego języka, który może współpracować z takimi bibliotekami, w tym C# , Java lub Python . Przewagą MPI nad starszymi bibliotekami przekazywania komunikatów jest przenośność (ponieważ MPI zostało zaimplementowane dla prawie każdej architektury pamięci rozproszonej) i szybkość (ponieważ każda implementacja jest w zasadzie zoptymalizowana pod kątem sprzętu, na którym działa).

MPI używa specyfikacji niezależnych od języka (LIS) do połączeń i powiązań językowych. Pierwszy standard MPI określał ANSI C i Fortran-77 wraz z LIS. Projekt został zaprezentowany na Supercomputing 1994 (listopad 1994) i wkrótce potem sfinalizowany. Około 128 funkcji składa się na standard MPI-1.3, który został wydany jako ostateczne zakończenie serii MPI-1 w 2008 roku.

Obecnie standard ma kilka wersji: wersję 1.3 (powszechnie w skrócie MPI-1 ), która kładzie nacisk na przekazywanie komunikatów i ma statyczne środowisko wykonawcze, MPI-2.2 (MPI-2), która zawiera nowe funkcje, takie jak równoległe wejścia/wyjścia, dynamiczne zarządzanie procesami i zdalne operacje na pamięci oraz MPI-3.1 (MPI-3), który obejmuje rozszerzenia operacji zbiorczych o wersje nieblokujące oraz rozszerzenia operacji jednostronnych. LIS MPI-2 określa ponad 500 funkcji i zapewnia powiązania językowe dla ISO C , ISO C++ i Fortran 90 . Dodano również interoperacyjność obiektów, aby umożliwić łatwiejsze programowanie przekazywania komunikatów w różnych językach. Efektem ubocznym standaryzacji MPI-2, zakończonej w 1996 roku, było doprecyzowanie standardu MPI-1, tworząc MPI-1.2.

MPI-2 jest w większości nadzbiorem MPI-1, chociaż niektóre funkcje zostały wycofane. Programy MPI-1.3 nadal działają w ramach implementacji MPI zgodnych ze standardem MPI-2.

MPI-3 zawiera nowe powiązania Fortran 2008, jednocześnie usuwając przestarzałe powiązania C++, a także wiele przestarzałych procedur i obiektów MPI.

MPI jest często porównywany z Parallel Virtual Machine (PVM), które jest popularnym środowiskiem rozproszonym i systemem przekazywania wiadomości opracowanym w 1989 roku i który był jednym z systemów, które zmotywowały potrzebę standardowego równoległego przekazywania komunikatów. Wątkowe modele programowania pamięci współdzielonej (takie jak Pthreads i OpenMP ) oraz programowanie z przekazywaniem komunikatów (MPI/PVM) można uznać za komplementarne i były czasami używane razem, na przykład na serwerach z wieloma dużymi węzłami pamięci współdzielonej.

Funkcjonalność

Interfejs MPI ma zapewniać niezbędną topologię wirtualną, synchronizację i funkcjonalność komunikacyjną między zestawem procesów (które zostały odwzorowane na węzły/serwery/instancje komputerów) w sposób niezależny od języka, ze składnią specyficzną dla języka (powiązania), plus kilka funkcji specyficznych dla języka. Programy MPI zawsze działają z procesami, ale programiści często nazywają te procesy procesorami. Zazwyczaj w celu uzyskania maksymalnej wydajności każdy procesor (lub rdzeń w maszynie wielordzeniowej) zostanie przydzielony tylko jeden proces. To przypisanie odbywa się w czasie wykonywania za pośrednictwem agenta, który uruchamia program MPI, zwykle nazywanego mpirun lub mpiexec.

Funkcje biblioteki MPI obejmują między innymi operacje wysyłania/odbierania typu rendez-vous typu punkt-punkt, wybór między logiczną topologią procesów kartezjańskich lub grafowych , wymianę danych między parami procesów (operacje wysyłanie/odbieranie), łączenie częściowych wyniki obliczeń (operacje zbierania i redukcji), synchronizację węzłów (operacja barierowa) oraz uzyskiwanie informacji związanych z siecią, takich jak liczba procesów w sesji obliczeniowej, bieżąca tożsamość procesora, na który proces jest mapowany, sąsiednie procesy dostępne w topologia logiczna itp. Pojawiają się operacje punkt-punkt formy synchroniczne , asynchroniczne , buforowane i gotowe , aby umożliwić zarówno relatywnie silniejszą, jak i słabszą semantykę dla aspektów synchronizacji wysłania rendez-vous. Wiele znakomitych [ wymaganych wyjaśnień ] operacji jest możliwych w trybie asynchronicznym w większości implementacji.

MPI-1 i MPI-2 umożliwiają implementacje, które nakładają się na komunikację i obliczenia, ale praktyka i teoria różnią się. MPI określa również bezpieczne dla wątków , które mają strategie spójności i łączenia , które pomagają uniknąć stanu ukrytego w interfejsie. Stosunkowo łatwo jest napisać wielowątkowy kod MPI punkt-punkt, a niektóre implementacje obsługują taki kod. Wielowątkową komunikację zbiorową najlepiej realizuje się za pomocą wielu kopii komunikatorów, jak opisano poniżej.

koncepcje

MPI zapewnia kilka funkcji. Poniższe koncepcje zapewniają kontekst dla wszystkich tych możliwości i pomagają programiście zdecydować, jakiej funkcjonalności użyć w swoich aplikacjach. Cztery z ośmiu podstawowych koncepcji MPI są unikalne dla MPI-2.

Komunikator

Obiekty komunikatora łączą grupy procesów w sesji MPI. Każdy komunikator nadaje każdemu zawartemu procesowi niezależny identyfikator i porządkuje zawarte w nim procesy w uporządkowanej topologii . MPI ma również jawne grupy, ale są one głównie przydatne do organizowania i reorganizacji grup procesów przed utworzeniem kolejnego komunikatora. MPI rozumie operacje wewnątrzkomunikacyjne pojedynczej grupy i dwustronną komunikację międzykomunikacyjną. W MPI-1 najbardziej rozpowszechnione są operacje na jednej grupie. dwustronne najczęściej pojawiają się w MPI-2, gdzie obejmują komunikację zbiorową i dynamiczne zarządzanie w trakcie procesu.

Komunikatory można podzielić na partycje za pomocą kilku poleceń MPI. Polecenia te obejmują MPI_COMM_SPLIT , gdzie każdy proces dołącza do jednego z kilku kolorowych komunikatorów podrzędnych, deklarując, że ma ten kolor.

Podstawy punkt-punkt

Szereg ważnych funkcji MPI obejmuje komunikację między dwoma określonymi procesami. Popularnym przykładem jest MPI_Send , która umożliwia jednemu określonemu procesowi wysłanie wiadomości do drugiego określonego procesu. Operacje punkt-punkt, jak się je nazywa, są szczególnie przydatne w komunikacji wzorcowej lub nieregularnej, na przykład w architekturze danych równoległych, w której każdy procesor rutynowo wymienia regiony danych z określonymi innymi procesorami między etapami obliczeń lub master- architektura podrzędna , w której urządzenie nadrzędne wysyła nowe dane zadania do urządzenia podrzędnego po zakończeniu poprzedniego zadania.

MPI-1 określa mechanizmy zarówno dla blokujących , jak i nieblokujących mechanizmów komunikacji punkt-punkt, a także tak zwanego mechanizmu „ready-send”, w którym żądanie wysyłania może być wykonane tylko wtedy, gdy pasujące żądanie odbioru zostało już wykonane .

Zbiorowe podstawy

Funkcje kolektywne obejmują komunikację między wszystkimi procesami w grupie procesów (co może oznaczać całą pulę procesów lub podzbiór zdefiniowany przez program). Typową funkcją jest MPI_Bcast (skrót od „ broadcast ”). Ta funkcja pobiera dane z jednego węzła i wysyła je do wszystkich procesów w grupie procesów. Operacją odwrotną jest MPI_Reduce , które pobiera dane ze wszystkich procesów w grupie, wykonuje operację (taką jak sumowanie) i zapisuje wyniki w jednym węźle. MPI_Reduce jest często przydatne na początku lub na końcu dużych obliczeń rozproszonych, w których każdy procesor działa na części danych, a następnie łączy je w wynik.

Inne operacje wykonują bardziej wyrafinowane zadania, takie jak MPI_Alltoall , które przestawia n elementów danych w taki sposób, że n- ty węzeł otrzymuje n- ty element danych z każdego.

Pochodne typy danych

Wiele funkcji MPI wymaga określenia typu danych przesyłanych między procesami. Dzieje się tak dlatego, że MPI ma na celu obsługę heterogenicznych środowisk, w których typy mogą być reprezentowane w różny sposób w różnych węzłach (na przykład mogą one uruchamiać różne architektury procesora, które mają różną endianność ), w którym to przypadku implementacje MPI mogą przeprowadzać konwersję danych . Ponieważ język C nie pozwala na przekazanie samego typu jako parametru, MPI predefiniuje stałe MPI_INT , MPI_CHAR , MPI_DOUBLE , aby odpowiadały int , char , double , itp.

Oto przykład w C, który przekazuje tablice int s ze wszystkich procesów do jednego. Jeden proces odbierający nazywany jest procesem „root” i może to być dowolny wyznaczony proces, ale normalnie będzie to proces 0. Wszystkie procesy proszą o przesłanie swoich tablic do katalogu głównego za pomocą MPI_Gather , co jest równoważne z posiadaniem każdego procesu ( w tym sam root) wywołuje MPI_Send , a root wykonuje odpowiednią liczbę uporządkowanych wywołań MPI_Recv , aby złożyć wszystkie te tablice w większą:

 
   0 
  
 
    
   int  wyślij_tablica  [  100  ];  int  korzeń  =  ;  /* lub cokolwiek innego */  int  num_procs  ,  *  recv_array  ;  MPI_Comm_size  (  comm  ,  &  num_procs  );  recv_array  =  malloc  (  num_procs  *  sizeof  (  send_array  ));  MPI_Gather  (  wyślij_tablica  ,  rozmiar  (  wyślij_tablica  )  /   
               
             sizeof  (  *  send_array  ),  MPI_INT  ,  recv_array  ,  sizeof  (  send_array  )  /  sizeof  (  *  send_array  ),  MPI_INT  ,  root  ,  comm  ); 

Jednak zamiast tego możesz chcieć wysłać dane jako jeden blok zamiast 100 int s. Aby to zrobić, zdefiniuj pochodny typ danych „ciągły blok”:

 
  

        MPI_typ danych  nowy typ  ;  MPI_Type_contiguous  (  100  ,  MPI_INT  i  nowy typ  )  ;  MPI_Type_commit  (  &  nowy typ  );  MPI_Gather  (  tablica  ,  1  ,  nowy typ  ,  tablica_odbioru  ,  1  ,  nowy typ  ,  root  ,  comm  ); 

Aby przekazać klasę lub strukturę danych, MPI_Type_create_struct tworzy pochodny typ danych MPI z MPI_predefiniowanych typów danych w następujący sposób:

  
                            
                            
                            
                             int  MPI_Type_create_struct  (  liczba  int  ,  int  *  blocklen  ,  MPI_Aint  *  disp  ,  MPI_Datatype  *  typ  ,  MPI_Datatype  *  nowy typ  ) 

Gdzie:

  • count jest liczbą bloków i określa długość (w elementach) tablic blocklen , disp i type .
  • blocklen zawiera numery elementów w każdym bloku,
  • disp zawiera przesunięcia bajtów każdego bloku,
  • type zawiera typy elementów w każdym bloku.
  • newtype (wyjście) zawiera nowy typ pochodny utworzony przez tę funkcję

Tablica disp (przemieszczeń) jest potrzebna do wyrównania struktury danych , ponieważ kompilator może wypełnić zmienne w klasie lub strukturze danych. Najbezpieczniejszym sposobem znalezienia odległości między różnymi polami jest uzyskanie ich adresów w pamięci. Odbywa się to za pomocą MPI_Get_address , który zwykle jest taki sam jak operator C & , ale może to nie być prawdą w przypadku segmentacji pamięci .

Przekazywanie struktury danych jako jednego bloku jest znacznie szybsze niż przekazywanie jednego elementu na raz, zwłaszcza jeśli operacja ma się powtarzać. Dzieje się tak, ponieważ bloki o stałym rozmiarze nie wymagają serializacji podczas przesyłania.

Biorąc pod uwagę następujące struktury danych:

  
     
     


  
      
      
 struktura  A  {  int  f  ;  krótkie  p  ;  };  struktura  B  {  struktura  A  a  ;  int  pp  ,  vp  ;  }; 

Oto kod C do budowania typu danych pochodzącego z MPI:

        
     
          
         static  const  int  blocklen  []  =  {  1  ,  1  ,  1  ,  1  };  static  const  MPI_Aint  disp  []  =  {  przesunięcie  (  struktura  B  ,  a  )  +  przesunięcie  (  struktura  A  ,  f  ),  przesunięcie  (  struktura  B  ,  a  )  +  przesunięcie  (   
      
      

       
 
  struct  A  ,  p  ),  offsetof  (  struct  B  ,  pp  ),  offsetof  (  struct  B  ,  vp  )  };  static  MPI_Datatype  typ  []  =  {  MPI_INT  ,  MPI_SHORT  ,  MPI_INT  ,  MPI_INT  };  MPI_typ danych  nowy typ  ;  MPI_Type_create_struct  (  sizeof  (  typ  )  /      
 sizeof  (  *  typ  ),  blocklen  ,  disp  ,  typ  i  nowy typ  )  ;  MPI_Type_commit  (  &  nowy typ  ); 

Koncepcje MPI-2

Komunikacja jednostronna

MPI-2 definiuje trzy jednostronne operacje komunikacyjne, MPI_Put , MPI_Get i MPI_Accumulate , będące odpowiednio zapisem do pamięci zdalnej, odczytem ze zdalnej pamięci i operacją redukcji w tej samej pamięci w ramach wielu zadań. Zdefiniowano również trzy różne metody synchronizacji tej komunikacji (blokady globalne, parami i zdalne), ponieważ specyfikacja nie gwarantuje, że operacje te miały miejsce do punktu synchronizacji.

Tego typu wywołania często mogą być przydatne w przypadku algorytmów, w których synchronizacja byłaby niewygodna (np. rozproszone mnożenie macierzy ) lub tam, gdzie pożądane jest, aby zadania mogły równoważyć swoje obciążenie, podczas gdy inne procesory operują na danych.

Dynamiczne zarządzanie procesami

Kluczowym aspektem jest „zdolność procesu MPI do udziału w tworzeniu nowych procesów MPI lub do nawiązywania komunikacji z procesami MPI, które zostały uruchomione oddzielnie”. Specyfikacja MPI-2 opisuje trzy główne interfejsy, za pomocą których procesy MPI mogą dynamicznie ustanawiać komunikację, MPI_Comm_spawn , MPI_Comm_accept / MPI_Comm_connect i MPI_Comm_join . Interfejs MPI_Comm_spawn umożliwia procesowi MPI tworzenie wielu instancji nazwanego procesu MPI. Nowo utworzony zestaw procesów MPI tworzy nowy MPI_COMM_WORLD intracommunicator, ale może komunikować się z rodzicem i intercommunicatorem, który funkcja zwraca. MPI_Comm_spawn_multiple to alternatywny interfejs, który pozwala, aby różne instancje spawnowane były różnymi plikami binarnymi z różnymi argumentami.

we/wy

Funkcja równoległego wejścia/wyjścia jest czasami nazywana MPI-IO i odnosi się do zestawu funkcji zaprojektowanych do abstrakcyjnego zarządzania wejściami/wyjściami w systemach rozproszonych do MPI i umożliwia łatwy dostęp do plików w sposób wzorcowy przy użyciu istniejącej funkcji pochodnego typu danych .

Niewielkie badania przeprowadzone na tej funkcji wskazują, że uzyskanie wysokiego wzrostu wydajności przy użyciu MPI-IO może nie być trywialne. Na przykład implementacja rzadkich mnożeń macierzowo-wektorowych przy użyciu biblioteki MPI I/O wykazuje ogólne zachowanie niewielkiego wzrostu wydajności, ale wyniki te nie są rozstrzygające. Dopiero idea kolektywnego wejścia/wyjścia zaimplementowana w MPI-IO zaczęła upowszechniać MPI-IO. Zbiorcze operacje we/wy znacznie zwiększają przepustowość operacji we/wy aplikacji, ponieważ procesy wspólnie przekształcają małe i nieciągłe operacje we/wy w duże i ciągłe, zmniejszając w ten sposób blokowanie i wyszukiwanie dysku nad głową. Ze względu na ogromne korzyści w zakresie wydajności, MPI-IO stało się również bazową warstwą I/O dla wielu najnowocześniejszych bibliotek I/O, takich jak HDF5 i Parallel NetCDF . Jego popularność zapoczątkowała również badania nad kolektywnymi optymalizacjami we/wy, takimi jak we/wy uwzględniające układ i agregacja między plikami.

Oficjalne wdrożenia

Wiele innych wysiłków to pochodne prac MPICH, LAM i innych, w tym między innymi komercyjne implementacje firm HP , Intel , Microsoft i NEC .

Chociaż specyfikacje wymagają interfejsu C i Fortran, język używany do implementacji MPI nie jest ograniczony do dopasowania do języka lub języków, które ma obsługiwać w czasie wykonywania. Większość implementacji łączy języki C, C++ i asembler oraz jest przeznaczona dla programistów C, C++ i Fortran. Powiązania są dostępne dla wielu innych języków, w tym Perl, Python, R, Ruby, Java i CL (zobacz powiązania #Language ).

ABI implementacji MPI są z grubsza podzielone między pochodne MPICH i Open MPI , tak że biblioteka z jednej rodziny działa jako bezpośredni zamiennik biblioteki z tej samej rodziny, ale bezpośrednia wymiana między rodzinami jest niemożliwa . Francuski CEA utrzymuje interfejs opakowania, aby ułatwić takie przełączanie.

Sprzęt komputerowy

Badania sprzętowe MPI koncentrują się na wdrażaniu MPI bezpośrednio w sprzęcie, na przykład poprzez procesor w pamięci , wbudowując operacje MPI w mikroukłady układów RAM w każdym węźle. Oznacza to, że podejście to jest niezależne od języka, systemu operacyjnego i procesora, ale nie można go łatwo zaktualizować ani usunąć.

Innym podejściem było dodanie akceleracji sprzętowej do jednej lub kilku części operacji, w tym do sprzętowego przetwarzania kolejek MPI i używania RDMA do bezpośredniego przesyłania danych między pamięcią a kontrolerem interfejsu sieciowego bez interwencji procesora lub jądra systemu operacyjnego.

Opakowania kompilatora

mpicc (i podobnie mpic++ , mpif90 , itp.) to program, który zawija istniejący kompilator, aby ustawić niezbędne flagi wiersza poleceń podczas kompilacji kodu używającego MPI. Zazwyczaj dodaje kilka flag, które umożliwiają skompilowanie kodu i połączenie go z biblioteką MPI.

Wiązania językowe

Powiązania to biblioteki, które rozszerzają obsługę MPI na inne języki, opakowując istniejącą implementację MPI, taką jak MPICH lub Open MPI.

Wspólna infrastruktura językowa

Dwie zarządzane implementacje Common Language Infrastructure .NET to Pure Mpi.NET i MPI.NET, projekt badawczy na Uniwersytecie Indiana, licencjonowany na licencji typu BSD . Jest kompatybilna z Mono i może w pełni wykorzystywać podstawową strukturę sieciową MPI o niskim opóźnieniu.

Jawa

Chociaż Java nie ma oficjalnego powiązania MPI, kilka grup próbuje połączyć te dwa elementy, z różnym powodzeniem i kompatybilnością. Jedną z pierwszych prób była mpiJava Bryana Carpentera, zasadniczo zestaw Java Native Interface (JNI) do lokalnej biblioteki C MPI, co zaowocowało implementacją hybrydową o ograniczonej przenośności, którą również należy skompilować z konkretną używaną biblioteką MPI .

Jednak ten oryginalny projekt zdefiniował również interfejs API mpiJava ( de facto interfejs API MPI dla języka Java, który był ściśle zgodny z równoważnymi powiązaniami C++), który przyjęły inne późniejsze projekty Java MPI. Rzadziej używanym interfejsem API jest MPJ API, który został zaprojektowany tak, aby był bardziej zorientowany obiektowo i bliższy konwencjom kodowania firmy Sun Microsystems . Poza interfejsem API, biblioteki Java MPI mogą być zależne od lokalnej biblioteki MPI lub implementować funkcje przekazywania komunikatów w Javie, podczas gdy niektóre, takie jak P2P-MPI, zapewniają również funkcjonalność peer-to-peer i umożliwiają działanie na różnych platformach.

Niektóre z najtrudniejszych części Java/MPI wynikają z cech Javy, takich jak brak jawnych wskaźników i liniowej przestrzeni adresowej pamięci dla jej obiektów, co sprawia, że ​​przesyłanie wielowymiarowych tablic i złożonych obiektów jest nieefektywne. Obejścia zwykle obejmują przesyłanie jednej linii na raz i/lub wykonywanie jawnej deserializacji i rzutowania zarówno po stronie wysyłającej, jak i odbierającej, symulowanie tablic typu C lub Fortran za pomocą tablicy jednowymiarowej oraz wskaźników do typów pierwotnych za pomocą tablic jednoelementowych, co skutkuje stylami programowania dość dalekimi od konwencji Java.

Innym systemem przekazywania komunikatów Java jest MPJ Express. Najnowsze wersje mogą być uruchamiane w konfiguracjach klastrowych i wielordzeniowych. W konfiguracji klastrowej może wykonywać równoległe aplikacje Java na klastrach i chmurach. Tutaj gniazda Java lub wyspecjalizowane połączenia we/wy, takie jak Myrinet , mogą obsługiwać przesyłanie wiadomości między procesami MPJ Express. Może również wykorzystywać natywną implementację C MPI przy użyciu swojego natywnego urządzenia. W konfiguracji wielordzeniowej równoległa aplikacja Java jest wykonywana na procesorach wielordzeniowych. W tym trybie procesy MPJ Express są reprezentowane przez wątki Java.

Julia

Istnieje opakowanie języka Julii dla MPI.

MATLAB

Istnieje kilka akademickich implementacji MPI przy użyciu MATLAB . MATLAB ma własną bibliotekę rozszerzeń równoległych zaimplementowaną przy użyciu MPI i PVM .

OCaml

Moduł OCamlMPI implementuje duży podzbiór funkcji MPI i jest aktywnie wykorzystywany w obliczeniach naukowych. Program OCaml zawierający 11 000 wierszy został „zintegrowany z MPI” przy użyciu modułu, z dodatkowymi 500 wierszami kodu i niewielką restrukturyzacją, i działał z doskonałymi wynikami na maksymalnie 170 węzłach superkomputera.

PARI/GP

PARI/GP można zbudować tak, aby wykorzystywał MPI jako silnik wielowątkowy, umożliwiając uruchamianie równoległych programów PARI i GP w niezmodyfikowanych klastrach MPI.

Pyton

Implementacje MPI w Pythonie obejmują: pyMPI , mpi4py, pypar, MYMPI i submoduł MPI w ScientificPython . pyMPI jest godne uwagi, ponieważ jest wariantem interpretera Pythona, podczas gdy moduły pypar, MYMPI i ScientificPython są modułami importu. Sprawiają, że zadaniem kodera jest decydowanie, gdzie należy wywołanie MPI_Init .

W 2006 roku biblioteki Boost C++ nabyły Boost:MPI, które zawierały MPI Python Bindings. Jest to szczególnie pomocne przy mieszaniu C++ i Pythona. Od października 2016 r. Powiązania Boost: MPI z Pythonem nadal mają nienaprawione błędy w pakowaniu w CentOS .

R

R MPI obejmują Rmpi ​​i pbdMPI , gdzie Rmpi ​​koncentruje się na równoległości kierownik-pracownicy, podczas gdy pbdMPI koncentruje się na równoległości SPMD . Obie implementacje w pełni obsługują Open MPI lub MPICH2 .

Przykładowy program

Oto „Witaj, świecie!” program w MPI napisany w C. W tym przykładzie wysyłamy komunikat „hello” do każdego procesora, manipulujemy nim w trywialny sposób, zwracamy wyniki do głównego procesu i drukujemy komunikaty.




 
 
 
 

    

     
      

    
     /*  Program testowy MPI „Hello World”  */  #include  <assert.h>  #include  <stdio.h>  #include  <string.h>  #include  <mpi.h>  int  main  (  int  argc  ,  char  **  argv  )  {  char  buf  [  256  ];  int  moja_ranking  ,  num_procs  ;  /* Inicjalizacja infrastruktury niezbędnej do komunikacji */  MPI_Init  (  

    
     

    
     

    

       0 &  argc  ,  &  argv  );  /* Zidentyfikuj ten proces */  MPI_Comm_rank  (  MPI_COMM_WORLD  ,  &  my_rank  );  /* Dowiedz się, ile wszystkich procesów jest aktywnych */  MPI_Comm_size  (  MPI_COMM_WORLD  ,  &  num_procs  );  /* Do tego momentu wszystkie programy robiły dokładnie to samo.  Tutaj sprawdzamy rangę, aby rozróżnić role programów */  if  (  my_rank  ==  )  
         
         

        
               
        
              
             {  int  inny_ranking  ;  printf  (  "Mamy %i procesów.  \n  "  ,  num_procs  );  /* Wysyłanie komunikatów do wszystkich innych procesów */  for  (  inna_ranking  =  1  ;  inna_ranking  <  num_procs  ;  inna_ranking  ++  )  {  sprintf  (  buf  ,  "Witaj %i!"  ,  inna_ranking  );  MPI_Send  (  buf  ,    
                     0 
        

        
               
        
               
                     0   256  ,  MPI_CHAR  ,  inny_ranking  ,,  MPI_COMM_WORLD  )  ;  }  /* Odbierz komunikaty od wszystkich innych procesów */  for  (  inna_ranking  =  1  ;  inna_ranking  <  liczba_procs  ;  inna_ranking  ++  )  {  MPI_Recv  (  buf  ,  256  ,  MPI_CHAR  ,  inna_ranking  ,,  MPI_COMM_WORLD  ,  MPI_STATUS_IGNORE  )  ; 
             
        

      

        
           0
                 0  
            0

         printf  (  "%s  \n  "  ,  buf  );  }  }  else  { /* Odbierz   wiadomość  od procesu nr 0 */  MPI_Recv  (  buf  ,  256  ,  MPI_CHAR  ,  ,,  MPI_COMM_WORLD  ,  MPI_STATUS_IGNORE  );  assert  (  memcmp  (  buf  ,  "Cześć"  ,  6  )  ==  );  /* Wyślij wiadomość do procesu #0 */ 
          
           0
                 0 

    

    
    
     0
 sprintf  (  buf  ,  "Proces %i zgłasza się do służby."  ,  my_rank  );  MPI_Send  (  buf  ,  256  ,  MPI_CHAR  ,,,  MPI_COMM_WORLD  )  ;  _  }  /* Zburzenie infrastruktury komunikacyjnej */  MPI_Finalize  ();  powrót  ;  } 

Po uruchomieniu z 4 procesami powinien generować następujące dane wyjściowe:

$ mpicc example.c && mpiexec -n 4 ./a.out Mamy 4 procesy. Proces 1 zgłasza się do służby. Proces 2 zgłasza się do służby. Proces 3 zgłasza się do służby.

Tutaj mpiexec jest poleceniem używanym do wykonania przykładowego programu z 4 procesami , z których każdy jest niezależną instancją programu w czasie wykonywania i ma przypisane rangi (tj. numeryczne identyfikatory) 0, 1, 2 i 3. Nazwa mpiexec to zalecane przez standard MPI, chociaż niektóre implementacje udostępniają podobne polecenie pod nazwą mpirun . MPI_COMM_WORLD , który składa się ze wszystkich procesów.

pojedynczego programu i wielu danych ( SPMD ) jest w ten sposób ułatwiony, ale nie jest wymagany; wiele implementacji MPI umożliwia uruchamianie wielu różnych plików wykonywalnych w tym samym zadaniu MPI. Każdy proces ma swoją własną rangę, całkowitą liczbę procesów na świecie i możliwość komunikowania się między nimi za pomocą komunikacji punkt-punkt (wysyłanie/odbieranie) lub komunikacji zbiorowej w grupie. Wystarczy, że MPI dostarczy program w stylu SPMD z MPI_COMM_WORLD , własną rangę i rozmiar świata, aby algorytmy mogły decydować, co robić. W bardziej realistycznych sytuacjach operacjami we/wy zarządza się ostrożniej niż w tym przykładzie. MPI nie precyzuje jak standardowe wejścia/wyjścia (stdin, stdout, stderr) powinny działać w danym systemie. Ogólnie działa zgodnie z oczekiwaniami w procesie rangi 0, a niektóre implementacje przechwytują również i kierują dane wyjściowe z innych procesów.

MPI używa pojęcia procesu, a nie procesora. Kopie programów są odwzorowywane na procesory przez środowisko wykonawcze MPI . W tym sensie maszyna równoległa może być odwzorowana na jeden procesor fizyczny lub N procesorów, gdzie N to liczba dostępnych procesorów lub nawet coś pośredniego. Aby uzyskać maksymalną prędkość równoległą, używa się większej liczby procesorów fizycznych. Ten przykład dostosowuje swoje zachowanie do rozmiaru świata N , więc stara się również skalować do konfiguracji środowiska uruchomieniowego bez kompilacji dla każdej odmiany rozmiaru, chociaż decyzje dotyczące środowiska wykonawczego mogą się różnić w zależności od bezwzględnej ilości dostępnej współbieżności.

Przyjęcie MPI-2

Przyjęcie MPI-1.2 było powszechne, szczególnie w obliczeniach klastrowych, ale akceptacja MPI-2.1 była bardziej ograniczona. Kwestie obejmują:

  1. Implementacje MPI-2 obejmują I/O i dynamiczne zarządzanie procesami, a rozmiar oprogramowania pośredniczącego jest znacznie większy. Większość witryn korzystających z systemów planowania wsadowego nie obsługuje dynamicznego zarządzania procesami. Równoległe wejścia/wyjścia MPI-2 są dobrze akceptowane. [ potrzebne źródło ]
  2. Wiele programów MPI-1.2 zostało opracowanych przed MPI-2. Obawy związane z przenośnością początkowo spowolniły adopcję, chociaż szersze wsparcie to zmniejszyło.
  3. Wiele aplikacji MPI-1.2 używa tylko podzbioru tego standardu (16-25 funkcji) bez faktycznej potrzeby korzystania z funkcji MPI-2.

Przyszły

Niektóre aspekty przyszłości MPI wydają się solidne; inni mniej. Forum MPI zebrało się ponownie w 2007 r., aby wyjaśnić niektóre kwestie MPI-2 i zbadać rozwój możliwego MPI-3, co zaowocowało wersjami MPI-3.0 (wrzesień 2012) i MPI-3.1 (czerwiec 2015).

Zmieniają się architektury, z większą wewnętrzną współbieżnością ( wielordzeniowość ), lepszą precyzyjną kontrolą współbieżności (wątkowanie, powinowactwo) i większą liczbą poziomów hierarchii pamięci . Programy wielowątkowe mogą łatwiej korzystać z tych rozwiązań niż aplikacje jednowątkowe. Doprowadziło to już do powstania odrębnych, uzupełniających się standardów symetrycznego przetwarzania wieloprocesowego , a mianowicie OpenMP . MPI-2 określa, w jaki sposób implementacje zgodne ze standardami powinny radzić sobie z problemami wielowątkowymi, ale nie wymaga, aby implementacje były wielowątkowe, a nawet bezpieczne dla wątków. MPI-3 dodaje możliwość korzystania z równoległości pamięci współdzielonej w węźle. Implementacje MPI, takie jak Adaptive MPI, Hybrid MPI, Fine-Grained MPI, MPC i inne, oferują rozszerzenia standardu MPI, które odpowiadają na różne wyzwania w MPI.

Astrofizyk Jonathan Dursi napisał opinię, w której nazwał MPI przestarzałym, wskazując na nowsze technologie, takie jak język Chapel , Unified Parallel C , Hadoop , Spark i Flink . Jednocześnie prawie wszystkie projekty w Exascale Computing Project wyraźnie opierają się na MPI; Wykazano, że MPI skaluje się do największych maszyn od początku lat 20. XX wieku i powszechnie uważa się, że pozostanie aktualny przez długi czas.

Zobacz też

Dalsza lektura

Linki zewnętrzne