background image

Message   Passing   Interface  (MPI)   (z   ang.  Interfejs   Transmisji   Wiadomości)   –   protokół
komunikacyjny będący standardem przesyłania komunikatów pomiędzy procesami programów
równoległych działających na jednym lub więcej komputerach. Interfejs ten wraz z protokołem
oraz   semantyką   specyfikuje,   jak   jego   elementy   winny   się   zachowywać   w   dowolnej
implementacji.   Celami   MPI   są   wysoka   jakość,   skalowalność   oraz   przenośność.   MPI   jest
dominującym   modelem   wykorzystywanym   obecnie   w   klastrach   komputerów   oraz
superkomputerach.   Pierwsza   wersja   standardu   ukazała   się   w   maju   1994   r.   Standard   MPI
implementowany jest najczęściej w postaci bibliotek, z których można korzystać w programach
tworzonych w różnych językach programowania, np. C, C++, Ada, Fortran. 

Opis

MPI jest specyfikacją biblioteki funkcji opartych na modelu wymiany komunikatów dla potrzeb
programowania równoległego. Transfer danych pomiędzy poszczególnymi procesami programu
wykonywanymi   na   procesorach   maszyn   będących   węzłami   klastra   odbywa   się   za
pośrednictwem sieci.

Główny model MPI-1 nie wspiera koncepcji współdzielonej pamięci, MPI-2 wspiera (w sposób
ograniczony) rozproszony system pamięci dzielonej. Mimo tego programy MPI są bardzo często
uruchamiane na komputerach o współdzielonej pamięci. 

Program w MPI składa się z niezależnych procesów operujących na różnych danych (MIMD).
Każdy   proces   wykonuje   się   we   własnej   przestrzeni   adresowej,   aczkolwiek   wykorzystanie
pamięci współdzielonej też jest możliwe.

Zaletami   MPI   nad   starszymi   bibliotekami   przekazywania   wiadomości   są   przenośność   oraz
prędkość. Przenośność, ponieważ MPI został zaimplementowany dla każdej architektury opartej
na rozproszonej pamięci. Prędkość, ponieważ każda implementacja jest zoptymalizowana pod
sprzęt, na którym działa. Standard udostępnia zbiór precyzyjnie zdefiniowanych metod, które
mogą   być   efektywnie   zaimplementowane.   Stał   się   on   punktem   wyjściowym   do   stworzenia
praktycznego, przenośnego, elastycznego i efektywnego narzędzia do przesyłania komunikatów
(ang. message passing). Standard MPI pozwala na jego zastosowanie zarówno w komputerach
równoległych, jak i heterogenicznych sieciach stacji roboczych.

Standard nie zabrania, aby poszczególne procesy były wielowątkowe. Nie są też udostępnione
mechanizmy związane z rozłożeniem obciążenia pomiędzy poszczególne procesy, z architekturą
rozkładu   procesorów,   z   dynamicznym   tworzeniem   i   usuwaniem   procesów.   Procesy   są
identyfikowane poprzez ich numer w grupie w zakresie 0 .. groupsize – 1.

background image

Główne własności MPI

umożliwia   efektywną   komunikację   bez   obciążania   procesora   operacjami   kopiowania
pamięci, 

udostępnia funkcje dla języków C/C++, Fortran oraz Ada, 

specyfikacja   udostępnia   hermetyczny   interfejs   programistyczny,   co   pozwala   na
skupienie się na samej komunikacji, bez wnikania w szczegóły implementacji biblioteki i
obsługi błędów, 

definiowany interfejs zbliżony do standardów takich jak: PVM, NX czy Express, 

udostępnia mechanizmy komunikacji punkt – punkt oraz grupowej, 

może   być   używany   na   wielu   platformach,   tak   równoległych   jak   i   skalarnych,   bez
większych zmian w sposobie działania. 

Zalety MPI

dobra efektywność w systemach wieloprocesorowych, 

dobra dokumentacja, 

bogata biblioteka funkcji, 

posiada status public domain, 

przyjął się jako standard. 

Wady MPI

statyczna konfiguracja jednostek przetwarzających, 

statyczna struktura procesów w trakcie realizacji programu (dotyczy to implementacji

opartych   na   MPI-1).   Wersja   MPI-2   (wspierana   np   przez   LAM   7.0.4)   umożliwia
dynamiczne   zarządzanie   strukturą   procesów   biorących   udział   w   obliczeniach   –
MPI_Spawn(), 

