Definiowanie typów danych użytkownika Każdy typ danych w MPI jest określony przez tablice typemap podającą dla każdego elementu parami typ podstawowy i przesunięcie w bajtach Typemap={(type 0,disp 0 ),...,(type n-1,disp n-1 )} przykład MPI_INT(int,0)
Podstawowe typy danych w MPI: przypomnienie
Definicje dolnej i górnej granicy oraz rozpietości i rozmiaru typu danych lb(Typemap) = min j (disp j ) ub(Typemap) = max j (disp j +sizeof(type j ))+pad extent(Typemap) = ub(Typemap)- lb(Typemap) „pad” określa poprawkę ze wzgędu na rozmieszczenie danych w pamięci komputera, w większosci przypadków wymagane jest aby dane określonego typu były umieszczone pod adresami będącymi wielokrotnością rozmiaru danego typu, np. int zajmuje 4 bajty więc adres powinien byc podzielny przez 4, więc dla {(int,0),(char,4)} lb=min(0,4)=0 ub=max(0+4,4+1)+pad=5+pad=8 extent=5+pad=8rozmiar=5
MPI_TYPE_EXTENT(datatype, extent) [ IN datatype] typ danych [ OUT extent] rozpiętość typu danych int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint *extent) MPI_TYPE_EXTENT(DATATYPE, EXTENT, IERROR) INTEGER DATATYPE, EXTENT, IERROR MPI_TYPE_SIZE(datatype, size) [ IN datatype] typ danych [ OUT size] rozmiar typu danych w bajtach – ilość bajtow buforowanych/przesyłanych int MPI_Type_size(MPI_Datatype datatype, int *size) MPI_TYPE_SIZE(DATATYPE, SIZE, IERROR) INTEGER DATATYPE, SIZE, IERROR
MPI_TYPE_LB( datatype, displacement) [ IN datatype] typ danych [ OUT displacement] pozycja dolnej granicy w bajtach względem początku obszaru danych int MPI_Type_lb(MPI_Datatype datatype, MPI_Aint* displacement) MPI_TYPE_LB( DATATYPE, DISPLACEMENT, IERROR) INTEGER DATATYPE, DISPLACEMENT, IERROR MPI_TYPE_UB( datatype, displacement) [ IN datatype] typ danych [ OUT displacement] pozycja górnej granicy w bajtach względem początku obszaru danych int MPI_Type_ub(MPI_Datatype datatype, MPI_Aint* displacement) MPI_TYPE_UB( DATATYPE, DISPLACEMENT, IERROR) INTEGER DATATYPE, DISPLACEMENT, IERROR
MPI_ADDRESS(location, address) [ IN location] zmienna [ OUT address] adres zmiennej w bajtach int MPI_Address(void* location, MPI_Aint *address) MPI_ADDRESS(LOCATION, ADDRESS, IERROR) LOCATION(*) INTEGER ADDRESS, IERROR Uwaga: integer w f77 jest 32bitowy i nie może reprezentować adresu dla maszyn 64bitowych integer ierror integer (kind=MPI_ADDRESS_KIND) iadd double precision a(10000) call MPI_Address( a, iadd, ierror)
Tworzenie typów danych użytkownika Contiguous - najprostszy typ pochodny; kolejne elementy są kopiami danego typu wejściowego a ich pozycje są wielokrotnościami jego rozpiętości. Vector – elementy są kopiami danego typu wejściowego ale pomiędzy nimi występują odstępy będące wielokrotnościami rozpiętości tego typu. Hvector – jak Vector ale odstępy między elementami są określone w bajtach. Indexed – jak Vector ale odstępy między elementami są określone dowolnie przez tablicę przesunięć. Hindexed – jak Indexed ale elementy tablicy przesunięć są podane w bajtach. Struct - najbardzięj ogólny typ, elementy nie muszą być jednakowego typu a odstępy między nimi są określone przez tablicę przesunięć, której elementy są podane w bajtach.
MPI_TYPE_CONTIGUOUS(count, oldtype, newtype) oldtype {(int,0),(double,8)} MPI_Type_contiguous (2, oldtype, &newtype) newtype {(int,0),(double,8),(int,16),(double,24)}
MPI_TYPE_VECTOR(count, blocklength, stride, oldtype, newtype) MPI_TYPE_HVECTOR(count, blocklength, stride_bytes, oldtype, newtype)
MPI_TYPE_INDEXED(count,array_of_blocklengths,array_of_displacements, oldtype, newtype) MPI_TYPE_HINDEXED(count,array_of_blocklengths,array_of_displacements_bytes, oldtype,newtype)
MPI_TYPE_STRUCT(count, array_of_blocklengths, array_of_displacements_bytes, array_of_types, newtype)
Uaktywnienie nowozdefiniowanego typu MPI i zwalnianie typu: MPI_TYPE_COMMIT(type) MPI_TYPE_FREE(type) type – nowozdefiniowany lub zwalniany typ C: MPI_Type_commit(&type); MPI_Type_free(&type); MPI_TYPE type Fortran 77: CALL MPI_TYPE_COMMIT(TYPE,IERROR) CALL MPI_TYPE_FREE(TYPE,IERROR) INTEGER TYPE,IERROR
Wprowadzania “przerw” między elementami typów od Vector włącznie: uwagi Przerwy wskazują co jest pomijane podczas pakowania tablicy lub struktury do bufora. W związku z powyższym, zdefiniowanie typu Vector umożliwia w Fortranie 77 przesłanie całej interesującej części tablicy w jednej instruktji (pamiętamy, że wymiary trzeba tam definiować zawsze “na wyrost”), natomiast HVector i Struct umożliwiają przesłanie wybranych elementów tablic, obszarów wspólnych i struktur bez pisania oddzielnego SEND i RECEIVE dla każdego z nich.
przesyłanie górnego trójkąta macierzy NxN double a[100][100] disp[100], blocklenp[100],i; MPI_Datatype upper; for (i=0; i<100; ++i) { disp[i] = 100 * i + i; blocklen[i] = i; } MPI_Type_indexed(100, blocklen, disp, MPI_DOUBLE, &upper); MPI_Type_commit(&upper);.... MPI_Send(a, 1, upper, dest, tag, MPI_COMM_WORLD);
przesyłanie struktury struct Partstruct { char class; double d[6]; char b[7]; } struct Partstruct particle[1000]; int i,dest,rank; MPI_Comm comm; MPI_Datatype Particletype; MPI_Datatype type[3] = {MPI_CHAR, MPI_DOUBLE, MPI_CHAR}; int blocklen[3] = {1, 6, 7}; /* double-word aligned */ MPI_Aint disp[3] = {0, sizeof(double), 7*sizeof(double)}; /* single-word aligned */ MPI_Aint disp[3] = {0, sizeof(int), sizeof(int)+6*sizeof(double)}; MPI_Type_struct(3, blocklen, disp, type, &Particletype); MPI_Type_commit(&Particletype);... MPI_Send(particle, 1000, Particletype, dest, tag, comm);
przesyłanie struktury struct Partstruct { char class; double d[6]; char b[7]; } struct Partstruct particle[1000]; int i,dest,rank; MPI_Comm comm; MPI_Datatype Particletype; MPI_Datatype type[3] = {MPI_CHAR, MPI_DOUBLE, MPI_CHAR}; int blocklen[3] = {1, 6, 7}; /* machine independency */ MPI_Aint disp[3]; MPI_Address(particle, &disp[0]); MPI_Address(particle[0].d, &disp[1]); MPI_Address(particle[0].b, &disp[2]); for (i=2; i >=0; i--) disp[i] -= disp[0]; MPI_Type_struct(3, blocklen, disp, type, &Particletype); MPI_Type_commit(&Particletype);... MPI_Send(particle, 1000, Particletype, dest, tag, comm);
przesyłanie zawartości COMMON PARAMETER(NBLOCKS = 2) INTEGER array_of_displacements(NBLOCKS) INTEGER array_of_addresses(NBLOCKS) INTEGER array_of_types(NBLOCKS) INTEGER array_of_blocklenghts(NBLOCKS) DOUBLE PRECISION results(RMAX) PARAMETER (RMAX=3) COMMON /resultPacket/ nResults, results array_of_blocklenghts(1) = 1 array_of_blocklenghts(2) = RMAX CALL MPI_ADDRESS(nResults, array_of_addresses(1), ierr) CALL MPI_ADDRESS(results, array_of_addresses(2), ierr) array_of_displacements(1)=0 array_of_displacements(2)=array_of_addresses(2)-array_of_addresses(1) array_of_types(1) = MPI_INTEGER array_of_types(2) = MPI_DOUBLE_PRECISION CALL MPI_TYPE_STRUCT (NBLOCKS, & array_of_blocklenghts, & array_of_displacements, & array_of_types, & resultPacketType, ierr) CALL MPI_TYPE_COMMIT (resultPacketType, ierr)... count = 1 CALL MPI_SSEND (nResults, count, resultPacketType, & dest, tag, comm, ierr)
Przykład przesyłania wybranych elementów tablicy struktur: przesyłanie informacji o cząsteczkach, które opuściły daną komórkę
typedef struct{ double x,y,z; double mass; } Particle; Particle myparticles[MAX_PARTICLES], newparticles[MAX_PARTICLES]; MPI_Type_contiguous (4, MPI_DOUBLE,&particletype) MPI_Type commit(&particletype) n_to_move = 0; for (i=0;i<count;i++) { if (...particle exited cell...) { elmoffset[n_to_move] = i; elmsize[n_to_move] =1; n_to_move++; } } MPI_Type_indexed( n_to_move, elmsize, elmoffset, particletype, &sendtype); MPI_Type_commit (&sendtype); MPI_Send( myparticles,1,sendtype,dest,tag,comm); MPI_Type_free( &sendtype);.... MPI_Recv( newparticles,MAX_PARTICLES, particletype,source,tag,comm, &status); MPI_Get_count( &status, particletype, &number);
Dynamiczna obsługa pamięci MPI_Probe( source, tag, comm, &status); MPI_Get_count( &status, particletype, &number); MPI_Type_extent( particletype, &extent); Newparticles = (Particle *)malloc( number * extend); MPI_Recv( newparticles, number, particletype, source, tag, comm, &status); Kompletny zestaw procedur w C do tworzenia, uaktualniania i przesyłania list cząstek
Równoległy algorytm mnożenia macierzy (więcej w rozdziale 6.8rozdziale 6.8 MPI: The Complete Reference)
SUBROUTINE PMATMULT( A, B, C, n, p, comm) INTEGER n(3) REAL A(n(1),n(2)), B(n(2),n(3)), C(n(1),n(3)) INTEGER p(2) INTEGER comm INTEGER nn(2) REAL, ALLOCATABLE AA(:), BB(:), CC(:,:) INTEGER comm_2D, comm_1D(2), pcomm INTEGER coords(2) INTEGER rank INTEGER, ALLOCATABLE dispc(:), countc(:) INTEGER typea, typec, types(2), blen(2), disp(2) INTEGER ierr, i, j, k, sizeofreal LOGICAL periods(2), remains(2) CALL MPI_COMM_DUP( comm, pcomm, ierr) CALL MPI_BCAST( n, 3, MPI_INTEGER, 0, pcomm, ierr) CALL MPI_BCAST( p, 2, MPI_INTEGER, 0, pcomm, ierr) periods = (/.FALSE.,.FALSE./) CALL MPI_CART_CREATE( pcomm, 2, p, periods,.FALSE., comm_2D, ierr) CALL MPI_COMM_RANK( comm_2D, rank, ierr) CALL MPI_CART_COORDS( comm_2D, rank, 2, coords, ierr)
! compute communicators for subspaces DO i = 1, 2 DO j = 1, 2 remains(j) = (i.EQ.j) END DO CALL MPI_CART_SUB( comm_2D, remains, comm_1D(i), ierr) END DO nn(1) = n(1)/p(1) nn(2) = n(3)/p(2) ALLOCATE (AA(nn(1),n(2)), BB(n(2),nn(2)), CC(nn(1),nn(2)))
IF (rank.EQ.0) THEN ! compute datatype for slice of A CALL MPI_TYPE_VECTOR( n(2), nn(1), n(1), MPI_REAL,types(1), ierr) ! and correct extent to size of subcolumn so that ! consecutive slices be "contiguous" CALL MPI_TYPE_EXTENT( MPI_REAL, sizeofreal, ierr) blen = (/ 1, 1 /) disp = (/ 0, sizeofreal*n(1) /) types(2) = MPI_UB CALL MPI_TYPE_STRUCT( 2, blen, disp, types, typea, ierr) CALL MPI_TYPE_COMMIT( typea, ierr) ! compute datatype for submatrix of C CALL MPI_TYPE_VECTOR( nn(2), nn(1), n(1), MPI_REAL,types(1), ierr) ! and correct extent to size of subcolumn CALL MPI_TYPE_STRUCT(2, blen, disp, types, typec, ierr) CALL MPI_TYPE_COMMIT(typec, ierr) ! compute number of subcolumns preceeding each successive ! submatrix of C. Submatrices are ordered in row-major ! order, to fit the order of processes in the grid. ALLOCATE (dispc(p(1)*p(2)), countc(p(1)*p(2))) DO i = 1, p(1) DO j = 1, p(2) dispc((i-1)*p(2)+j) = ((j-1)*p(1) + (i-1))*nn(2) countc((i-1)*p(2)+j) = 1 END DO END IF
! 1. scatter row slices of matrix A on x axis IF (coords(2).EQ.0) THEN CALL MPI_SCATTER(A, 1, typea, AA, nn(1)*n(2), MPI_REAL, 0, comm_1D(1), ierr) END IF ! 2. scatter column slices of matrix B on y axis IF (coords(1).EQ.0) THEN CALL MPI_SCATTER(B, n(2)*nn(2), MPI_REAL, BB, n(2)*nn(2), MPI_REAL, 0, comm_1D(2), ierr) END IF ! 3. broadcast matrix AA in y dimension CALL MPI_BCAST(AA, nn(1)*n(2), MPI_REAL, 0, comm_1D(2)) ! 4. broadcast matrix BB in x dimension CALL MPI_BCAST(BB, n(2)*nn(2), MPI_REAL, 0, comm_1D(1)) ! 5. compute submatrix products DO j = 1, nn(2) DO i = 1, nn(1) CC(i,j) = 0 DO k = 1, n(2) CC(i,j) = CC(i,j) + AA(i,k)*BB(k,j) END DO
! 6. gather results from plane to node 0 CALL MPI_GATHERV( CC, nn(1)*nn(2),MPI_REAL, C, countc, dispc, typec, 0, comm_2D, ierr) DEALLOCATE(AA, BB, CC) MPI_COMM_FREE( pcomm, ierr) MPI_COMM_FREE( comm_2D, ierr) DO i = 1, 2 MPI_COMM_FREE( comm_1D(i), ierr) END DO IF (rank.EQ.0) THEN DEALLOCATE(countc, dispc) MPI_TYPE_FREE( typea, ierr) MPI_TYPE_FREE( typec, ierr) MPI_TYPE_FREE( types(1), ierr) END IF RETURN END
Definiowanie, inicjalizacja i zwalnianie własnych operatorów dla MPI_REDUCE MPI_Op_create (MPI_User_function *function, int commute, MPI_Op *op) MPI_OP_CREATE (USER_FUNCTION, COMMUTE, OP, IERROR) EXTERNAL USER_FUNCTION LOGICAL COMMUTE INTEGER OP, IERROR typedef void MPI_User_function (void *invec, void *inoutvec, int *len, MPI_Datatype *datatype); FUNCTION USER_FUNCTION( INVEC(*), INOUTVEC(*), LEN, TYPE) INVEC(LEN), INOUTVEC(LEN) INTEGER LEN, TYPE MPI_op_free (MPI_Op *op) MPI_OP_FREE (OP, IERROR)
mnożenie liczb zespolonych typedef struct { double real,imag; } Complex; void myProd( Complex *in, Complex *inout, int *len, MPI_Datatype *dtpr) { int i; Complex c; for (i=0; i< *len; i++) { c.real = inout->real * in->real - inout->imag * in->imag; c.imag = inout->real * in->imag + inout->imag * in->real; *inout = c; in++; inout++; }
Complex a[100], answer[100]; MPI_Op myOp; MPI_Datatype ctype; MPI_Type_contiguous( 2, MPI_DOUBLE, &ctype ); MPI_Type_commit( &ctype ); MPI_Op_create( myProd, True, &myOp ); MPI_Reduce( a, answer, 100, ctype, myOp, root,comm );