1 / 26

Kolejność odbieranych wiadomości

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

fern
Download Presentation

Kolejność odbieranych wiadomości

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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

  2. 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

  3. 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

  4. 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

  5. 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

  6. 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

  7. 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) }

  8. 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

  9. Tryby i rodzaje komunikacji punktowej Rodzaje komunikacji Wstrzymująca (blocking) Niewstrzymująca (nonblocking) Tryby : • Standardowy (Standard)MPI_SendMPI_Isend MPI_RecvMPI_Irecv • Synchroniczny(Synchronous)MPI_SsendMPI_Issend • Buforowany (Buffered)MPI_BsendMPI_Ibsend • Gotowości (Ready) MPI_RsendMPI_Irsend

  10. 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

  11. 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

  12. 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

  13. 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

  14. 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

  15. 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

  16. 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

  17. 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

  18. Pierwsza wersja kodu równoległego – prosta ale nie spełnia wymogów bezpieczeństwa- opiera się na buforowaniu dwu instrukcjiMPI_SEND 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

  19. Wersja bezpieczna – buforowanie danych w MPI_BSEND 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)

  20. Wersja bardziej bezpieczna – odpowiednia kolejność wysyłania i odbioru lecz nie zadziała w trybie synchronicznym (deadlock) 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)

  21. 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

  22. Tryb gotowości

  23. 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

  24. 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

  25. 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

  26. 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

More Related