brak wielowątkowości. 

Interfejs MPI ma na celu dostarczenie wirtualnej topologii, synchronizacji oraz funkcjonalności
komunikacji   pomiędzy   zestawem   procesów   (które   zostały   zmapowane   do
węzłów/serwerów/instancji   komputerów)   w   niezależny   od   języka   sposób,   przy  podobnej   do
niego   składni.   Programy   MPI   zawsze   współpracują   z   procesami,   aczkolwiek   programiści
powszechnie odnoszą się do procesów jako procesorów. Zazwyczaj dla uzyskania maksymalnej
wydajności, każdy CPU (lub rdzeń w wielordzeniowym procesorze) ma przypisany pojedynczy
proces.   Przypisanie   to   ma   miejsce   w   czasie   rzeczywistym   przez   agenta,   który   uruchamia
program MPI (zazwyczaj nazywany mpirun lub mpiexec). 

background image

Koncepcje

Komunikator

Komunikatory są obiektami łączącymi, tj. grupującymi procesy podczas sesji MPI. W obrębie
każdego komunikatora każdy zawarty proces ma niezależny identyfikator, zaś same procesy
organizowane są w uporządkowaną topologię. 

Punkt – punkt

Szereg   ważnych   funkcji   w   API   MPI   dotyczy   komunikacji   pomiędzy   dwoma   określonymi
procesami. Operacje punkt – punkt są szczególnie użyteczne w nieregularnej komunikacji, np.
w   architekturze   opartej   na   równoległości   danych,   w   której   każdy   procesor   stale   wymienia
regiony   danych   z   innymi   określonymi   procesorami   pomiędzy   kolejnymi   krokami   obliczeń.
Innym przykładem jest architektura master-slave, w której master wysyła nowe dane zadania
do slave’a niezależnie od tego, czy poprzednie zostało ukończone.

Funkcje zbiorowe

Funkcje zbiorowe w API MPI dotyczą komunikacji pomiędzy wszystkimi procesami w grupie
procesów.   Wywołania   te   są   często   używane   na   początku   lub   końcu   dużych   rozproszonych
obliczeń, gdzie każdy z procesorów operuje na części danych, po czym łączy je w wynik.

Typy danych

Wiele   funkcji   MPI   wymaga,   ażeby   określić   typ   danych,   jaki   jest   przesyłany   pomiędzy
procesorami.   Dzieje   się   tak,   ponieważ   argumentami   funkcji   są   zmienne,   nie   zaś   typy
definiowane.   Jeśli   typ   danych   jest   standardowy,   jak   np.   int,   char,  double   itd.,   można   użyć
predefiniowanych w MPI typów takich jak: MPI_INT, MPI_CHAR, MPI_DOUBLE. Dane mogą być
również w postać klas lub struktur danych. Można wykorzystywać pochodne typy danych z
typów predefiniowanych.

Komunikacja jednostronna (MPI-2)

MPI-2   definuje   trzy   operacje   jednostronnej   komunikacji:   Put,   Get   i   Accumulate,   które   to
umożliwiają zapis do zdalnej pamięci, odczyt z niej oraz zmniejszają liczbę operacji pamięci dla
wielu   zadań.   Definiowane   są   również   trzy   różne   metody   dla   synchronizacji   komunikacji   –
globalna,   parami   oraz   przy   wykorzystaniu   zdalnych   blokad   –   specyfikacja   nie   gwarantuje
jednak, iż operacje te będą wykonywane aż do momentu punktu synchronizacji.

Dynamiczne zarządzanie procesami (MPI-2)

Kluczowym aspektem dynamicznego zarządzania procesami w MPI-2 jest zdolność procesów do
tworzenia nowych procesów bądź ustanowienia komunikacji z procesami, które rozpoczęły się
oddzielnie. 

MPI I/O (MPI-2)

Równoległe I/O wprowadzone w MPI-2 jest często nazywane w skrócie MPI-IO oraz odnosi się
do zbioru funkcji przeznaczonych do ułatwienia zarządzania I/O w systemach rozproszonych w
sposób   abstrakcyjny   oraz   umożliwienia   łatwego   dostępu   do   plików   korzystając   z
funkcjonalności istniejących typów pochodnych.

background image

1. Ogólna koncepcja systemu
a) Podstawy

