Message Passing Interface
Minimális MPI program
A legegyszerűbb MPI programnak is tartalmaznia kell az MPI_Init
és MPI_Finalize
hívásokat, így például az alábbi formában képzelhető el:
A program fordítása:
gcc hello.c -o hello -l mpi
A program futtatása:
mpirun hello
Master-slave
Az MPI környezet a programból elérhető teszi, hogy az éppen hogy lett elindítva. A következő példában azt láthatjuk, hogy hogyan tudjuk megállapítani, hogy több folyamat közül melyik-melyik.
#include <mpi/mpi.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
int rank;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
printf("[%d] I am the master!\n", rank);
}
else {
printf("[%d] I am just a slave.\n", rank);
}
MPI_Finalize();
return 0;
}
A programot fordítani a következő parancs kiadásával tudjuk:
gcc master_slave.c -o master_slave -l mpi
Futtatáshoz adjuk meg, hogy a processzek száma 2 legyen:
mpirun -np 2 master_slave
A program kimenetének az elvártak szerint a következő sorokat kell tartalmaznia:
[0] I am the master!
[1] I am just a slave.
Érdemes azt észrevenni/kipróbálni, hogy futtatástól függően a kiíratások sorrendje változhat.
Blokkoló üzenet küldése
Az üzenet küldésénél a blokkolás azt jelenti, hogy a program csak az üzenet elküldése után folytatja a futását.
#include <mpi/mpi.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
int rank;
int message;
int destination;
int tag;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
printf("[%04d] I am the sender!\n", rank);
message = 42;
destination = 1;
tag = 1;
printf("[%04d] Start sending ...\n", rank);
MPI_Send(&message, 1, MPI_INT, destination, tag, MPI_COMM_WORLD);
printf("[%04d] Message has sent!\n", rank);
}
else {
printf("[%04d] I am the receiver!\n", rank);
printf("[%04d] Wait for a message ...\n", rank);
MPI_Recv(&message, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
printf("[%04d] The received message is %d.\n", rank, message);
printf("[%04d] The sender's rank is %d.\n", rank, status.MPI_SOURCE);
}
MPI_Finalize();
return 0;
}
Néhány észrevétel:
Az
MPI_Send
ésMPI_Recv
függvény visszatérési értéke ebben a rövid példában nincs ellenőrízve. A függvény egy hibakódot ad vissza, amellyel ellenőrízni lehet, hogy sikeres volt-e a küldés, vagy ha nem, akkor miért (http://web.mit.edu/22.00J/www/www3/MPI_Send.html).Futtatásonként a kiírt üzenetek sorrendje eltérhet.
A program feltételezi, hogy legalább 2 process van.
Szintén feltételezés, hogy a címzett rangja 1.
A
message
változó kétféle módon kerül felhasználásra a küldő és fogadó oldalon.
Átlagolós példa
Tegyük fel, hogy készíteni szeretnénk egy olyan programot, amelyben van egy megkülönböztetett (master) folyamat, amelyik a többi folyamattól vár egy-egy lebegőpontos értékeket, majd ezek átlagát számítja ki a többi által összességében becsült értékként.
Ahhoz, hogy a master folyamat tudja, hogy mikor érkezik be számára az utolsó érték, az egyszerűbb megoldás az, hogy ha kiszámítja a rajta kívül lévő folyamatok számát. Ez az MPI_Comm_Size
függvénnyel lekérdezhető.
Az egyszerűség kedvéért most feltételezzük azt, hogy minden folyamat a saját rangját küldi át a master-nek (így az eredmény a rangok átlaga lesz majd).
Blokkoló hívás segítségével a következőképpen oldható meg a probléma:
#include <mpi/mpi.h>
#include <stdio.h>
void send_guess(int rank)
{
float guess;
int destination;
int tag;
guess = rank;
destination = 0;
tag = 1;
printf("[%04d] Send %f to 0 ...\n", rank, guess);
MPI_Send(&guess, 1, MPI_FLOAT, destination, tag, MPI_COMM_WORLD);
printf("[%04d] Message has sent!\n", rank);
}
void collect_guesses(int rank)
{
int n_tasks;
int n_received_guesses;
float guess;
MPI_Status status;
float sum;
MPI_Comm_size(MPI_COMM_WORLD, &n_tasks);
n_received_guesses = 0;
printf("[%04d] I wait %d guesses.\n", rank, n_tasks - 1);
sum = 0.0;
while (n_received_guesses < n_tasks - 1) {
MPI_Recv(&guess, 1, MPI_FLOAT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
printf("[%04d] I received %f from %d.\n", rank, guess, status.MPI_SOURCE);
sum += guess;
++n_received_guesses;
}
printf("[%04d] All guesses has received!\n", rank);
printf("[%04d] The result is %f.\n", rank, sum / n_received_guesses);
}
int main(int argc, char* argv[])
{
int rank;
float guess;
int destination;
int tag;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank > 0) {
send_guess(rank);
}
else {
collect_guesses(rank);
}
MPI_Finalize();
return 0;
}
A probléma megoldható az MPI_Reduce
függvényének a segítségével is.
Ehhez itt található egy részletes leírás: https://mpitutorial.com/tutorials/mpi-reduce-and-allreduce/
Ezzel a következő formában képzelhető el:
#include <mpi/mpi.h>
#include <stdio.h>
void send_guess(int rank)
{
float guess;
float result;
guess = rank;
result = 0.0;
printf("[%04d] I guess %f ...\n", rank, guess);
MPI_Reduce(&guess, &result, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);
printf("[%04d] I will return.\n", rank);
}
void collect_guesses(int rank)
{
int n_tasks;
float guess;
float result;
MPI_Comm_size(MPI_COMM_WORLD, &n_tasks);
printf("[%04d] The number of tasks is %d.\n", rank, n_tasks);
printf("[%04d] Start reduction ...\n", rank);
guess = 0.0;
result = 0.0;
MPI_Reduce(&guess, &result, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);
result /= (n_tasks - 1);
printf("[%04d] The result is %f.\n", rank, result);
}
int main(int argc, char* argv[])
{
int rank;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank > 0) {
send_guess(rank);
}
else {
collect_guesses(rank);
}
MPI_Finalize();
return 0;
}
Figyelem
A MPI_Reduce
függvény esetében figyelni kell a függvény paramétereinek az inicializálása!
A függvény a szinkronizációt ugyan elvégzi, de a kezdeti értékek beállítása esetleges.
A program a következő tömörebb formába is átírható:
#include <mpi/mpi.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
int rank;
int n_tasks;
float guess;
float result;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
MPI_Comm_size(MPI_COMM_WORLD, &n_tasks);
printf("[%04d] The number of tasks is %d.\n", rank, n_tasks);
guess = 0.0;
}
else {
guess = rank;
printf("[%04d] I guess %f ...\n", rank, guess);
}
result = 0.0;
printf("[%04d] Start reduction ...\n", rank);
MPI_Reduce(&guess, &result, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);
if (rank == 0) {
result /= (n_tasks - 1);
printf("[%04d] The result is %f.\n", rank, result);
}
MPI_Finalize();
return 0;
}