WWW http://www.netlib.org/pvm3/book/pvm-book.html http://www.netlib.org/pvm3/index.html http://www.netlib.org/pvm3/pvm3.4.5.tgz PVM p. 1
instalowanie 1. Sprowadzić: http://www.netlib.org/pvm3/pvm3.4.5.tgz 2. Rozpakować: $ tar zxvf pvm3.4.5.tgz 3. Dodać do.bashrc PVM_ROOT=$HOME/pvm3 export PVM_ROOT 4. Wkleić do.bashrc zawartość pvm3/lib/bashrc.stub 5. Dodać do.bashrc coś w stylu: export PVM_RSH=/usr/bin/ssh 6. W kartotece pvm3/ wywołać: make PVM p. 2
uruchamianie Uruchomienie interaktynej konsoli oraz demona pvmd na danej maszynie: $ pvm Polecenia w konsoli pvm: help - drukuje listę poleceń add - dodaje nowy host do maszyny wirtualnej (uruchamia na nim demona pvmd) conf - wypisuje kofigurację maszyny wirtualnej delete - usuwa host z maszyny wirtualnej quit - wyjście z terminala (ale pvmd pozostaje uruchomiony) halt - zatrzymuje demony pvmd... PVM p. 3
kompilacja przykładów $ cd $PVM_ROOT/examples $ aimk hello ( aimk polecenie podobne do make dla programów pvm) $ aimk hello_other $ $PVM_ROOT/bin/LINUX/hello (Programy binarne powinny być w tym katalogu na każdej maszynie wchodzącej w skład maszyny wirtualnej) Dobrze jest umieszczać katalogi z własnymi kodami źródłowymi w miejscu gdzie jest katalog examples/, skopiować do nich i zmodyfikować na własne potrzeby plik $PVM_ROOT/examples/Makefile.aimk. PVM p. 4
hello.c #include <stdio.h> #include "pvm3.h" main() { int cc, tid; char buf[100]; printf("i m t%x\n", pvm_mytid()); cc = pvm_spawn("hello_other", (char**)0, 0, "", 1, &tid); if (cc == 1) { cc = pvm_recv(-1, -1); pvm_bufinfo(cc, (int*)0, (int*)0, &tid); pvm_upkstr(buf); printf("from t%x: %s\n", tid, buf); //... PVM p. 5
hello.c } else printf("can t start hello_other\n"); pvm_exit(); exit(0); } PVM p. 6
hello_other.c #include "pvm3.h" main() { int ptid; char buf[100]; ptid = pvm_parent(); strcpy(buf, "hello, world from "); gethostname(buf + strlen(buf), 64); pvm_initsend(pvmdatadefault); pvm_pkstr(buf); pvm_send(ptid, 1); pvm_exit(); exit(0); } PVM p. 7
Interfejs programisty procedury dotępne dla programisty w języku C: plik nagłówkowy: $PVM_ROOT/include/pvm3.h biblioteki: $PVM_ROOT/lib/LINUX/libpvm3.a $PVM_ROOT/lib/LINUX/libgpvm3.a PVM p. 8
pvm_mytid int tid = pvm_mytid( void ) Zwraca task identifier danego procesu. Wartość < 0 -- błąd. Często używany aby wpisać się na listę procesów używanych przez PVM. PVM p. 9
pvm_exit int info = pvm_exit( void ) Informuje lokalnego pvmd, że proces opuszcza PVM. (Musi być wywołane przez proces, który nie był utworzony przez pvm_spawn. PVM p. 10
pvm_spawn int numt = pvm_spawn(char *task, char **argv, int flag, char *where, int ntask, int *tids ) Uruchomienie nowych procesów przez dany proces. task - nazwa pliku wykonywalnego (defaultowo na $HOME/pvm3/bin/$PVM_ARCH/) argv - wskaźnik do tablicy agrumentów zakończonej NULLem. (nie zawiera nazwy pliku wykonywalnego) (może być =NULL) PVM p. 11
pvm_spawn flag - suma wybranych opcji: PvmTaskDefault 0 PVM can choose any machine to start task PvmTaskHost 1 where specifies a particular host PvmTaskArch 2 where specifies a type of architecture PvmTaskDebug 4 Start up processes under debugger PvmTaskTrace 8 Processes will generate PVM trace data. PvmMppFront 16 Start process on MPP front-end. PvmHostCompl 32 Use complement host set PVM p. 12
pvm_spawn where - gdzie uruchomić (znaczenie zależy od opcji flag) ntask - liczba elementów tids tids - tablica długości ntask, w której zwrócone będą TIDy uruchomionych procesów lub błędy. numt - zwracana wartość: liczba faktycznie uruchomionych procesów. PVM p. 13
pvm_kill int info = pvm_kill( int tid ) Zabija proces tid (wysyła SIGTERM). Zwraca 0 lub <0 jeśli błąd. (Nie przeznaczony do samobójstwa - należy wykonać pvm_exit() i exit().) PVM p. 14
pvm_catchout #include <stdio.h> int info = pvm_catchout( FILE *ff ) Po wykonaniu tej procedury wyjście (stdout, stderr) zebrane z utworzonych (spawned) zadań trafia do pliku. ff - deskryptor pliku. info - status (<0 - błąd.) Identyfikatory procesów też mogą być wypisywane (opcja PvmShowTids w pvm_setopt) PVM p. 15
pvm_parent int tid = pvm_parent( void ) tid - identyfikator rodzica lub PvmNoParent - jeśli proces był utworzony przez pvm_spawn. PVM p. 16
pvm_tidtohost int dtid = pvm_tidtohost( tid ) tid - TID zadania dtid - TID demona na hoscie, na którym jest to zadanie. PVM p. 17
pvm_config int info = pvm_config( int *nhost, int *narch, struct pvmhostinfo **hostp ) struct pvmhostinfo { int hi_tid; char *hi_name; char *hi_arch; int hi_speed; }; nhost - zwrócona liczba hostów narch - zwrócona liczba formatów danych hostp - zwrócony wskaźnik do tablicy opisującej hosty maszyny wirtualnej. PVM p. 18
pvm_tasks int info = pvm_tasks( int where, int *ntask, struct pvmtaskinfo **taskp ) struct pvmtaskinfo { int ti_tid; // TID int ti_ptid; // parent TID int ti_host; // chyba DTID pvmd... int ti_flag; char *ti_a_out; // nazwa (tylko dla spawned ) } taskp; where - rodzaj zapytania: 0 for all the tasks on the virtual machine pvmd tid for all tasks on a given host tid for a specific task ntask - zwrócona liczba zadań taskp - zwrócony wskaźnik do tablicy z opisami PVM p. 19
pvm_addhosts int info = pvm_addhosts( char **hosts, int nhost, int *infos ) hosts - tablica nazw hostów (z opcjami) do dodania nhost - długośc hosts infos - tablica wyników (TIDy lub wartości <0 - błedy). info - ilość sukcesów PVM p. 20
pvm_delhosts int info = pvm_delhosts( char **hosts, int nhost, int *infos ) hosts - tablica nazw hostów (z opcjami) do dodania nhost - długośc hosts infos - tablica wyników (TIDy lub wartości <0 - błedy). info - ilość sukcesów PVM p. 21
pvm_sendsig int info = pvm_sendsig( int tid, int signum ) Wysyła sygnał signum do zadania tid. PVM p. 22
pvm_notify int info = pvm_notify( int what, int msgtag, int cnt, int *tids ) what - PvmTaskExit Task exits or is killed PvmHostDelete Host is deleted or crashes PvmHostAdd New host is added PvmNotifyCancel odwołanie (zorowane z oryginalnym what) cnt - dla PvmTaskExit i PvmHostDelete, długość tids dla PvmHostAdd, ile razy powiadamiać tids - dla PvmTaskExit i PvmHostDelete, tablica interesujących nas TIDów. dla PvmHostAdd, nieużywane. msgtag - etykieta dla komunikatów z powiadomieniami. PVM p. 23
pvm_setopt int oldval = pvm_setopt( int what, int val ) Może ustawić dużo różnych opcji. np. pvm_setopt( PvmRoute, PvmRouteDirect ); PVM p. 24
pvm_initsend int bufid = pvm_initsend( int encoding ) Czyści bufer do wysyłania i przygotowuje na zapakowanie nowych danych. encoding - PvmDataDefault 0 XDR PvmDataRaw 1 no encoding PvmDataInPlace 2 data left in place bufid - zwracany identyfikator bufora lub <0 błąd PVM p. 25
pvm_mkbuf int bufid = pvm_mkbuf( int encoding ) Tworzy nowy bufor do wysyłania. (Zwykle niepotrzebne.) encoding - PvmDataDefault 0 XDR PvmDataRaw 1 no encoding PvmDataInPlace 2 data left in place bufid - zwracany identyfikator bufora lub <0 błąd PVM p. 26
pvm_freebuf int info = pvm_freebuf( int bufid ) Zwalnia pamięć bufora bufid utworzonego przez pvm_mkbuf. (Bufory mogą być tworzone przez pvm_mkbuf, pvm_initsend, and pvm_recv.) PVM p. 27
pvm_getsbuf itp. int bufid = pvm_getsbuf( void ) int bufid = pvm_getrbuf( void ) int oldbuf = pvm_setsbuf( int bufid ) int oldbuf = pvm_setrbuf( int bufid ) zwracają/ustawiają buforów wysyłania i odbioru identyfikatory aktywnych PVM p. 28
pakowanie danych int info = pvm_pkbyte( char *xp, int nitem, int stride ) int info = pvm_pkcplx( float *cp, int nitem, int stride ) int info = pvm_pkdcplx( double *zp, int nitem, int stride ) int info = pvm_pkdouble( double *dp, int nitem, int stride int info = pvm_pkfloat( float *fp, int nitem, int stride ) int info = pvm_pkint( int *ip, int nitem, int stride ) int info = pvm_pkuint( unsigned int *ip, int nitem, int str int info = pvm_pkushort( unsigned short *ip, int nitem, int int info = pvm_pkulong( unsigned long *ip, int nitem, int s int info = pvm_pklong( long *ip, int nitem, int stride ) int info = pvm_pkshort( short *jp, int nitem, int stride ) int info = pvm_pkstr( char *sp ) PVM p. 29
pakowanie danych nitem - ile elementów zapakować stride - co który wybierać xp - wskaźnik do początku bloku bajtów cp, zp, dp, fp, ip, - analogicznie dla innytch typów PVM p. 30
pakowanie danych int info = pvm_packf( const char *fmt,... ) Podbny opis formatu jak w printf PVM p. 31
pvm_send int info = pvm_send( int tid, int msgtag ) Wysyła komunikat umieszczony w buforze wysyłania. tid - adresat msgtag - etykieta komunikatu (>=0) info - status wykonania pvm_send jest asynchroniczne (nieblokujące) PVM p. 32
pvm_mcast int info = pvm_mcast( int *tids, int ntask, int msgtag ) Wysyła wiadomość do tablicy tids. (Ale nie do nadawcy) PVM p. 33
pvm_recv int bufid = pvm_recv( int tid, int msgtag ) Blokuje wykonanie aż nadejdzie komunikat z etykietą msgtag od zadania tid. Umieszcza otrzymane dane w nowym aktywnym buforze odbierania. Zwraca identyfikator nowego aktywnego bufora odbierania. PVM p. 34
pvm_nrecv int bufid = pvm_nrecv( int tid, int msgtag ) Sprawdza czy nadeszła wiadomość z etykietą msgtag od zadania tid. Jeśli nadeszła to umieszcza w nowym aktywnym buforze i zwraca jego bufid. W przeciwnym razie zwraca 0. wildcard: -1 PVM p. 35
pvm_probe int bufid = pvm_probe( int tid, int msgtag ) Sprawdza ale nie odbiera wiadomości. Przed odebraniem można sprawdzić: pvm_bufinfo PVM p. 36
pvm_trecive #include <sys/time.h> int bufid = pvm_trecv( int tid, int msgtag, struct timeval *tmout ) wersja z timeoutem PVM p. 37
rozpakowywanie int info = pvm_unpackf( const char *fmt,... ) int info = pvm_upkbyte( char *xp, int nitem, int stride) int info = pvm_upkcplx( float *cp, int nitem, int stride) int info = pvm_upkdcplx( double *zp, int nitem, int stride) int info = pvm_upkdouble( double *dp, int nitem, int stride int info = pvm_upkfloat( float *fp, int nitem, int stride) int info = pvm_upkint( int *ip, int nitem, int stride) int info = pvm_upkuint( unsigned int *ip, int nitem, int st int info = pvm_upkushort( unsigned short *ip, int nitem, in int info = pvm_upkulong( unsigned long *ip, int nitem, int int info = pvm_upklong( long *ip, int nitem, int stride) int info = pvm_upkshort( short *jp, int nitem, int stride) int info = pvm_upkstr( char *sp ) PVM p. 38
grupy int inum = pvm_joingroup( char *group ) group - nazwa grupy inum - zwracany instance number (numer zadania w danej grup int info = pvm_lvgroup( char *group ) PVM p. 39
grupy int tid = pvm_gettid( char *group, int inum ) Zwraca tid zadania o instance number inum w grupie group. int inum = pvm_getinst( char *group, int tid )... int size = pvm_gsize( char *group ) aktualny rozmiar grupy PVM p. 40
pvm_bcast int info = pvm_bcast( char *group, int msgtag ) wysyła zawartośc aktywnego bufora wysyłania do całej grupy (poza nadawcą). nadawca nie musi być członkiem grupy. PVM p. 41
pvm_barrier info = pvm_barrier( char *group, int count ) Blokuje wywołujący proces aż pvm_barrier zostanie wywołane przez count człoków grupy. (każde wywołanie z tym samym parametrem count) (typowo: count = docelowy rozmiar grupy) PVM p. 42
pvm_reduce int info = pvm_reduce( void (*func)(), void *data, int count, int datatype, int msgtag, char *group, int rootginst) func - funkcja w C void func(int *datatype, void *x, void *y, int *num, int *info) x, num - odpowiadają dat i num w pvm_reduce y - otrzymane wartości (też rozmiaru num). predefiniowane: PvmMin, PvmMax, PvmSum, PvmProduct PVM p. 43
pvm_reduce data - wskaźnik do początku tablicy lokalnych wartości. w korzeniu w data zapisywany jest wynik. count - liczba elementów datatype w data. (jednakowy) datatype - typ danych: PVM_BYTE, PVM_SHORT, PVM_INT...... rootginst - instance number członka grupy, który otrzyma wynik Redukcja wykonywana element-wise na data. Wada: jest nieblokująca. Może wymagać pvm_barrier. PVM p. 44
mmult.c /* Matrix Multiply */ /* defines and prototypes for the PVM library */ #include <pvm3.h> #include <stdio.h> /* Maximum number of children this program will spawn */ #define MAXNTIDS 100 #define MAXROW 10 /* Message tags */ #define ATAG 2 #define BTAG 3 #define DIMTAG 5 PVM p. 45
mmult.c void InitBlock(float *a, float *b, float *c, int blk, int row, int col) { int len, ind; int i,j; srand(pvm_mytid()); len = blk*blk; for (ind = 0; ind < len; ind++) { a[ind]=(float)(rand()%1000)/100.0; c[ind]=0.0; } for (i = 0; i < blk; i++) { for (j = 0; j < blk; j++) { if (row == col) b[j*blk+i]=(i==j)?1.0:0.0; else b[j*blk+i]=0.0; } } } PVM p. 46
mmult.c void BlockMult(float* c, float* a, float* b, int blk) { int i,j,k; } for (i = 0; i < blk; i++) for (j = 0; j < blk; j ++) for (k = 0; k < blk; k++) c[i*blk+j] += (a[i*blk+k] * b[k*blk+j]); PVM p. 47
mmult.c int main(int argc, char* argv[]){ /* number of tasks to spawn, use 3 as the default */ int ntask = 2; /* return code from pvm calls */ int info; /* my task and group id */ int mytid, mygid; /* children task id array */ int child[maxntids-1]; int i, m, blksize; /* array of the tids in my row */ int myrow[maxrow]; float *a, *b, *c, *atmp; int row, col, up, down; PVM p. 48
mmult.c /* find out my task id number */ mytid = pvm_mytid(); // pvm_advise(pvmroutedirect); /* check for error */ if (mytid < 0) { /* print out the error */ pvm_perror(argv[0]); /* exit the program */ return -1; } /* join the mmult group */ mygid = pvm_joingroup("mmult"); if (mygid < 0) { pvm_perror(argv[0]); pvm_exit(); return -1; } PVM p. 49
mmult.c /* if my group id is 0 then I must spawn the other tasks */ if (mygid == 0) { /* find out how many tasks to spawn */ if (argc == 3) { m = atoi(argv[1]); blksize = atoi(argv[2]); } if (argc < 3) { fprintf(stderr, "usage: mmult m blk\n"); pvm_lvgroup("mmult"); pvm_exit(); return -1; } PVM p. 50
mmult.c /* make sure ntask is legal */ ntask = m*m; if ((ntask < 1) (ntask >= MAXNTIDS)) { fprintf(stderr, "ntask = %d not valid.\n", ntask); pvm_lvgroup("mmult"); pvm_exit(); return -1; } /* no need to spawn if there is only one task */ if (ntask == 1) goto barrier; /* spawn the child tasks */ info = pvm_spawn("mmult", (char**)0, PvmTaskDefault, (char*)0, ntask-1, child); PVM p. 51
mmult.c /* make sure spawn succeeded */ if (info!= ntask-1) { pvm_lvgroup("mmult"); pvm_exit(); return -1; } /* send the matrix dimension */ pvm_initsend(pvmdatadefault); pvm_pkint(&m, 1, 1); pvm_pkint(&blksize, 1, 1); pvm_mcast(child, ntask-1, DIMTAG); } PVM p. 52
mmult.c else { /* recv the matrix dimension */ pvm_recv(pvm_gettid("mmult", 0), DIMTAG); pvm_upkint(&m, 1, 1); pvm_upkint(&blksize, 1, 1); ntask = m*m; } PVM p. 53
mmult.c /* make sure all tasks have joined the group */ barrier: info = pvm_barrier("mmult",ntask); if (info < 0) pvm_perror(argv[0]); /* find the tids in my row */ for (i = 0; i < m; i++) myrow[i] = pvm_gettid("mmult", (mygid/m)*m + i); /* allocate the memory for the local blocks */ a = (float*)malloc(sizeof(float)*blksize*blksize); b = (float*)malloc(sizeof(float)*blksize*blksize); c = (float*)malloc(sizeof(float)*blksize*blksize); atmp = (float*)malloc(sizeof(float)*blksize*blksize); PVM p. 54
mmult.c /* check for valid pointers */ if (!(a && b && c && atmp)) { fprintf(stderr, "%s: out of memory!\n", argv[0]); free(a); free(b); free(c); free(atmp); pvm_lvgroup("mmult"); pvm_exit(); return -1; } /* find my block s row and column */ row = mygid/m; col = mygid % m; /* calculate the neighbor s above and below */ up = pvm_gettid("mmult", ((row)?(row-1):(m-1))*m+col); down = pvm_gettid("mmult", ((row == (m-1))?col:(row+1)*m+col)); /* initialize the blocks */ InitBlock(a, b, c, blksize, row, col); PVM p. 55
mmult.c /* do the matrix multiply */ for (i = 0; i < m; i++) { /* mcast the block of matrix A */ if (col == (row + i)%m) { pvm_initsend(pvmdatadefault); pvm_pkfloat(a, blksize*blksize, 1); pvm_mcast(myrow, m, (i+1)*atag); BlockMult(c,a,b,blksize); } else { pvm_recv(pvm_gettid("mmult", row*m + (row +i)%m), (i+1)*atag); pvm_upkfloat(atmp, blksize*blksize, 1); BlockMult(c,atmp,b,blksize); } PVM p. 56
mmult.c /* rotate the columns of B */ pvm_initsend(pvmdatadefault); pvm_pkfloat(b, blksize*blksize, 1); pvm_send(up, (i+1)*btag); pvm_recv(down, (i+1)*btag); pvm_upkfloat(b, blksize*blksize, 1); } PVM p. 57
mmult.c /* check it */ for (i = 0 ; i < blksize*blksize; i++) if (a[i]!= c[i]) printf("error a[%d] (%g)!= c[%d] (%g) \n", i, a[i],i,c[i]); } printf("done.\n"); free(a); free(b); free(c); free(atmp); pvm_lvgroup("mmult"); pvm_exit(); return 0; PVM p. 58