MPI realizuje model przetwarzania współbieżnego zwany MIMD ( Multiple Instruction Multiple
Data
),   a   dokładniej  SPMD  (Single   Program   Multiple   Data).   Zakłada   on,   że   ten   sam   kod
źródłowy   wykonuje   się   jednocześnie   na   kilku   maszynach   i   procesy   mogą   przetwarzać
równocześnie różne fragmenty danych, wymieniając informacje przy użyciu komunikatów 
Takie   podejście   ma   wiele   zalet,   z   których   najbardziej   spektakularną   jest   chyba   możliwość
współbieżnych obliczeń wykonywanych na maszynach o zupełnie różnych architekturach (np.
Linux-x86 oraz Solaris-Sparc). 

Realizując ten model, MPI umożliwia: 

Wymianę komunikatów między procesami
(Główny nacisk jest położony na wymianę danych, ale możliwe jest również wysyłanie 
komunikatów kontrolnych, czy synchronizacja procesów) 

Uzyskiwanie informacji o środowisku
(Typowy przykład to ilość aktywnych proces[-ów/-orów], czy numer aktualnego 
procesu) 

Kontrolę nad systemem
(Inicjalizacja/kończenie programu, kontrola poprawności przesyłanych komunikatów 
itp.) 

Wszystkie   te   rzeczy   są   realizowane   przy   minimalnym   stopniu   skomplikowania   kodu
źródłowego.

b) Komunikaty

Przy   przesyłaniu   komunikatów   między   procesami  MPI  stara   się   zachować   niezależność   od
platformy   (np.   kolejności   bajtów).   Dla   standardowych   typów   jest   to   proste,   natomiast   dla
typów   niestandardowych  MPI  dostarcza   funkcje   pozwalające   na   zdefiniowanie   typów
użytkownika dla potrzeb przesyłania komunikatów. 
Możliwe   jest   adresowanie   komunikatów   zarówno   do   konkretnych   procesów,   jak   i   do
określonych   grup   odbiorców.   Dostępne   są   funkcje   do   definiowania   grup   procesów   i
późniejszego   rozsyłania   komunikatów   do   tych   grup.   Komunikaty   opatrzone   są  tagami
pozwalającymi na późniejsze selektywne odbieranie ich z kolejki w zależności od rodzaju. 
Możliwa jest wymiana komunikatów w trybie  non-blocking  pozwalającym na jeszcze większe
zrównoleglenie obliczeń.

background image

c) Zaawansowana komunikacja

Główną zaletą MPI przy bardziej złożonych schematach wymiany danych jest ukrywanie przed
programistą   szczegółów   implementacyjnych   oraz   możliwość   optymalizacji   ścieżki   przepływu
danych. Wyobraźmy sobie przykładowo, że mamy 8 procesów i pierwszy z nich ma przekazać
pewną porcję danych wszystkim pozostałym. Najprostsza możliwość: 

jest   oczywiście   niezbyt   optymalna   -   widać,   że   w   ogólnym   przypadku   złożoność   czasowa
procesu przesyłania jest zależna liniowo od liczby procesów biorących udział w tej operacji. 

Kolejny przykład jest już krokiem w dobrym kierunku - przesyłanie danych trwa dwukrotnie
krócej niż poprzednio 

Oczywiście   i   tak   każdy   wie,   że   optymalny   schemat   broadcastu   danych   w   obrębie   grupy
procesów wygląda następująco :) 

jednak zaletą MPI jest fakt, że takie decyzje (dotyczące wyboru dróg przesyłania danych) są
ukryte przed programistą - może on po prostu założyć, że dane "kiedyś" i "jakoś" dotrą na
miejsce   przeznaczenia,   a "kiedy"   i  "jak"  -  decyduje   system  i  stara  się   to   zrobić  w  sposób
optymalny. 
Wszystkie te rozważania można również przeprowadzić przeprowadzić w odwrotną stronę - dla
agregacji danych z kilku procesorów. MPI definiuje zbiór funkcji służących do zbierania danych
z kilku procesów. Typowy przykład to obliczanie sumy elementów wektora na podstawie zbioru
sum częściowych (lub np. wyszukiwanie maksymalnego elementu). 

background image

2. Środowisko pracy

Z punktu widzenia programisty MPI składa się z dwóch części: 

Biblioteki (do języka C lub Fortranu) zawierającej niezbędne funkcje i pliki nagłówkowe 

Środowiska uruchamiania (runtime)

(Można tu zauważyć pewną użytkową analogię np. z Javą - program mpirun jest tu 
odpowiednikiem polecenia java) 

