MPI in dettaglio Chi ha definito MPI
Transcript
MPI in dettaglio Chi ha definito MPI
MPI in dettaglio Salvatore Orlando CALCOLO PARALLELO - S. Orlando 1 CALCOLO PARALLELO - S. Orlando 2 Chi ha definito MPI Il modello Message-Passing Model • • • Ad un processo è tradizionalmente associato con – program counter & address space. Processi possono però avere threads multipli (program counters & private stacks) che condividobo un singolo address space – Le versioni correnti di MPI supportano la comunicazione tra processi, non tra thread Interprocess communication (message-passing) combina – Sincronizzazione – Movimento di dati dall’address space di un processo (mittente), all’address space di un altro processo (destinatario) Process 0 Process 1 Send(data) Receive(data) CALCOLO PARALLELO - S. Orlando 3 Operazioni One-Sided di comunicazione • • • MPI-2 permette anche operazioni One-Side tra processi – remote memory read & writes Solo un processo deve esplicitamente partecipare. Vantaggio – comunicazione e sincronizzazione sono disaccoppiate Process 0 Process 1 Put(data) (memory) (memory) Get(data) CALCOLO PARALLELO - S. Orlando 4 Un semplice programma MPI #include <mpi.h> #include <stdio.h> int main( int argc, char *argv[] ) { MPI_Init( &argc, &argv ); printf( "Hello, world!\n" ); MPI_Finalize(); return 0; } CALCOLO PARALLELO - S. Orlando 5 Compilare un programma MPI • Con MPICH possiamo semplicemente compilare programmi in C usando il comando: mpicc – È uno script che invoca cc/gcc con i parametri corretti, risolvendo i problemi dovuti • INCLUDE (es. mpi.h) • LIB (es. mpi.a) • Con MPICH è possibile linkare speciali librerie, e instrumentare così il codice per analizzare le prestazioni mpicc –o myprog -mpitrace myprog.c CALCOLO PARALLELO - S. Orlando 6 Eseguire un programma MPI • Lo standard MPI-1 non specifica come eseguire un programma MPI • In generale, eseguire il programma MPI dipende dalla specifica implementazione della libreria – Potrebbe richiedere vari scripts, argomenti, e/o variabili di ambiente – Con MPICH si usa mpirun [-np<num>] [-machinefile <nomefile>] -np <num> : <execfile> specifica grado di parallelismo -machinefile <nomefile> : lista di IP address o domain name dei calcolatori del cluster CALCOLO PARALLELO - S. Orlando 7 Scoprire a run-time informazioni sull’ambiente • Ogni processo può avere necessità di chiedere : – Quanti processi partecipano in questa computazione? – Chi sono io? • MPI fornisce funzioni per rispondere a questi quesiti: – MPI_Comm_size() restituisce il numero di processi – MPI_Comm_rank restituisce il rank, un numero tra 0 e size-1, che identifica il processo chiamante CALCOLO PARALLELO - S. Orlando 8 Un Hello.c migliore #include <mpi.h> #include <stdio.h> int main( int argc, char *argv[] ) { int rank, size; MPI_Init( &argc, &argv ); MPI_Comm_rank( MPI_COMM_WORLD, &rank ); MPI_Comm_size( MPI_COMM_WORLD, &size ); printf( "I am %d of %d\n", rank, size ); MPI_Finalize(); return 0; } CALCOLO PARALLELO - S. Orlando 9 Comunicatori: Gruppi e Contesti • • • I processi MPI possono essere organizzati in gruppi Ogni messaggio è inviato in un gruppo, ma all’interno di uno specifico contesto – Il msg deve anche essere ricevuto nello stesso contesto Gruppo e Contesto insieme formano – Un comunicatore • Nota: per lo stesso gruppo potremmo così avere comunicatori diversi • Un processo è identificato dal suo rank nel gruppo associato con un comunicatore • MPI_COMM_WORLD – Comunicatore di default CALCOLO PARALLELO - S. Orlando 10 MPI Datatypes • I dati in un messaggio da inviare/ricevere descritti da una tripla: – address, count, datatype • Un MPI datatype è definito ricorsivamente come: – Predefinito, corrispondente a un datatype di base del linguaggio ospite (es., MPI_INT, MPI_DOUBLE_PRECISION) – Un array contiguo di MPI datatypes – Un block di datatypes con stride – Un struttura arbitraria di datatypes • Ad esempio, è possibile costruire datatypes come – Un array di coppie (int, float) – Una riga di una matrice, memorizzata però per colonne (stride fisso) CALCOLO PARALLELO - S. Orlando 11 Perché sono necessari i Datatypes? • Un’implementazione MPI può così permettere comunicazioni tra processi allocati su macchine eterogenee, i cui datatype elementari – hanno diversa rappresentazione in memoria – hanno lunghezze differenti • Con i datatype si possono specificare layout application-oriented dei dati – Questo riduce copie memory-to-memory nell’implementazione CALCOLO PARALLELO - S. Orlando 12 MPI Tags • I messaggi sono inviati accompagnati con un tag intero user-defined – Questo serve al processo ricevente per identificare il messaggio – I messaggi possono essere selezionati dal ricevente specificando il tag – I messaggi possono non essere scelti dal ricevente sulla base del tag • Specificando MPI_ANY_TAG come tag nella receive. • I tag sono considerati come message type in alcuni sistemi messagepassing, ma non in MPI – MPI li chiama semplicemente tag per evitare confusione con i datatypes. CALCOLO PARALLELO - S. Orlando 13 Tag e Contesti • E’ noto che la separazione dei messaggi da adottare per la comunicazione all’interno di specifico contesti può essere realizzata usando i tag, ma – Questo richiede che eventuali librerie usate siano a conoscenza dei tag usati dal programma chiamante o da altre librerie – In ogni caso, avremmo problemi con i “wild card” per i tag (ANY_TAG) • I contesti in MPI sono differenti dai tag – Non sono permessi wild card – Sono allocati dinamicamente e univocamente dal sistema quando ad esempio una libraria stabilisce di usare un comunicatore per i suoi scopi • Usa MPI_Comm_split per creare nuovi contesti (communicatori) CALCOLO PARALLELO - S. Orlando 14 Uso dei comunicatori: vantaggi • Subroutine Sub1() e Sub2(), corrispondenti a librerie MPI diverse – Nota: subroutine chiamate da tutti i processi (codice SPMD) • Funzionamento corretto: CALCOLO PARALLELO - S. Orlando 15 Uso dei comunicatori: vantaggi • Usando lo stesso comunicatore, a causa dell’uso di ANY_TAG, il funzionamento potrebbe essere scorretto: CALCOLO PARALLELO - S. Orlando 16 MPI Basic (Blocking) Send MPI_SEND (start, count, datatype, dest, tag, comm) • • • • • Comunicazione standard, Asincrona/Buffered/Bloccante Il buffer del messaggio è descritto da – start, count, datatype Il processo destinazione è specificato da dest, che è il rank del processo target nel comunicatore specificato da comm. Quando la funzione ritorna, i dati sono già stati consegnati al sistema e il buffer può essere riusato Il messaggio può non essere stato ricevuto dal processo ricevente CALCOLO PARALLELO - S. Orlando 17 MPI Basic (Blocking) Receive MPI_RECV(start, count, datatype, source, tag, comm, status) • Comunicazione standard, Asincrona/Buffered/Bloccante • Attende fino a quando un matching message (rispetto a comm, source e tag) è ricevuto dal sistema, e copiato nel buffer utente (start, count, datatype) • source è il rank nel comunicatore specificato da comm, oppure MPI_ANY_SOURCE. • tag è il tag atteso e associato al messaggio, oppure MPI_ANY_TAG • status è un parametro in output, e conterrà informazioni sulla comunicazione • Si possono ricevere meno di count occorrenze di un datatype, ma riceverne di più è un errore CALCOLO PARALLELO - S. Orlando 18 Status • Status è una struttura dati allocata nello user’s program, che conterrà informazioni sulla receive appena conclusa: int recvd_tag, recvd_from, recvd_count; MPI_Status status; MPI_Recv(..., MPI_ANY_SOURCE, MPI_ANY_TAG, ..., &status ) recvd_tag = status.MPI_TAG; recvd_from = status.MPI_SOURCE; MPI_Get_count( &status, datatype, &recvd_count ); CALCOLO PARALLELO - S. Orlando 19 Calcolo PI greco E’ noto che l’area del cerchio è r2 π, per cui l’area del semicerchio con r=1 è: 1 π/4 y Curva cerchio (teorema di Pitagora): x2 + y2 = 1 y = √(1-x2) 0 x 1 L’area del semicerchio corrisponde al calcolo del1seguente integrale: Equivalentemente possiamo calcolare ∫ 1− X 2 0 1 1 ∫0 1 + x 2 ARCTAN(1) = π/4 ARCTAN(0) = 0 Possiamo calcolarlo numericamente. Maggiore è il numero di intervalli in cui suddividiamo [0..1], maggiore è la precisione del calcolo dell’integrale CALCOLO PARALLELO - S. Orlando 20 Esempio: PI greco in C (1) #include <mpi.h> #include <math.h> int main(int argc, char *argv[]) { int done = 0, n, myid, numprocs, i, rc; double PI25DT = 3.141592653589793238462643; double mypi, pi, h, sum, x, a; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); if (myid == 0) { printf("Enter the number of intervals: (0 quits) "); scanf("%d",&n); } MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); CALCOLO PARALLELO - S. Orlando 21 Esempio: PI greco in C (2) if (n != 0) { h = 1.0 / (double) n; sum = 0.0; for (i = myid + 1; i <= n; i += numprocs) { x = h * ((double) i - 0.5); sum += 4.0 / (1.0 + x*x); } mypi = h * sum; MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (myid == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - PI25DT)); } MPI_Finalize(); return 0; } CALCOLO PARALLELO - S. Orlando 22 Codici unsafe e deadlock • Anche usando primitive asincrone (bufferizzate), si può generare un deadlock: – Supporre di voler inviare un messaggio molto grande dal processo 0 al processo 1 – se la memoria di sistema (buffer) sul ricevente è insufficiente, la send deve attendere che l’utente fornisca spazio di memoria (attraverso una receive) • Possibile deadlock: Process 0 Process 1 Send(1) Recv(1) Send(0) Recv(0) • Questo codice è chiamato “unsafe”, poiché il suo funzionamento dipende dalla disponibiltà di buffer di sistema CALCOLO PARALLELO - S. Orlando 23 Alcune soluzioni al problema del codice “unsafe” • • Ordinare le operazioni più attentamente: Process 0 Process 1 Send(1) Recv(1) Recv(0) Send(0) Usare primitive non-blocking: Process 0 Process 1 Isend(1) Irecv(1) Waitall Isend(0) Irecv(0) Waitall CALCOLO PARALLELO - S. Orlando 24 Estensioni a MPI(1): MPI-2 • Oltre alle comunicazioni one-sided – Dynamic Process Management • Startup dinamico dei processi • Determina dinamicamente le connessioni – I/O parallelo – Bindings anche per C++/ Fortran-90 • problemi legati a linguaggi diversi e alla rappresentazione dei tipi semplici e strutturati CALCOLO PARALLELO - S. Orlando 25 MPICH • MPICH è un’implementazione portabile di MPI –1 con alcune nuove primitive MPI-2 • Funziona su MPPs, Grid, cluster, e NOWs o Clumps eterogenei • Per compilare, decomprimere in /usr/local – configure – make bisogna poi sistemare PATH e MANPATH • Per compilare, eseguire e analizzare le prestazioni mpicc –o myprog myprog.c mpirun -np 10 myprog CALCOLO PARALLELO - S. Orlando 26 MPICH (compilazione) • • Esistono quindi diversi script per compilare e linkare (librerie e path relativi, path degli include sono specificati dagli script) – C (mpicc) – C++ (mpiCC) – Fortran 77 (mpif77) – Fortran 90 Script di compilazione permettono diverse opzioni: – -mpilog • Costruisce eseguibile che genera MPE log files. – -mpitrace • Costruisce eseguibile che genera tracce di esecuzione – -mpianim • Costruisce eseguibile che genera animazione real-time – -show • Mostra solo i comandi che verranno eseguiti • Possibile linkare altre librerie – mpicc -o foo foo.o -lm CALCOLO PARALLELO - S. Orlando 27 MPICH (esecuzione) • mpirun [mpirun_options...] <progname> [options...] • Alcune mpirun_options: -machinefile <machine-file name> Take the list of possible machines to run on from the file <machine-file name>. This is a list of all available machines; use -np <np> to request a specific number of machines. -np <np> specify the number of processors to run on -nolocal don’t run on the local machine -t Testing - do not actually run, just print what would be executed • In un cluster di workstation i processi di un job parallelo sono fatti partire individualmente su ciascuna workstation – Se il parametro –machifile non è specificato, mpich cerca una lista di default: machines.<arch> nella directory di istallazione (es.: /usr/local/mpich2.2.21). CALCOLO PARALLELO - S. Orlando 28 MPICH (machinefile) • • E’ possibile costruire un file delle workstation (machinefile) personale Esempio di machinefile: mercury venus earth mars:2 jupiter:4 • che specifica l’esistenza di 3 macchine con processore singolo (mercury, venus, and earth), una macchina con 2 processori (mars), e una con 15 (jupiter). Eseguendo: mpirun -np 9 .... con il file di sopra, verranno creati – 1 processo su mercury, venus, e earth – 2 su mars – 4 su jupiter • Se fossero necessari 10 o più processi, mpirun ripartirebbe dall’inizio del file, creando processi aggiuntivi su mercury, venus, ecc. CALCOLO PARALLELO - S. Orlando 29 MPICH (machinefile e rsh/ssh) • Deve essere possibile lanciare un processo/comando tramite rsh/ssh su ogni macchina compresa nel <machinefile> – Senza digitare la password ogni volta – Con garanzia di sicurezza • rsh – Meno sicuro – Un host permette il login remoto ad un insieme di utenti@host listati in un file (.rhosts) – Quindi il login è permesso sulla base di un IP e di un user name • ssh – Più sicuro – Basato sul meccanismo delle chiavi pubbliche/private – L’host su cui vogliamo collegarci in remoto (server) deve possedere la chiave pubblica dell’utente@host (client) – Il client dimostra la propria identità firmando con la propria chiave privata un messaggio challenge – Il server verifica il challenge grazie al possesso della relativa chiave pubblica – Sicurezza garantita dalla protezione della chiave privata CALCOLO PARALLELO - S. Orlando 30 rsh • Per costruire un cluster composto dalle macchine p0.dsi.unive.it p1.dsi.unive.it p3.dsi.unive.it ponete nella vostra home condivisa il file .rhosts così costruito: p0.dsi.unive.it p1.dsi.unive.it p3.dsi.unive.it • • username username username Una generica macchina py.dsi.unive.it accetterà l’esecuzione locale di una richiesta remota, proveniente dalla macchina px.dsi.unive.it e con utente pippo, solo se – nel file .rhosts presente nella home di pippo su py.dsi.unive.it apparirà la riga: px.dsi.unive.it pippo NOTA: con MPICH, è il comando mpirun ad aver bisogno di usare rsh per lo spawn dei vari task del programma SPMD. Quindi, se compiliamo e lanciamo mpirun sempre da px.dsi.unive.it, basterà includere in tutti i file . rhosts del cluster l’unica riga di sopra CALCOLO PARALLELO - S. Orlando 31 MPICH (machinefile e rsh) • Potete controllare la corretta configurazione di rsh e .rhosts, ovvero se dalla una macchina p0 è possibile lanciare job sulla macchina p2 effettuando il comando > rsh p2.dsi.unive.it ls che invoca un ‘ls’ remoto sulla home directory. • MPICH mette anche a disposizione un tool > tstmachines -v -machinefile=<miofile> per controllare il corretto funzionamento di rsh su tutte le workstation del machinefile. CALCOLO PARALLELO - S. Orlando 32 SSH • Sulle macchine del laboratorio, rsh è stato disabilitato – In verità, con il meccanismo dei link simbolici, quando si invoca rsh si sta in realtà invocando ssh – MPI (ma anche PVM) usano ssh per l’esecuzione remota di processi/comandi • Usando SSH, il meccanismo di autenticazione senza password non è basato sul file .rhosts – è necessario generare la coppia di chiavi privata e pubblica CALCOLO PARALLELO - S. Orlando 33 SSH senza password • Digitare – ssh-keygen -t rsa • digitare sempre <Invio> alla richiesta di password, senza inserire nulla • In questo modo però la chiave privata non sarà protetta da passphase – Questo comando genera in $HOME:/.ssh i file • id_rsa (chiave privata) e • id_rsa.pub (chiave publica) • • Copiare il file $HOME:/.ssh/id_rsa.pub in $HOME:/.ssh/authorized_keys – Poiché la home è condivisa, basta questo per permettere l’invio di comandi remoti tra tutte le macchine del laboratorio – Eseguire i test suggeriti per rsh Suggerimento: – Conservare in modo sicuro la chiave privata $HOME:/.ssh/id_rsa – Ad esempio rimuovendola se necessario CALCOLO PARALLELO - S. Orlando 34 Alcuni semplici esercizi • Compila e esegui il programma cpi – dpkg -L mpich (per verificare i file istallati) – cp –R /usr/share/doc/mpich/examples/pi mpi_examples – cd mpi_examples; make cpi • Modifica il programma cpi in modo da usare send/receive invece di bcast/reduce • Scrivere un programma che invia un messaggio lungo un anello: – Processo 0 legge una linea dallo stdin – Processo 0 invia la linea a Processo 1, che l’invia al Processo 2, etc. – L’ultimo processi la invia indietro al Processo 0, che lo stampa. – Usa MPI_Wtime per prendere i tempi (man MPI_Wtime) Calcola banda di trasmissione e overhead di trasmissione tra 2 processi – Ping-pong: • • il primo processo invia e riceve, il secondo rimbalza i messaggi ricevuti – Calcolare con MPI_Wtime il tempo impiegato sul primo processo, dividendo il tempo per il numero Byte trasmessi – Cosa succede all’aumentare del size dei messaggi? CALCOLO PARALLELO - S. Orlando 35