1 / 39

Podstawy MPI-2: część II Dynamiczne zarządzanie procesami

Podstawy MPI-2: część II Dynamiczne zarządzanie procesami. Pula procesorów przydzielonych do zadania jest ustalona przy wywołaniu mpirun. mpirun uruchamia tę samą aplikację na wszystkich procesorach, stąd mnogość if (rank==master) , itp. w kodzie.

cachez
Download Presentation

Podstawy MPI-2: część II Dynamiczne zarządzanie procesami

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. Podstawy MPI-2: część IIDynamiczne zarządzanie procesami

  2. Pula procesorów przydzielonych do zadania jest ustalona przy wywołaniu mpirun. mpirun uruchamia tę samą aplikację na wszystkich procesorach, stąd mnogość if (rank==master), itp. w kodzie. Nie ma narzędzi do uruchomienia komunikacji z innymi procesami. Można dodawać/zwalniać procesory w toku wykonywania aplikacji. Można uruchamiać inne aplikacje z poziomu danej aplikacji. Dany proces może nawiązywać komunikację z innymi procesami. MPI-1 MPI-2

  3. Kiedy ogranicznenia MPI-1 są uciążliwe? • Jeżeli musimy policzyć wiele (dziesiątki a nawet setki) zadań jedno- albo niewielo-procesorowych (np. obliczenia kwantowomechaniczne dla wielu konformacji) a “trywialne” zrównoleglenie aplikacji jest niemożliwe lub nieopłacalne. • Jeżeli chcemy napisać program, który musi wywoływać co jakiś czas inną aplikację (np. dynamiczna wizualizacja wyników).

  4. Przykład programu z użyciem MPI-1 startującego wiele kopi aplikacji jednoprocesorowej z różnymi argumentami linii polecenia • Każdy procesor czyta plik fort.1 zawierający argumenty linii polecenia dla poszczególnych procesorów. • Używając instrukcji call system, dany procesor wykonuje skrypt runall z argumentami linii polecenia odpowiadającymi jego rzędowi. Ten schemat można uogólnić na startowanie różnych aplikacji jednoprocesorowych w zależności od rzędu procesora.

  5. driver.f include “mpif.h” parameter (maxcom=100000) character*80 command(0:maxcom-1) integer rank, size, IERROR integer i,ncom call MPI_Init( IERROR ) call MPI_COMM_RANK( MPI_COMM_WORLD, rank, IERROR ) call MPI_COMM_SIZE( MPI_COMM_WORLD, size , IERROR ) ncom=0 do i=0,maxcom-1 read (1,'(a)',end=10) command(i) ncom=ncom+1 enddo 10 continue do i=rank,ncom-1,size print *,"Processor",rank," executes runall "//command(i) call system("./runall "//command(i)) enddo print *,"Processor",rank," has finished" call MPI_FINALIZE( IERROR ) stop end

  6. runall fort.1 #!/bin/csh -f cd $1; run >! $2.monte 1/0.60 1 1/0.60 2 1/0.60 3 1/0.60 4 1/0.60 5 1/0.60 6 1/0.60 7 1/0.60 8 1/0.60 9 1/0.60 10 1/0.60 11 1/0.60 12 1/0.60 13 1/0.60 14 1/0.60 15 1/0.60 16 1/0.60 17 1/0.60 18 1/0.60 19 1/0.60 20 skrypt PBS start.mat #PBS -N ZUCH #PBS -q dque #PBS -l nodes=10:ppn=2 set NPROCS=`cat\ $PBS_NODEFILE | wc -l` echo $NPROCS processors used time mpirun –machinefile\ $PBS_NODEFILE -np $NPROCS\ -nolocal -p4wd\ $PBS_O_WORKDIR\ $PBS_O_WORKDIR/driver

  7. Wykorzystanie rozszerzeń MPI-2: równoległe kopiowanie pcpn-mplik_źródłowykopia np. pcp 1-10 testfile /tmp/testfile_out ma skopiować plik testfile jako testfile_out do katalogów /tmp maszyn o numerach od 1 do 10. W “Using MPI-2”, katalog starting pcp.c - wersja w MPI-1 (kod dla mastera robotników) pcp-master.c pcp-slave.c – MPI-1, rozdzielone kody pcp-spawn-master.c pcp-spawn-slave.c – MPI-2, rozdzielone kody

  8. Motywacja • Często zachodzi konieczność szybkiego skopiowania np. programu wykonywalnego lub pliku danych wejściowyh na dyski lokalne wszystkich maszyn przydzielonych do zadania. • scp w pętli foreach lub while działa sekwencyjnie a zatem wolno. Rozwiązanie • Zorganizować kopiowanie jako czytanie fragmetów pliku a następnie rozsyłanie ich po “drzewie” do robotników, którzy zapisują część na swoich dyskach lokalnych i przesyłają dalej. • Najlepiej zastosować do tego celu MPI_Bcast, który rozsyła wiadomości po “drzewie”.

  9. Schemat kopiowania równoległego z wykorzystaniem MPI_Bcast 3 Bufor czytany Bufor przesyłany Bufor zapisywany na dysku lokalnym 1 4 0 2 5

  10. pcp.c: początek programu /* pcp from SUT, in MPI */ #include "mpi.h" #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define BUFSIZE 256*1024 #define CMDSIZE 80 int main( int argc, char *argv[] ) { int myrank, mystatus, allstatus, done, numread; char outfilename[128], controlmsg[80]; int infd, outfd; char buf[BUFSIZE]; MPI_Init( &argc, &argv ); MPI_Comm_rank( MPI_COMM_WORLD, &myrank );

  11. pcp.c: przesyłanie nazwy pliku if ( myrank == 0 ) { makehostlist( argv[1], "targets" ); strcpy( outfilename, argv[3] ); if ( (infd = open( argv[2], O_RDONLY ) ) == -1 ) { fprintf( stderr, "input file %s does not exist\n", argv[2] ); sprintf( controlmsg, "exit" ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, MPI_COMM_WORLD ); MPI_Finalize(); return( -1 ); } else { sprintf( controlmsg, "ready" ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, MPI_COMM_WORLD ); } } else { MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, MPI_COMM_WORLD ); if ( strcmp( controlmsg, "exit" ) == 0 ) { MPI_Finalize(); return( -1 ); } }

  12. pcp.c: otwieranie plików if ( myrank == 0 ) sprintf( controlmsg, outfilename ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, MPI_COMM_WORLD ); if ( (outfd = open( controlmsg, O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU ) ) == -1 ) mystatus = -1; else mystatus = 0; MPI_Allreduce( &mystatus, &allstatus, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD ); if ( allstatus == -1 ) { if ( myrank == 0 ) fprintf( stderr, "output file %s could not be opened\n", outfilename ); MPI_Finalize(); return( -1 ); } /* at this point all files have been successfully opened */ printf("all files opened\n");

  13. pcp.c: kopiowanie done = 0; while ( !done ) { if ( myrank == 0 ) numread = read( infd, buf, BUFSIZE ); MPI_Bcast( &numread, 1, MPI_INT, 0, MPI_COMM_WORLD ); if ( numread > 0 ) { MPI_Bcast( buf, numread, MPI_BYTE, 0, MPI_COMM_WORLD ); write( outfd, buf, numread ); } else { close( outfd ); done = 1; } } MPI_Finalize(); }

  14. Program pcp będzie działać ale: • nie będzie działać jak “zwykłe” polecenie unixowe a będzie musiał być uruchomiony pod mpirun, • lista maszyn, na które będzie kopiowany plik jest przekazywana do mpirun w postaci pliku stojącego po –machinefile. • Rozmnażanie procesów w MPI-2 • Program wywołujemy na jednym procesorze jak polecenie unixową; mpirun niepotrzebny. • Proces wywołujący tworzy listę maszyn na podstawie argumentów linii polecenia a następnie otwiera z nimi komunikację. • Inny kod będzie wykonywał master a inny robotnicy.

  15. Najpierw podzielimy program na kod mastera (pcp-master) i robotników (pcp-slave) pcp-master.c: przesyłanie nazwy pliku makehostlist( argv[1], "targets" ); strcpy( outfilename, argv[3] ); if ( (infd = open( argv[2], O_RDONLY ) ) == -1 ) { fprintf( stderr, "input file %s does not exist\n", argv[2] ); sprintf( controlmsg, "exit" ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, MPI_COMM_WORLD ); MPI_Finalize(); return( -1 ); } else { sprintf( controlmsg, "ready" ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, MPI_COMM_WORLD ); }

  16. pcp-master.c: otwieranie pliku danych sprintf( controlmsg, outfilename ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, MPI_COMM_WORLD ); if ( (outfd = open( outfilename, O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU ) ) == -1 ) mystatus = -1; else mystatus = 0; MPI_Allreduce( &mystatus, &allstatus, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD ); if ( allstatus == -1 ) { fprintf( stderr, "output file %s could not be opened\n", outfilename ); MPI_Finalize(); return( -1 ); }

  17. pcp-master.c: kopiowanie /* at this point all files have been successfully opened */ done = 0; while ( !done ) { numread = read( infd, buf, BUFSIZE ); MPI_Bcast( &numread, 1, MPI_INT, 0, MPI_COMM_WORLD ); if ( numread > 0 ) { MPI_Bcast( buf, numread, MPI_BYTE, 0, MPI_COMM_WORLD ); write( outfd, buf, numread ); } else { close( outfd ); done = 1; } } MPI_Finalize(); }

  18. pcp-slave.c: odbieranie nazwy pliku i otwieranie pliku MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, MPI_COMM_WORLD ); if ( strcmp( controlmsg, "exit" ) == 0 ) { MPI_Finalize(); return -1; } MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, MPI_COMM_WORLD ); if ( (outfd = open( controlmsg, O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU ) ) == -1 ) mystatus = -1; else mystatus = 0; MPI_Allreduce( &mystatus, &allstatus, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD ); if ( allstatus == -1 ) { MPI_Finalize(); return( -1 ); }

  19. pcp-slave.c: kopiowanie /* at this point all files have been successfully opened */ done = 0; while ( !done ) { MPI_Bcast( &numread, 1, MPI_INT, 0, MPI_COMM_WORLD ); if ( numread > 0 ) { MPI_Bcast( buf, numread, MPI_BYTE, 0, MPI_COMM_WORLD ); write( outfd, buf, numread ); } else { close( outfd ); done = 1; } } MPI_Finalize(); }

  20. Tworzenie informacji o procesie wywołującym (MPI_Info_create i MPI_Info_set). Rozmnażanie procesów z przekazaniem informacji o procesie wywołującym (MPI_Comm_spawn). Tworzenie wspólnego komunikatora zawierającego mastera i robotników (MPI_Comm_merge). Uzyskiwanie informacji o procesie wywołującym (MPI_Get_parent). Wprowadzanie cech MPI-2Master Robotnicy

  21. MPI_INFO_CREATE(info) [OUT info] utworzony obiekt informacyjny (handle) C: int MPI_Info_create(MPI_Info *info) FORTRAN/FORTRAN90: MPI_INFO_CREATE(INFO, IERROR)INTEGER INFO, IERROR C++: static MPI::Info MPI::Info::Create() MPI_INFO_CREATE tworzy nowy obiekt informacyjny, niezawierający na razie żadnych (klucz,wartość).

  22. MPI_INFO_SET(info, key, value) [INOUT info] obiekt informacyjny (handle) [IN key] klucz (string) [IN value] wartość (string) C: int MPI_Info_set(MPI_Info info, char *key, char *value) FORTRAN/FORTRAN90: MPI_INFO_SET(INFO, KEY, VALUE, IERROR)INTEGER INFO, IERROR CHARACTER*(*) KEY, VALUE C++: void MPI::Info::Set(const char* key, const char* value) MPI_INFO_SET dodaje parę (klucz, wartość) do obiektu informacyjnego; poprzednio ustalona wartość klucza jest nadpisywana.

  23. MPI_INFO_DELETE(info, key) [INOUT info] obiekt informacyjny (handle) [IN key] klucz (string) C: int MPI_Info_delete(MPI_Info info, char *key) FORTRAN/FORTRAN90: MPI_INFO_DELETE(INFO, KEY, IERROR)INTEGER INFO, IERROR CHARACTER*(*) KEY C++: void MPI::Info::Delete(const char* key) MPI_INFO_DELETE usuwa parę (klucz,wartość) z danego obiektu informacyjnego.

  24. MPI_COMM_SPAWN(command, argv, maxprocs, info, root, comm, intercomm, array_of_errcodes) [IN command] nazwa programu który ma być wystartowany na odległych procesorach; ważna tylko dla mastera (string) [IN argv] argumenty linii polecenia do przekazania dla robotników; ważne tylko dla mastera (tablica łańcuchów) [IN maxprocs] maksymalna liczba procesów potomnych; ważne tylko dla mastera (integer) [IN info] zbiór par (klucz,wartość), mówiących systemowi gdzie wystartować procesy potomne; ważne tylko dla mastera (handle) [IN root] rząd procesu, który ustala poprzednie argumenty (integer) [IN comm] komunikator (wewnętrzny) zawierający procesy tworzące procesy potomne (handle) [OUT intercomm] interkomunikator pomiędzy grupą oryginalną i noworozmnożonymi procesami (handle) [OUT array_of_errcodes] tablica kodów błędów; jeden dla procesu (array of integer)

  25. C: int MPI_Comm_spawn(char *command, char *argv[], int maxprocs, MPI_Info info, int root, MPI_Comm comm, MPI_Comm *intercomm, int array_of_errcodes[]) FORTRAN/FORTRAN90: MPI_COMM_SPAWN(COMMAND, ARGV, MAXPROCS, INFO, ROOT, COMM, INTERCOMM, ARRAY_OF_ERRCODES, IERROR)CHARACTER*(*) COMMAND, ARGV(*) INTEGER INFO, MAXPROCS, ROOT, COMM, INTERCOMM, ARRAY_OF_ERRCODES(*), IERROR C++:MPI::Intercomm MPI::Intracomm::Spawn(const char* command, const char* argv[], int maxprocs, const MPI::Info& info, int root, int array_of_errcodes[]) const MPI::Intercomm MPI::Intracomm::Spawn(const char* command, const char* argv[], int maxprocs, const MPI::Info& info, int root) const

  26. MPI_COMM_GET_PARENT(parent) [OUT parent] komunikator rodzicielski (handle) C: int MPI_Comm_get_parent(MPI_Comm *parent) FORTRAN/FORTRAN90: MPI_COMM_GET_PARENT(PARENT, IERROR) INTEGER PARENT, IERROR C++: static MPI::Intercomm MPI::Comm::Get_parent() MPI_COMM_GET_PARENT zwraca interkomunikator utworzony przez proces(y) rodzicielski(e) poleceniem MPI_COMM_SPAWN lub MPI_COMM_SPAWN_MULTIPLE; jeżeli takie polecenie nie zostało wydane lub interkomunikator został zwolniony, zwraca MPI_COMM_NULL.

  27. Ilustracja działania MPI_Comm_spawn i MPI_Comm_parent Procesy rodzicielskie Procesy potomne MPI_Comm_spawn MPI_Init Interkomunikator Procesy potomne jako grupa odległa Procesy rodzicielskie jako grupa odległa Zwracany przez MPI_Comm_spawn Zwracany przez MPI_Comm_parent

  28. pcp-spawn-master.c: początek /* pcp from SUT, in MPI */ #include "mpi.h" #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define BUFSIZE 256*1024 #define CMDSIZE 80 int main( int argc, char *argv[] ) { int mystatus, allstatus, done, numread; char outfilename[128], controlmsg[80]; int infd, outfd; char buf[BUFSIZE]; char soft_limit[20]; MPI_Info hostinfo; MPI_Comm pcpslaves; MPI_Init( &argc, &argv ); makehostlist( argv[1], "targets“, &num_hosts ); MPI_Info_create( &hostinfo ); MPI_Info_set( hostinfo, "file", "targets" ); MPI_Info_set( hostinfo, "soft", soft_limit );

  29. pcp-spawn-master.c: przydzielanie procesorów, do których ma być kopiowany plik MPI_Comm_spawn( "pcp_slave", MPI_ARGV_NULL, num_hosts, hostinfo, 0, MPI_COMM_SELF, &pcpslaves, MPI_ERRCODES_IGNORE ); MPI_Info_free( &hostinfo ); strcpy( outfilename, argv[3] ); if ( (infd = open( argv[2], O_RDONLY ) ) == -1 ) { fprintf( stderr, "input %s does not exist\n", argv[2] ); sprintf( controlmsg, "exit" ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, pcpslaves ); MPI_Finalize(); return( -1 ); } else { sprintf( controlmsg, "ready" ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, pcpslaves ); }

  30. pcp-spawn-master.c: przesyłanie nazwy pliku celowego sprintf( controlmsg, outfilename ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, pcpslaves ); if ( (outfd = open( outfilename, O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU ) ) == -1 ) mystatus = -1; else mystatus = 0; MPI_Allreduce( &mystatus, &allstatus, 1, MPI_INT, MPI_MIN, pcpslaves ); if ( allstatus == -1 ) { fprintf( stderr, "output file %s could not be opened\n", outfilename ); MPI_Finalize(); return( -1 ); }

  31. pcp-spawn-master.c: kopiowanie /* at this point all files have been successfully opened */ done = 0; while ( !done ) { numread = read( infd, buf, BUFSIZE ); MPI_Bcast( &numread, 1, MPI_INT, 0, pcpslaves ); if ( numread > 0 ) { MPI_Bcast( buf, numread, MPI_BYTE, 0, pcpslaves ); write( outfd, buf, numread ); } else { close( outfd ); done = 1; } } MPI_Comm_free( &pcpslaves ); MPI_Finalize(); }

  32. pcp-spawn-slave.c: uzyskiwanie informacji od procesu wywołującego i otwieranie pliku celowego MPI_Comm_get_parent( &slavecomm ); MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, slavecomm ); if ( strcmp( controlmsg, "exit" ) == 0 ) { MPI_Finalize(); return( -1 ); } MPI_Bcast( controlmsg, CMDSIZE, MPI_CHAR, 0, slavecomm ); if ( (outfd = open( controlmsg, O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU ) ) == -1 ) mystatus = -1; else mystatus = 0; MPI_Allreduce( &mystatus, &allstatus, 1, MPI_INT, MPI_MIN, slavecomm ); if ( allstatus == -1 ) { MPI_Finalize(); return( -1 ); }

  33. pcp-spawn-slave.c: kopiowanie /* at this point all files have been successfully opened */ done = 0; while ( !done ) { MPI_Bcast( &numread, 1, MPI_INT, 0, slavecomm ); if ( numread > 0 ) { MPI_Bcast( buf, numread, MPI_BYTE, 0, slavecomm ); write( outfd, buf, numread ); } else { close( outfd ); done = 1; } } MPI_Comm_free( &slavecomm ); MPI_Finalize(); return 0;

  34. Uwagi dotyczące MPI_Comm_spawn MPI_UNIVERSE_SIZE mówi ile maksymalnie procesów potomnych można utworzyć a MPI_UNIVERSE_FLAG czy ta zmienna jest zdefiniowana przez system. MPI_UNIVERSE_SIZE definiuje “wszechświat” wszystkich komunikatoró, zarówno wystartowanych jak i potencjalnych komunikatorów potomnych. Zmienną tę można ustawić przy uruchamianiu aplikacji pod mpiexec, np: mpiexec –usize 4 –n 1 ./myprog Zadanie zostanie wystartowane na jednym procesorze z możliwością rozszerzenia do czterech.

  35. Przykład int world_size, universe_size, *universe_sizep, flag; … MPI_Attr_get(MPI_COMM_WORLD, MPI_UNIVERSE_SIZE,&universe_sizep, &flag); if (!flag) { printf("This MPI does not support UNIVERSE_SIZE. How many processes total?"); scanf("%d", &universe_size); } else universe_size = *universe_sizep; if (universe_size == 1) printf("No room to start workers"); strcpy(worker_program, "./workers" ); printf("Nome do programa: %s\n",worker_program); ... error = MPI_Comm_spawn(worker_program, MPI_ARGV_NULL, universe_size-1,Info, 0, MPI_COMM_SELF, &everyone,MPI_ERRCODES_IGNORE);

  36. Tworzenie procesów potomnych obniża efektywność działania programu, dlatego w miarę możności należy pracować na puli od razu przydzielonej do zadania.

  37. Przykład wywołania połączenia z innymi procesami przez serwer i zaakceptowania połączenia przez robotników Serwer: gets(port_name); MPI_Comm_connect(port_name,MPI_INFO_NULL,0, MPI_COMM_WORLD,&server); Robotnicy: MPI_Open_port(MP_INFO_NULL,port_name); MPI_Comm_accept(port_name,MPI_INFO_NULL,port_name);

  38. Unifikacja startowania zadań równoległych w MPI-2 MPI-1: zwykle mpirun ale też yod i inne narzędzia. Składnia mpirun zależna od implementacji. MPI-2: mpiexec (dla kompatybilności jest zwykle podlikowany jako mpirun). Najprościej, np.: mpiexec –n 4 ./myprog działa jak mpirun –np 4 ./myprog Inne parametry: -soft, -host, -arch, -wdir, -path, -file, -usize

  39. mpiexec –n 32 –soft 16 myprog Jeżeli dostępne są 32 procesory to startuje myprog na 32, jeżeli nie na 16. mpiexec –n 4 –host denali –wdir /home/me/outfiles myprog Startuje myprog na 4 procesorach maszyny denali używając jako katalogu roboczego /home/me/outfiles. mpiexec –n 12 –soft 1:12 –arch sparc-solaris \ -path /home/me/sunprogs myprog Usiłuje wystartować myprog na 12 procesorach ale użyje od 1 do 12 w zależności od tego ile jest dostępnych, ograniczając się do maszyn o architekturze sparc-solaris i poszukując myprog w /home/me/sunprogs. mpiexec –file myfile Argumenty polecenia są w pliku myfile.

More Related