MPI è uno standard di fatto per il calcolo parallelo, che definisce un insieme di interfacce per la creazione di processi paralleli, la comunicazione fra i processi e operazioni tipiche del calcolo parallelo, come meccanismi di sincronizzazione.
MPI, nelle sue implementazioni, fornisce uno strumento efficace per il calcolo parallelo su sistemi cluster o, nei casi più semplici, in sistemi multiprocessore/multicore.
MPI definisce propri tipi dati. Questo, ed altro, favorisce la portabilità su varie piattaforme, uno degli scopi di MPI.
MPI è rilasciato con licenza The 3-Clause BSD License
MPI non definisce librerie o codice. L’implementazione è libera, pertanto chiunque (capace), nel rispetto delle specifiche, può implementare una libreria MPI per il linguaggio che preferisce.
MPI è pensato per programmazione ad alte prestazioni, per questo le principali implementazioni sono per C/C++ e Fortran.
Le due implementazioni più note sono Open MPI e MPICH.
I programmi paralleli sono scomposti in sotto programmi (task, thread) che possono essere eseguiti in concomitanza. Alla difficoltà di suddividere un algoritmo in parti opportune per il parallelismo, si aggiungono altre problematiche, fra queste, la necessità di comunicare con i vari processi per coordinali, suddividere i dati da elaborare sui vari task e riunirli in un unico risultato.
Questa comunicazione non è semplice e pone diverse tematiche come l’accesso concorrente (in lettura o in scrittura) a dati.
I modi di comunicare sono principalmente due:
- Condivisione di memoria (shared memory), aree di memoria comuni a tutti i processi/processori consentono lo scambio dei dati.
- Comunicazione attraverso meccanismi di rete, quando ogni elaboratore/cpu/processo è il il responsabile della propria memoria (distribuited memory). MPI usa questo meccanismo.
Pur essendo destinato a cluster ‘impegnativi’ (si parla di HPC – High Performance Computing https://it.wikipedia.org/wiki/High_Performance_Computing ), MPI si può implementare su un Raspberry Pi, se non altro per provare programmi poco ‘impegnativi’, imparare e comunque implementare programmi paralleli. Pi 3 ha 4 core che possono lavorare in parallelo.
Inoltre, nulla vieta di mettere insieme alcuni Pi per realizzare un cluster a basso costo.
Volevo fare qualcosa di simile.
Su internet esistono diversi progetti di cluster con Raspberry Pi ma la maggior parte usano mpi4py, per python, la mia idea era di implementare MPI per C/C++.
Ho iniziato con Open MPI. In rete ho trovato qualcosa che non mi ha soddisfatto. L’installazione dell’eseguibile sembrava mancare di parti. Ho optato per ricompilare Open MPI su Pi: errori/orrori. Alcuni, credo, si riferissero ad istruzioni del processore di Pi per la gestione delle operazioni atomiche, non ho indagato troppo. Insomma non sono riuscito a farlo funzionare, magari qualcuno può darmi spunti. Col tempo, forse, ci riprovo.
Mi sono, poi, orientato a MPICH e ricompilato tutto su Pi. Funziona!
Di seguito le semplici, ma un po’ lunghe, operazioni da seguire.
Sul sito di MPICH, a questo indirizzo, è disponibile il download del sorgente.
Come root, nella cartella /root/bin ho scaricato il programma per linux:
wget http://www.mpich.org/static/downloads/3.2.1/mpich-3.2.1.tar.gz
Si espande il tutto:
tar -xzf mpich-3.2.1.tar.gz cd mpich-3.2.1.tar.gz
ora la cartella mpich-3.2.1 contiene tutto il codice sorgente. Preparare e compilare è un po’ lungo, ma funziona.
Si lancia:
./configure Il comando con l’opzione --help fornisce una miriade di opzioni, delle quali non mi sono curato molto. Io ho usato solo l’opzione --disable-fortran per evitare la compilazioni di librerie fortran. Sarà opportuno rivedere con più attenzione i vari parametri.
Quindi:
./configure --disable-fortran
Seguito da:
make make install
Attendere con pazienza…
Alla fine la prima prova può essere:
root@raspberrypi:~/bin/prg# mpiexec --version HYDRA build details: Version: 3.3b2 Release Date: Mon Apr 9 17:58:42 CDT 2018 CC: gcc CXX: g++ F77: F90: … …
Realizziamo il solito programma ‘Hello World’ (per me hw.c):
#include <stdio.h> #include "mpi.h" #define MASTER 0 int main(int argc, char **argv) { int numtask, taskid, len; char hostname[MPI_MAX_PROCESSOR_NAME]; // inizializza l’ambiente MPI MPI_Init(&argc, &argv); // numtask è il numero dei task MPI_Comm_size(MPI_COMM_WORLD, &numtask); // tasked è l’identificativo di uno specific task MPI_Comm_rank(MPI_COMM_WORLD, &taskid); // Identifica il nodo MPI_Get_processor_name(hostname, &len); // Stampo il messaggio. Questa istruzione, come quelle all’interno di MPI_Init // è eseguita da ogni singolo task il cui identificativo è taskid // quindi avrò la stampa di tante righe quanto i task // Per il task 0, definite come MASTER, stampo qualcosa di diverso. if (taskid == MASTER) MPI_Finalize(); }
Non resta che compilare:
mpicc -o hw hw.c
mpicc è una shell script che richiama gcc con le corrette opzioni e librerie. mpicc –help per avere più informazioni.
Lanciamo il programma:
mpirun -np 5 ./hw Hello world, 0, raspberrypi Sono MASTER, 0, raspberrypi Hello world, 1, raspberrypi Hello world, 3, raspberrypi Hello world, 4, raspberrypi Hello world, 2, raspberrypi
-np 5 crea a runtime 5 processi. E’ immaginabile un programma su una CPU con 4 core veda 4 come numero ragionevole di processi. Non è escluso che 5 o 6 processi forniscano migliori prestazioni (in termini di tempo di esecuzione), di certo un numero di processi molto più elevato delle CPU/core disponibili non porta vantaggi. Hello World non è certamente un programma adatto a fare test di prestazioni.
Come si nota, l’ordine di stampa è casuale: il processo per primo esegue printf, stampa il messaggio.
Se questo disposizione non va bene occorre coordinare i task per dare un ordine di esecuzione. Attenzione però: siamo in programmazione parallela. Se l’algoritmo parallelo è ben realizzato, i task sono il più possibile indipendenti, senza troppi ordini di esecuzione e vincoli di dipendenza..
Commenti recenti