/******************************************************************************/
/**
@file "MPI/C/Primer 2 - threads/mpithreads_both.c"
@brief Primer hibridnog MPI/PThreads programa.
Ovaj program ilustruje simultanu upotrebu MPI-a i POSIX niti. U osnovi to je
kombinacija koda koji racuna skalarni proizvod koristeci niti, i koda koji
koristi MPI za istu svrhu. Ovo je ujedno i poslednji primer koji pokazuje
napredak od sekvencijalnog programa do hibridnog MPI/Pthreads programa.
Ostale verzije mozete pronaci u fajlovima:@n @n
Pthreads/C/Primer 4 - mutex/dot_product_simple.c @n
Pthreads/C/Primer 4 - mutex/dot_product_threaded_mutex.c @n
MPI/C/Primer 2 - threads/mpithreads_mpi.c @n @n
Svu MPI komunikaciju unutar jednog cvora odradjuje glavna nit programa na
svakom od cvorova - ostale niti unutar tog cvora ne moraju biti svesne toga.
Izabrana je upotreba SPMD modela sa replikacijom glavnih podataka na svim
cvorovima. Za vece setove podataka savet je implementacija verzije koja je
stedljivija u pogledu memorije.@n @n
Autor ovog primera je Vijay Sonnad, IBM. Kod poslednje revizije, koju je izvrsio
Blaise Barney, je preuzet iz Pthreads tutorijala sa adrese
http://www.llnl.gov/computing/tutorials/pthreads/
i prilagodjen potrebama ovog kursa.
*/
/******************************************************************************/
#include
#include
#include
#include
/******************************************************************************/
/**
@struct DATA
@brief Ulazni podaci za obradu i razultat izvrsavanja
Sledeca struktura sadrzi neophodne informacije koje dozvoljavaju funkciji
dot_prod da pristupi njenim ulaznim podacima i da sacuva rezultate kako bi
im se moglo kasnije pristupiti. Struktura je ostala nepromenjena u odnosu
na sekvencijalnu verziju i Pthread verziju.
*/
typedef struct DATA {
/**
@property a
@brief Adresa prvog niza koji se koristi u obradi
*/
double* a;
/**
@property b
@brief Adresa drugog niza koji se koristi u obradi
*/
double* b;
/**
@property sum
@brief Rezultat obrade (sumiranja)
*/
double sum;
/**
@property vec_len
@brief Duzina niza nad kojima se vrsi obrada
*/
int vec_len;
} DOT_DATA;
/******************************************************************************/
/**
@def MAX_THRDS
@brief Maksimalni broj niti po svakom cvoru.
*/
#define MAX_THRDS 8
/**
@def VEC_LEN
@brief Duzina svakog vektora.
*/
#define VEC_LEN 100
/**
@var dot_str
@brief Globalno dostupne informacije neophodne dot_prod funkciji.
*/
DOT_DATA dot_str;
/**
@var call_thd
@brief Niz deskriptora niti.
*/
pthread_t call_thd[MAX_THRDS];
/**
@var mutex_sum
@brief Mutex promenljiva.
*/
pthread_mutex_t mutex_sum;
/******************************************************************************/
/**
@brief Funkcija koja racuna skalarni proizvod.
@param arg Pokazivac na redni broj niti.
@return Vraca genericki pokazivac na ?
Funkcija dot_prod je dozivela samo manje promene u odnosu na verzije koje
koriste niti ili MPI.
*/
void* dot_prod(void* arg)
{
/*
Zgodno je definisati i koristiti lokalne promenljive
*/
int i, start, end, my_thrd, len, num_thrds, my_id;
double my_sum, *x, *y;
my_thrd = (int) arg;
MPI_Comm_rank (MPI_COMM_WORLD, &my_id);
num_thrds = dot_str.num_thrds;
len = dot_str.veclen;
start = (my_id * num_thrds * len) + (my_thrd * len);
end = start + len;
x = dot_str.a;
y = dot_str.b;
my_sum = 0;
for (i = start; i < end ; i++)
{
my_sum += (x[i] * y[i]);
}
/*
Pre nego sto azuriramo vrednost u strukturi zakljucamo mutex. Nakon
azuriranja ponovo ga otkljucamo.
*/
pthread_mutex_lock(&mutex_sum);
printf("Task %d thread %d adding partial sum of %f to node sum of %f\n",
my_id, my_thrd, my_sum, dot_str.sum);
dot_str.sum += my_sum;
pthread_mutex_unlock (&mutex_sum);
pthread_exit((void*)0);
}
/******************************************************************************/
/**
@brief Glavni program
@param argc Broj argumenata komandne linije
@param argv Argumenti komandne linije
@return Vraca @b 0 ako je sve uspesno izvrseno
Kao i u ranijim verzijama, glavni program radi jako malo proracuna. U njemu se,
na svakom cvoru, kreiraju niti. Glavna nit izvrsava sve MPI pozive.
*/
int main(int argc, char* argv[]) {
int i, len = VEC_LEN;
int my_id, numprocs;
int nump1, num_thrds;
double *a, *b;
double nodesum, allsum;
int status;
pthread_attr_t attr;
/* MPI inicijalizacija */
MPI_Init (&argc, &argv);
MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
MPI_Comm_rank (MPI_COMM_WORLD, &my_id);
/* Alociramo i inicijalizujemo promenljive */
num_thrds = MAX_THRDS;
a = (double*) malloc (numprocs * num_thrds * len * sizeof(double));
b = (double*) malloc (numprocs * num_thrds * len * sizeof(double));
for (i = 0; i < len * numprocs * num_thrds; i++) {
a[i] = 1;
b[i] = a[i];
}
dot_str.veclen = len;
dot_str.a = a;
dot_str.b = b;
dot_str.sum = 0;
dot_str.num_thrds = MAX_THRDS;
/*
Kreiramo atribute niti koji specificiraju da glavne nit treba da
sacekaju da se niti koje su kreirale izvrse do kraja
*/
pthread_attr_init(&attr );
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
/* Inicijalizujemo mutex promenljivu */
pthread_mutex_init (&mutex_sum, NULL);
/*
Kreiramo niti unutar ovog cvora.
Niti treba da izvrsavaju dot_prod() funkciju
*/
for(i = 0; i < num_thrds; i++) {
pthread_create(&call_thd[i], &attr, dot_prod, (void*) i);
}
/* Oslobodimo objekat atributa niti posto nam vise nije potreban */
pthread_attr_destroy(&attr );
/* Cekamo da ostale niti unutar ovog cvora zavrse sa radom */
for(i = 0; i < num_thrds; i++) {
pthread_join(call_thd[i], (void**) &status);
}
nodesum = dot_str.sum;
printf("Task %d node sum is %f\n",my_id, nodesum);
/* Nakon izracunavanja, izvrsimo sumiranje rezultata svakog od cvorova */
MPI_Reduce(&nodesum, &allsum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (my_id == 0)
printf ("Done. MPI with threads version: sum = %f \n", allsum);
/* Zavrsimo MPI i oslobodimo alocirani prostor */
MPI_Finalize();
free (a);
free (b);
pthread_mutex_destroy(&mutex_sum);
return 0;
}
/******************************************************************************/