Najprostszy   sposób   kompilacji   programu   to   użycie   któregoś   ze   standardowych   skryptów
dostarczanych z MPI. Są to: 

mpicc - dla programów w C 

mpiCC - dla programów w C++ 

mpif77 - dla programów w Fortranie 77 

mpif90 - dla programów w Fortranie 90 

Uruchamianie programów w MPI jest równie proste i sprowadza się do uruchomienia skryptu
mpirun. Jego jedynym interesującym nas w tej chwili parametrem jest  -np <n>. Określa on
ilość równoległych procesów, jakie ma uruchomić system w celu wykonania obliczeń. 

Przed   uruchomieniem  mpirun  musimy   upewnić   się,   że   w   naszym   katalogu   domowym   na
wszystkich   maszynach   potencjalnie   mogących   wchodzić   w   grę   przy   wykonywaniu   naszego
programu   w   odpowiednim   katalogu   znajduje   się   kopia   pliku   wykonywalnego   programu.
Najczęściej w takich przypadkach stosuje się montowanie katalogów przez NFS

Szczegóły   techniczne   ukryte   za   mechanizmem   rozsyłania   procesów   na   różne   maszyny   w
zasadzie nas tu nie interesują, warto może tylko wspomnieć o ADI (Abstract Device Interface).
Jest   to   wewnętrzny   standard  mpich  określający   sposób   komunikacji   między   procesami   na
niskim poziomie. 

Zaletą   używania   takiego   modelu   jest   oddzielenie   części   systemu   zależnej   od   konkretnej
architektury, od części identycznej dla wszystkich. Wynika z tego  na przykład, że ten sam
program uruchomiony na maszynie wieloprocesorowej może używać do komunikacji pamięci
wspólnej (shared memory), a wykonywany na klastrze kilku silnych pecetów używa TCP/IP bez
rekompilacji kodu! 

background image

3. Ważniejsze funkcje MPI 

a) Podstawy

Podstawowe funkcje służące do inicjalizacji/zamykania programu oraz do wysyłania 
najprostszych komunikatów (typu Point-to-point) to: 

int MPI_Init(int *argc, char ***argv);

Funkcja inicjalizuje środowisko wykonywania programu, m.in. tworzy domyślny 
komunikator MPI_COMM_WORLD. Dopiero od momentu wywołania MPI_Init można 
używać pozostałych funkcji MPI

int MPI_Finalize();

Funkcja zwalnia zasoby używane przez MPI i przygotowuje system do zamknięcia. 

int MPI_Comm_rank(MPI_Comm comm, int *rank);

Funkcja pobiera numer aktualnego procesu (w obrębie komunikatora comm) i 
umieszcza go w zmiennej rank. 

int MPI_Comm_size(MPI_Comm comm, int *size);

Funkcja pobiera ilość procesów (w obrębie komunikatora comm i umieszcza ją w 
zmiennej size. 

int MPI_Send(void *msg, int count, MPI_Datatype datatype,

             int dest, int tag, MPI_Comm comm);

Funkcja wysyła komunikat typu datatype do procesu numer dest oznaczony 
znacznikiem tag w obrębie komunikatora comm. 

Typ komunikatu jest zawarty w zmiennej datatype i może to być któryś z 
predefiniowanych typów takich jak MPI_INT, MPI_FLOAT, MPI_DOUBLE, MPI_CHAR, lub 
inne (zob. instrukcja), jak i typy zdefiniowane przez użytkownika (o tym jeszcze 
będzie). 

Tag jest liczbą w zakresie [0..MPI_TAG_UB] i określa dodatkowy typ komunikatu 
wykorzystywany przy selektywnym odbiorze funkcją MPI_Recv. 

background image

int MPI_Recv(void *msg, int count, MPI_Datatype datatype,

             int source, int tag, MPI_Comm comm,

             MPI_Status *status);

Funkcja odczytuje z kolejki komunikatora comm (z ewentualnym blokowaniem do czasu
nadejścia) pierwszy komunikat od procesu source oznaczony znacznikiem tag typu 
datatype. Wynik umieszczany jest w buforze msg a status operacji w zmiennej status. 

Jeżeli proces ustawi source==MPI_ANY_SOURCE to odczytany będzie pierwszy 
komunikat od dowolnego procesu. Podobnie, dla tag==MPI_ANY_TAG nie będzie 
sprawdzany znacznik typu wiadomości. 

Bufor stanu status musi zostać uprzednio stworzony przez programistę. Dla C jego 
pojedynczy element składa się z trzech liczb całkowitych: MPI_SOURCE, MPI_TAG oraz 
MPI_STATUS. W ogólnym przypadku bowiem (przy odbieraniu komunikatów w których 
count>1) tablica status określa nam źródło i typ każdego komunikatu z osobna. Do 
pobierania ilości odebranych komunikatów na podstawie zmiennej stanu służy kolejna 
funkcja: 

int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count);

