/******************************************************************************/ /** @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; } /******************************************************************************/