Upload
fern
View
38
Download
2
Embed Size (px)
DESCRIPTION
Kolejność odbieranych wiadomości. Zgodność typów przysyłanych danych MPI_BYTE też musi być zgodny u nadawcy i odbiorcy wyjątek MPI_PACKED automatyczna konwersja w środowisku heterogenicznym, ale nie zdefiniowana pomiędzy językami np. C-Fortran Zgodność identyfikatorów - PowerPoint PPT Presentation
Citation preview
Kolejność odbieranych wiadomości● Zgodność typów przysyłanych danych
– MPI_BYTE też musi być zgodny u nadawcy i odbiorcy – wyjątek MPI_PACKED– automatyczna konwersja w środowisku heterogenicznym, ale
nie zdefiniowana pomiędzy językami np. C-Fortran● Zgodność identyfikatorów
– tag= 0...MPI_TAG_UB>=32767● Wielkość odbieranej wiadomości nie musi być zgodna
z rozmiarem bufora odbiorcy (błąd w przypadku przepełnienia); MPI_PROBE pozwala sprawdzić wielkośćwiadomości bez jej odbierania
Poprawny kod
CALL MPI_COMM_RANK(comm, rank, ierr) IF(rank.EQ.0) THEN
CALL MPI_SEND(a(1), 10, MPI_REAL, 1, tag, comm, ierr) ELSE
CALL MPI_RECV(b(1), 15, MPI_REAL, 0, tag, comm, status, ierr) END IF
Błędny kod
CALL MPI_COMM_RANK(comm, rank, ierr) IF(rank.EQ.0) THEN
CALL MPI_SEND(a(1), 10, MPI_REAL, 1, tag, comm, ierr) ELSE
CALL MPI_RECV(b(1), 40, MPI_BYTE, 0, tag, comm, status, ierr) END IF
MPI_PACK i MPI_UNPACK
● Funkcja MPI_PACK umieszcza dane różnych typów w buforze zdefiniowanym przez użytkownika który pózniej można przesłać korzystając z typu MPI_PACKED
● Wiadomość o dowolnym typie można wysłać/odebrać korzystając z typu MPI_PACKED
● Funkcja MPI_UNPACK umieszcza odebrane dane typu MPI_PACKED w zmiennej określonego typu
MPI_PACK(inbuf, incount, datatype, outbuf, outsize, position, comm)
[ IN inbuf] zmienna zawierając dane wejściowe [ IN incount] ilość danych wejsciowych[ IN datatype] typ danych wejściowych[ OUT outbuf] bufor danych [ IN outsize] rozmiar bufora danych w bajtach[ INOUT position] bierząca pozycja w buforze w bajtach [ IN comm] komunikator
int MPI_Pack(void* inbuf, int incount, MPI_Datatype datatype, void *outbuf, int outsize, int *position, MPI_Comm comm)
MPI_PACK(INBUF, INCOUNT, DATATYPE, OUTBUF, OUTSIZE, POSITION, COMM, IERROR)
<type> INBUF(*), OUTBUF(*)INTEGER INCOUNT, DATATYPE, OUTSIZE, POSITION, COMM, IERROR
MPI_UNPACK(inbuf, insize, position, outbuf, outcount, datatype, comm)
[ IN inbuf] bufor zawierając dane wejściowe [ IN insize] rozmiar bufora w bajtach[ INOUT position] bierząca pozycja w buforze w bajtach [ OUT outbuf] zmienna dla danych wyjściowych[ IN outcount] ilość danych wyjściowych[ IN datatype] typ danych wyjściowych[ IN comm] komunikator
int MPI_Unpack(void* inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm)
MPI_UNPACK(INBUF, INSIZE, POSITION, OUTBUF, OUTCOUNT, DATATYPE, COMM, IERROR)
<type> INBUF(*), OUTBUF(*) INTEGER INSIZE, POSITION, OUTCOUNT, DATATYPE, COMM, IERROR
MPI_PACK_SIZE(incount, datatype, comm, size)
[ IN incount] ilość danych wejściowych dla MPI_PACK [ IN datatype] typ danych wejściowych [ IN comm] komunikator [ OUT size] potrzebny rozmiar bufora w MPI_PACK w bajtach
int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size)
MPI_PACK_SIZE(INCOUNT, DATATYPE, COMM, SIZE, IERROR)INTEGER INCOUNT, DATATYPE, COMM, SIZE, IERROR
int position, i, j, a[2]; char buff[1000]; .... MPI_Comm_rank(MPI_COMM_WORLD, &myrank); if (myrank == 0) {
/ * SENDER CODE */ position = 0; MPI_Pack(&i,1,MPI_INT,buff,1000,&position,MPI_COMM_WORLD); MPI_Pack(&j,1,MPI_INT,buff,1000,&position,MPI_COMM_WORLD); MPI_Send(buff, position, MPI_PACKED, 1, 0, MPI_COMM_WORLD); }
else /* RECEIVER CODE */
MPI_Recv( a, 2, MPI_INT, 0, 0, MPI_COMM_WORLD) }
MPI_CHARACTER●W Fortranie ciąg znaków jest zmienna o stałej dlugości bez specjałnego znaku zakończenia
●MPI_CHARACTER odpowiada pojedynczemy znakowi a nie ciągowi znaków CHARACTER*n
CHARACTER*10 a CHARACTER*10 b CALL MPI_COMM_RANK(comm, rank, ierr) IF(rank.EQ.0) THEN
CALL MPI_SEND(a, 5, MPI_CHARACTER, 1, tag, comm, ierr)ELSE
CALL MPI_RECV(b(6:10), 5, MPI_CHARACTER, 0, tag, comm, status, ierr)
END IF
Tryby i rodzaje komunikacji punktowej
– Standardowy (Standard) MPI_Send MPI_Isend
MPI_RecvMPI_Irecv
– Synchroniczny(Synchronous) MPI_Ssend MPI_Issend
– Buforowany (Buffered) MPI_Bsend MPI_Ibsend
– Gotowości (Ready) MPI_Rsend MPI_Irsend
Wstrzymująca(blocking)
Niewstrzymująca(nonblocking)
Rodzaje komunikacji
Tryby :
● Działanie funkcji biblioteki MPI jest zdefiniowane przez specyfikację MPI Standard 1.1 i 2.0 a nie przez konkretną implementację
● Implementacje mogą się różnić w szczegółach nie zdefiniowanych przez MPI Standard, co pozwala na dostosowanie implementacji do sprzętu (np. MPICHG2 jest dostosowany do pracy w rozproszonym środowisku GRID)
● Nie należy korzystać z cech obecnych jedynie w konkretnej implementacji przy tworzeniu przenośnych kodów równoległych
Tryb standardowy
● Specyfikacja nie definiuje czy komunikacja bedzie buforowana czy nie
● Charakter nielokalny - zakończenie może zależeć od odbiorcy
● Większość implementacji zapewnia buforowanie w zależności od rozmiaru wiadomości
● Przenośne programy nie powinny bazować na buforowaniu w trybie standardowym
Tryb buforowany
● Bufor u nadawacy jest definiowany przez programistę MPI_BUFFER_ATTACH
● Charakter lokalny - zakończenie zależy jedynie od odbiorcy
● Może doprowadzić do podwójnego buforowania i związanych z tym dodatkowych operacji dostępu do pamięci
● MPI_PACK_SIZE + MPI_BSEND_OVERHEAD
MPI_BUFFER_ATTACH( buffer, size) [ IN buffer] początkowy adres bufora[ IN size] rozmiar bufora w bajtach
int MPI_Buffer_attach( void* buffer, int size)
MPI_BUFFER_ATTACH( BUFFER, SIZE, IERROR)<type> BUFFER(*) INTEGER SIZE, IERROR
MPI_BUFFER_DETACH( buffer_addr, size) [ OUT buffer_addr] początkowy adres bufora[ OUT size] rozmiar bufora w bajtach
int MPI_Buffer_detach( void* buffer_addr, int* size)
MPI_BUFFER_DETACH( BUFFER_ADDR, SIZE, IERROR)<type> BUFFER_ADDR(*) INTEGER SIZE, IERROR
Tryb synchroniczny
● Brak buforowania u nadawacy ● Charakter nielokalny - zakończenie zależy
zawsze od odbiorcy● Zakończenie działania u nadawcy gwarantuje że
odbiorca rozpoczął przyjmowanie wiadomości● W przypadku komunikacji blokującej u nadawcy
i odbiorcy zapewnia pełną synchronizację komunikacji
Tryb gotowości
● Może się rozpocząć tylko jeśli odbiorca już czeka na wiadomość, w innym przypadku zachowanie jest nie zdefiniowane przez specyfikację
● Charakter lokalny - zakończenie operacji nie gwarantuje ze wiadomość jest odebrana
● Pozwala zaoszczędzić część operacji synchronizacji (handshaking)
● W poprawnym programie zastapienie MPI_Rsend przez MPI_Send nie powinno wpłynąć na efekt działania poza szybkością komunikacji
Przykładowa implementacja
● Tryb gotowości - wysyłanie natychmiastowe● Tryb synchroniczny - nadawca wysyła najpierw
prośbę o potwierdzenie gotowości odbiorcy● Tryb standardowy - dla małych wiadomości
wysyłanie natychmiastowe z buforowaniem na poziomie sprzętu a dla większych jak w trybie synchronicznym
● Tryb buforowany - skopiowanie wiadomości do bufora i wykonanie wysyłania nieblokującego
CALL MPI_COMM_RANK(comm, rank, ierr) IF (rank.EQ.0) THEN
CALL MPI_BSEND(buf1, count, MPI_REAL, 1, tag, comm, ierr) CALL MPI_BSEND(buf2, count, MPI_REAL, 1, tag, comm, ierr)
ELSE ! rank.EQ.1 CALL MPI_RECV(buf1, count, MPI_REAL, 0, MPI_ANY_TAG, comm,
status, ierr) CALL MPI_RECV(buf2, count, MPI_REAL, 0, tag, comm, status, ierr)
END IF
CALL MPI_COMM_RANK(comm, rank, ierr) IF (rank.EQ.0) THEN
CALL MPI_BSEND(buf1, count, MPI_REAL, 1, tag1, comm, ierr) CALL MPI_SSEND(buf2, count, MPI_REAL, 1, tag2, comm, ierr)
ELSE ! rank.EQ.1 CALL MPI_RECV(buf1, count, MPI_REAL, 0, tag2, comm, status, ierr) CALL MPI_RECV(buf2, count, MPI_REAL, 0, tag1, comm, status, ierr)
END
include 'mpif.h' integer ierr,rank,size,req(4),stat(MPI_STATUS_SIZE) integer i,n double precision pi parameter(pi=3.1415, n=10) double precision x(n),dx,y(n),right,left
call MPI_INIT( ierr ) call MPI_COMM_RANK( MPI_COMM_WORLD, rank, ierr ) call MPI_COMM_SIZE( MPI_COMM_WORLD, size, ierr )
dx=2*pi/(n*size) do i=1,n x(i)=cos(n*dx*rank+i*dx) enddo
iright=mod(size+rank+1,size) ileft=mod(size+rank-1,size)
call MPI_SEND(x(1),1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, ierr) call MPI_SEND(x(n),1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, ierr) call MPI_RECV(left,1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, stat, ierr) call MPI_RECV(right,1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, stat, ierr)
do i=2,n-1 y(i)=(x(i-1)-2*x(i)+x(i+1))/(dx*dx) enddo
y(1)=(left-2*x(1)+x(2))/(dx*dx) y(n)=(x(n-1)-2*x(n)+right)/(dx*dx)
do i=1,n print *,rank,n*dx*rank+i*dx,x(i),y(i) enddo
call MPI_FINALIZE(ierr) stop end
Pierwsza wersja kodu równoległego – prosta ale nie spełnia wymogów bezpieczeństwa- opiera się na buforowaniu dwu instrukcji MPI_SEND
double precision x(n),dx,y(n),right,left,bufor(100).....
call MPI_BUFFER_ATTACH(bufor,100,ierr)..... call MPI_BSEND(x(n),1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, ierr)
call MPI_BSEND(x(1),1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, ierr)
call MPI_RECV(left,1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, stat, ierr)
call MPI_RECV(right,1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, stat, ierr)
Wersja bezpieczna – buforowanie danych w MPI_BSEND
call MPI_SSEND(x(n),1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, ierr) call MPI_RECV(left,1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, stat, ierr) call MPI_SSEND(x(1),1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, ierr) call MPI_RECV(right,1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, stat, ierr)
Wersja bardziej bezpieczna – odpowiednia kolejność wysyłania i odbioru lecz nie zadziała w trybie synchronicznym (deadlock)
if (mod(rank,2).eq.0) then call MPI_SSEND(x(n),1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, ierr) call MPI_RECV(left,1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, stat, ierr) call MPI_SSEND(x(1),1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, ierr) call MPI_RECV(right,1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, stat, ierr)else call MPI_RECV(left,1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, stat, ierr) call MPI_SSEND(x(n),1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, ierr) call MPI_RECV(right,1,MPI_DOUBLE_PRECISION, iright, & 1, MPI_COMM_WORLD, stat, ierr) call MPI_SSEND(x(1),1,MPI_DOUBLE_PRECISION, ileft, & 1, MPI_COMM_WORLD, ierr)endif
Tryb gotowości
Gorąca linia (persistent communication)
● Pozwala na dodatkową optymalizację przesyłania danych jeśli lista argumentów jest taka sama
● Rozdzielenie inicjalizacji gorącej linii (funkcja MPI_SEND_INIT i MPI_RECV_INIT) od samego przesyłania danych MPI_START
● Wyłączenie gorącej linii (MPI_REQUEST_FREE) jest możliwe po zakończeniu komunikacji
● Możliwe jest użycie różnych trybów komunikacji ● Gorąca może współpracować ze zwyczajnymi
instrukcjami komunikacji punktowej
MPI_SEND_INIT(buf, count, datatype, dest, tag, comm, request) [ IN buf] zmienna do wysłania [ IN count] ilość elementów do wysłania [ IN datatype] typ elementów [ IN dest] odbiorca [ IN tag] identyfikator wiadomości [ IN comm] komunikator [ OUT request] wskaźnik pozwalający na dowołanie się do tej instrukcji
int MPI_Send_init(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request)
MPI_SEND_INIT(BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR)<type> BUF(*) INTEGER REQUEST, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR
MPI_RECV_INIT( buf, count, datatype, source, tag, comm, request) [ OUT buf] zmienna dla odbieranych danych [ IN count] ilość elementów [ IN datatype] typ danych [ IN source] nadawca[ IN tag] identyfikator wiadomości [ IN comm] komunikator [ OUT request] wskaźnik pozwalający na dowołanie się do tej instrukcji
int MPI_Recv_init(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request)
MPI_RECV_INIT(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, REQUEST, IERROR)<type> BUF(*) INTEGER COUNT, DATATYPE, SOURCE, TAG, COMM, REQUEST, IERROR
MPI_START(request) [ INOUT request] wskaźnik do instrukji inicjalizującej gorąca linię
int MPI_Start(MPI_Request *request)
MPI_START(REQUEST, IERROR)INTEGER REQUEST, IERROR
MPI_STARTALL( count, array_of_requests) [ IN count] wielkość tablicy ze wskaźnikami [ INOUT array_of_requests] tablica ze wskaźnik do instrukji
inicjalizującej gorąca linię
int MPI_Startall(int count, MPI_Request *array_of_requests)
MPI_STARTALL(COUNT, ARRAY_OF_REQUESTS, IERROR)INTEGER COUNT, ARRAY_OF_REQUESTS(*), IERROR