która umieszcza szukaną ilość w zmiennej count. 

b) Rozsyłanie/zbieranie danych

int MPI_Bcast(void *msg, int count,

              MPI_Datatype datatype, int root, MPI_Comm comm);

Funkcja rozsyła komunikat do wszystkich procesów w obrębie komunikatora comm 
poczynając od procesu root. Pozostałe argumenty - podobnie jak w MPI_Send(). 

int MPI_Reduce(void *operand, void *result,

               int count, MPI_Datatype datatype, MPI_Op op,

               int root, MPI_Comm comm);

Bardzo ważna funkcja - pozwala wykonać na przykład sumowanie wszystkich 
częściowych wyników otrzymanych w procesach i umieszczenie wyniku w zmiennej. 
Argument root wskazuje dla którego procesu wynik ma być umieszczony w zmiennej 
result. Oto przykład użycia tej funkcji: 

  MPI_Reduce(&suma_cz,&suma,1,MPI_FLOAT,MPI_SUM,0,MPI_COMM_WORLD);

Przykładowe operatory to MPI_MAX, MPI_MIN, MPI_SUM.

background image

int MPI_Allreduce(void *operand, void *result,

                  int count, MPI_Datatype datatype, MPI_Op op,

                  MPI_Comm comm);

Funkcja identyczna z poprzednią, różniąca się jedynie tym, że po jej wykonaniu wynik 
agregacji z użyciem operatora op znajduje się w zmiennej result we wszystkich 
procesach. 

int MPI_Scatter(void *send_buf, int send_count,

                MPI_Datatype send_type, void *recv_buf,

                int recv_count, MPI_Datatype recv_type,

                int root, MPI_Comm comm);

Funkcja rozproszenia ("scatter") danych między procesami. Działa w ten sposób, że 
proces root rozsyła zawartość send_buff między wszystkie procesy. Jest ona dzielona na
p segmentów, każdy składający się z send_count elementów. Pierwszy segment trafia 
do procesu 0, drugi do procesu 1 itp. Oczywiście argumenty, których nazwy zaczynają 
się na Send mają znaczenie tylko dla procesu który jest nadawcą. 

int MPI_Gather(void *send_buf, int send_count,

               MPI_Datatype send_type, void *recv_buf,

               int recv_count, MPI_Datatype recv_type,

               int root, MPI_Comm comm);

Każdy proces w grupie comm wysyła zawartość send_buff do procesu root. Ten proces 
układa przysłane dane w recv_buff w kolejności numerów procesów. 

int MPI_Allgather(void *send_buf, int send_count,

                  MPI_Datatype send_type, void *recv_buf,

                  int recv_count, MPI_Datatype recv_type,

                  MPI_Comm comm);

Funkcja ta jest analogiczna do MPI_Gather z tą różnicą, że wynik jest umieszczany w 
recv_buff każdego procesu. Można tą funkcję traktować jako ciąg kolejnych wywołań 
MPI_Gather każdorazowo z innym numerem procesu root 

background image

Zależność między trzema ostatnimi funkcjami ilustruje następujący rysunek: 

c) Struktury danych

W zastosowaniach rzeczywistych najczęściej wysyłamy większą ilość danych, bądź to w 
strukturach, albo w postaci wektorów. Przesyłanie ich w postaci oddzielnych komunikatów 
powodowałoby powstanie dużego narzutu czasowego związanego z organizacją przesyłania 
danych. Potrzebna jest więc możliwość "pakowania" danych i wysyłania większej ich ilości za 
jednym razem. 

MPI jest to dość spory problem, gdyż najczęściej typ przesyłanych danych jest różny od 
standardowych typów zdefiniowanych w bibliotece. Zdecydowano się na rozwiązanie w którym 
programista ma możliwość tworzenia nowych typów danych w czasie wykonywania programu. 

int MPI_Address(void *data, MPI_Aint *address);

