Przetwarzanie współbieżne |
---|
Wojciech Kupczyk 94415 |
Cel:
Celem laboratorium jest zaimplementowanie programu przesyłającego złożoną strukturę danych w środowisku MPI oraz sprawdzenie wydajności poszczególnych rozwiązań w zależności od zastosowanej metody przesyłania danych. Ze względu na problem z uruchomieniem programu na innych maszynach, pomiary zostaną wykonane lokalnie na jednej maszynie.
Ad. 1. W celu przetestowania wydajności przesyłania struktur posłużyłem się programem typy.c w którym zaimplementowano poniższą strukturę:
struct rekord{ double skalar; char znak; float wektor[3]; char napis[2]; }; |
---|
Ad. 2. Aby przetestować wydajność metod przesyłania struktur w MPI, zmodyfikowałem wspomniany program typy.c usuwając instrukcje wejścia/wyjścia oraz dodając specjalne funkcje pomiaru czasu dla poszczególnych rozwiązań.
int main( int argc, char** argv ){ int rank, ranksent, size, source, dest, tag, i, j; MPI_Status status; double start,end; struct rekord{ double skalar; char znak; float wektor[3]; char napis[2]; }; struct rekord baza[20], baza1[20]; MPI_Datatype rekord1_typ, rekord2_typ; int tab_dlug_blokow[4] = {1, 1, 3, 2 }; MPI_Datatype tab_typow[4] = { MPI_DOUBLE, MPI_CHAR, MPI_FLOAT, MPI_CHAR }; MPI_Aint lb, zasieg, podstawa, tab_odstepow[4], temp; MPI_Init( &argc, &argv ); MPI_Comm_rank( MPI_COMM_WORLD, &rank ); MPI_Comm_size( MPI_COMM_WORLD, &size ); MPI_Get_address( &baza[0].skalar, &tab_odstepow[0] ); MPI_Get_address( &baza[0].znak, &tab_odstepow[1] ); MPI_Get_address( &baza[0].wektor[0], &tab_odstepow[2] ); MPI_Get_address( &baza[0].napis[0], &tab_odstepow[3] ); podstawa = tab_odstepow[0]; for( i=0; i<4; i++ ) tab_odstepow[i] -= podstawa; MPI_Type_struct( 4, tab_dlug_blokow, tab_odstepow, tab_typow, &rekord1_typ ); MPI_Type_get_extent( rekord1_typ, &lb, &zasieg ); MPI_Type_create_resized( rekord1_typ, lb, zasieg, &rekord2_typ); MPI_Type_commit(&rekord2_typ); if( rank != 0 ) { dest=0; tag=0; baza[2*rank].skalar = rank; baza[2*rank].znak = 'a'; baza[2*rank].wektor[0] = 1; baza[2*rank].wektor[1] = 2; baza[2*rank].wektor[2] = 3; baza[2*rank].napis[0] = 'x'; baza[2*rank].napis[1] = 'y'; baza[2*rank+1].skalar = rank; baza[2*rank+1].znak = 'b'; baza[2*rank+1].wektor[0] = 3; baza[2*rank+1].wektor[1] = 2; baza[2*rank+1].wektor[2] = 1; baza[2*rank+1].napis[0] = 'y'; baza[2*rank+1].napis[1] = 'x'; inicjuj_czas(); start = czas_zegara(); MPI_Send( &baza[2*rank], 2, rekord2_typ, dest, tag, MPI_COMM_WORLD ); } else { for( i=1; i<size; i++ ) MPI_Recv( &baza[2*i], 2, rekord2_typ, i, MPI_ANY_TAG, MPI_COMM_WORLD, &status ); } MPI_Barrier(MPI_COMM_WORLD); if( rank == 0 ){ end = czas_zegara(); printf("Czas przesylania dla typu zwyklego= %lf \n",end-start); printf("Przeslanie typu spakowanego\n"); } MPI_Barrier(MPI_COMM_WORLD); { int rozm, rozm_pakietu, rozmiar_komunikatu, pozycja; void* bufor; MPI_Pack_size(1, MPI_DOUBLE, MPI_COMM_WORLD, &rozm); rozm_pakietu = rozm; MPI_Pack_size(1, MPI_CHAR, MPI_COMM_WORLD, &rozm); rozm_pakietu += rozm; MPI_Pack_size(3, MPI_FLOAT, MPI_COMM_WORLD, &rozm); rozm_pakietu += rozm; MPI_Pack_size(2, MPI_CHAR, MPI_COMM_WORLD, &rozm); rozm_pakietu += rozm; rozmiar_komunikatu = 2*rozm_pakietu; bufor = (void *)malloc(rozmiar_komunikatu); if( rank != 0 ) { dest=0; tag=0; pozycja = 0; for( i=2*rank; i<=2*rank+1; i++ ) { MPI_Pack(&baza[i].skalar, 1, MPI_DOUBLE, bufor, rozmiar_komunikatu, &pozycja, MPI_COMM_WORLD ); MPI_Pack(&baza[i].znak, 1, MPI_CHAR, bufor, rozmiar_komunikatu, &pozycja, MPI_COMM_WORLD ); MPI_Pack(&baza[i].wektor[0], 3, MPI_FLOAT, bufor, rozmiar_komunikatu, &pozycja, MPI_COMM_WORLD ); MPI_Pack(&baza[i].napis[0], 2, MPI_CHAR, bufor, rozmiar_komunikatu, &pozycja, MPI_COMM_WORLD ); } inicjuj_czas(); start = czas_zegara(); MPI_Send( bufor, pozycja, MPI_PACKED, dest, tag, MPI_COMM_WORLD ); } else { for( j=1; j<size; j++ ) { MPI_Recv( bufor, rozmiar_komunikatu, MPI_PACKED, j, MPI_ANY_TAG, MPI_COMM_WORLD, &status ); pozycja = 0; for( i=2*j; i<=2*j+1; i++ ) { MPI_Unpack(bufor, rozmiar_komunikatu, &pozycja, &baza1[i].skalar, 1, MPI_DOUBLE, MPI_COMM_WORLD ); MPI_Unpack(bufor, rozmiar_komunikatu, &pozycja, &baza1[i].znak, 1, MPI_CHAR, MPI_COMM_WORLD ); MPI_Unpack(bufor, rozmiar_komunikatu, &pozycja, &baza1[i].wektor[0], 3, MPI_FLOAT, MPI_COMM_WORLD ); MPI_Unpack(bufor, rozmiar_komunikatu, &pozycja, &baza1[i].napis[0], 2, MPI_CHAR, MPI_COMM_WORLD ); } } } } MPI_Barrier(MPI_COMM_WORLD); if(rank == 0) { end = czas_zegara(); printf("Czas przesylania dla typu spakowane MPI = %lf \n",end-start); } MPI_Finalize(); return(0); } |
---|
Ad. 3. Do pomiaru czasu przesłania struktury użyto funkcji czas_zegara() która wchodzi w skład modułu pomiar_czasu.h, wyniki pomiarów dla 10 wykonań przy wyłączonych funkcjach wejścia/wyjścia:
typ zwykły [s] | typ spakowany MPI [s] |
0,000146 | 0,000094 |
0,000144 | 0,000111 |
0,000169 | 0,000072 |
0,000162 | 0,00009 |
0,000127 | 0,000075 |
0,000214 | 0,00009 |
0,000224 | 0,000108 |
0,000112 | 0,000082 |
0,000089 | 0,000068 |
0,000087 | 0,000047 |
Wnioski
MPI pozwala przesyłać złożone struktury na dwa sposoby, pierwszy jako normalną strukturę, drugi jako specjalny typ spakowany, pomimo iż w przypadku typu spakowanego musimy dodatkowo wywoływać funkcje MPI_Pack przed wysłaniem pakietu, oraz MPI_Unpack po jego odebraniu, to w stosunku do normalnego przesłania struktury zapewnia on lepszą wydajność.