Funkcja zapisuje adres zmiennej data do zmiennej address. 

int MPI_Type_struct(int count,

                    int *array_of_block_lengths,

                    MPI_Aint *array_of_displacements,

                    MPI_Datatype *array_of_types,

                    MPI_Datatype *newtype);

Funkcja tworzy nowy typ - strukturę o count składowych. Ilość elementów każdej 
składowej jest zapisana w tablicy array_of_block_lengths. Długości składowych (a 
dokładniej - offsety względem początku rekordu) należy umieścić w tablicy 
array_of_displacements. Typy pojedynczych danych składowych umieszczamy w 
array_of_types. Wynikowy typ MPI jest umieszczany w strukturze wskazywanej przez 
newtype. 

background image

int MPI_Type_vector(int count, int block_length,

                    int stride, MPI_Datatype element_type,

                    MPI_Datatype *newtype);

Funkcja tworzy nowy typ - wektor długości count. Każdy element wektora zawiera 
block_length elementów typu element_type, elementy wektora są zaś rozdzielone 
dodatkowo stride elementami tego typu. 

int MPI_Type_contiguous(int count, MPI_Datatype oldtype,

                        MPI_Datatype *newtype);

Tworzenie prostszego (jednowymiarowego) wektora elementów typu oldtype długości 
count. 

int MPI_Type_indexed(int count,

                     int *array_of_block_lengths,

                     int *array_of_displacements,

                     MPI_Datatype element_type,

                     MPI_Datatype *newtype);

Jest to prostsza wersja funkcji MPI_Type_struct. Różni się tym, że wszystkie elementy 
struktury mają ten sam typ bazowy (element_type) i mogą być przesunięte względem 
początku o zadaną (tablica array_of_displacements) ilość elementów. 

int MPI_Type_commit(MPI_Datatype *newtype);

Tą funkcję należy wywołać po zdefiniowaniu nowego typu przy użyciu którejś z 
poprzednich funkcji - dopiero wtedy zacznie on być widziany przez MPI

d) Grupowanie danych

Istnieje również prostsza metoda wysyłania większej ilości danych za jednym zamachem - 
polega ona na tym, że przed każdą operacją typu MPI_Send() ręcznie pakujemy wszystkie 
dane do jednego obszaru pamięci, a druga strona po odebraniu komunikatu może je 
rozpakować w analogiczny sposób. Odpowiednie funkcje mają następującą postać: 

int MPI_Pack(void *pack_data, int in_count,

             MPI_Datatype datatype, void *buffer,

             int size, int *position_ptr, MPI_Comm comm);

Parametr pack_data powinien zawierać in_count elementów typu datatype. Parametr 
position_ptr zawiera adres początkowy w buforze wyjściowym buffer i po wykonaniu 
funkcji jego wartość jest uaktualniana (dodawana jest długość zapisanych danych). 
Parametr size określa rozmiar obszaru pamięci wskazywanej przez buffer. 

background image

int MPI_Unpack(void *buffer, int size,

               int *position_ptr, void *unpack_data, int count,

               MPI_Datatype datatype, MPI_Comm comm);

Funkcja odwrotna do poprzedniej - rozpakowuje count elementów typu datatype ze 
zmiennej buffer począwszy od pozycji position do obszaru pamięci wskazywanego przez 
unpack_data i odpowiednio uaktualnia zmienną position. 

e) Synchronizacja

Podstawową (i najczęściej stosowaną) funkcją MPI służącą do synchronizacji procesów jest 

int MPI_Barrier(MPI_Comm comm);

Wywołanie jej w pewnym miejscu programu powoduje, będzie on czekał, aż wszystkie 
pozostałe jego instancje dojdą do tego miejsca i dopiero potem ruszy dalej. 

Komunikacja non-blocking

MPI umożliwia, oprócz klasycznej komunikacji typu MPI_Send/ MPI_Recv również przesyłanie 
wiadomości bez blokowania programu. W skrócie wygląda to tak, że po wywołaniu funkcji 
MPI_Isend program może wykonywać inne czynności (oczywiście nie zmieniające bufora 
wysyłanego komunikatu), natomiast w chwili, gdy chce się upewnić, że komunikat został już 
wysłany w całości, wywołuje MPI_Wait, lub MPI_Test. 

W podobny sposób realizowany jest odbiór komunikatów bez blokowania - analogiczna funkcja 
nosi nazwę MPI_Irecv. 


Document Outline