Τμήμα Μηχανικών Πληροφορικής Τ.Ε. Σχολή Τεχνολογικών Εφαρμογών Ακαδημαϊκό έτος 2016-2017 ΤΕΙ Ηπείρου - Άρτα Κατανεμημένα και Παράλληλα Συστήματα (εργαστήριο) Γκόγκος Χρήστος Εκφωνήσεις ασκήσεων εργαστηρίου 2 (pthreads) Άσκηση 1 Να γράψετε πρόγραμμα που να δημιουργεί 5 νήματα. Το κάθε νήμα απλά να εμφανίζει έναν τυχαίο ακέραιο αριθμό στο διάστημα [1,100] και να τερματίζει. Γράψτε τις εντολές μεταγλώττισης και εκτέλεσης του προγράμματος. Άσκηση 2 Να γράψετε πρόγραμμα που να δέχεται ως παράμετρο γραμμής εντολών έναν ακέραιο αριθμό και να εμφανίζει το άθροισμα όλων των θετικών ακεραίων που είναι μικρότεροι ή ίσοι του αριθμού αυτού (οι σχετικές μεταβλητές να δηλωθούν ως τύπου long long). Για τον υπολογισμό του αθροίσματος να χρησιμοποιεί νήματα. Ο αριθμός των νημάτων θα περνά επίσης ως παράμετρος γραμμής εντολών. Υπολογίστε το άθροισμα των πρώτων 10.000.000 ακέραιων αριθμών χρησιμοποιώντας 4 νήματα. Γράψτε τις εντολές μεταγλώττισης και εκτέλεσης του προγράμματος Άσκηση 3 Να γράψετε πρόγραμμα που να γεμίζει με τυχαίες ακέραιες τιμές στο διάστημα [1,10] δύο διανύσματα 1000 θέσεων το καθένα και να υπολογίζει το εσωτερικό τους γινόμενο (http://mathinsight.org/dot_product_matrix_notation). Για τον υπολογισμό του εσωτερικού γινομένου η εργασία να «σπάσει» σε 4 νήματα. Άσκηση 4 Σε έναν κύκλο με ακτίνα r η επιφάνεια του είναι πr 2 ενώ η επιφάνεια του περιγεγραμμένου στον κύκλο τετραγώνου είναι 4r 2. Εξετάζοντας το λόγο της επιφάνειας του κύκλου προς την επιφάνεια του τετραγώνου προκύπτει ότι θα πρέπει να ισούται με π/4. Αν πραγματοποιηθεί ένα πείραμα στο οποίο θα μετρούνται πόσα από ένα σύνολο τυχαίων σημείων πέφτουν εντός του κύκλου τότε ο λόγος του πλήθους των τυχαίων σημείων εντός του κύκλου προς το συνολικό πλήθος σημείων θα πρέπει και αυτός να ισούται με π/4. Να γράψετε πρόγραμμα που να υπολογίζει χρησιμοποιώντας τον τρόπο που αναφέρθηκε τον αριθμό π. Η εργασία να μοιράζεται σε έναν αριθμό νημάτων. Εκτελέστε το πρόγραμμα για πλήθος 300.000.000 σημείων και για 8 νήματα. Άσκηση 5 Δίνεται ο ακόλουθος κώδικας ο οποίος υπολογίζει με τη βοήθεια δύο νημάτων το άθροισμα των στοιχείων του πίνακα Α. Το πρώτο thread αθροίζει τα πρώτα 5 στοιχεία και το δεύτερο thread τα υπόλοιπα 5 στοιχεία του πίνακα. Συμπληρώστε τον κώδικα έτσι ώστε να είναι κρίσιμο τμήμα η εντολή sum+=q και στα δύο νήματα. 1
int A[10]={1,2,3,4,5,6,7,8,9,10; int sum = 0; // [1] void* thread1_work(void* arg){ int q=0; for(int i=0;i<5;i++) // [2] // [3] void* thread2_work(void* arg){ int q=0; for(int i=5;i<10;i++) // [4] // [5] int main(){ pthread_t thread1, thread2; // [6] pthread_create(&thread1, NULL, thread1_work, NULL); // [7] pthread_join(thread1, NULL); // [8] printf("the sum is %d\n", sum); // [9] a) pthread_create(&thread2, NULL, thread2_work, NULL); b) pthread_mutex_lock(&mx); c) pthread_mutex_unlock(&mx); d) pthread_mutex_t mx; e) pthread_mutex_init(&mx, NULL); f) pthread_mutex_destroy(&mx); g) pthread_join(thread2, NULL); Άσκηση 6 Μετατρέψτε τον κώδικα της άσκησης 5 έτσι ώστε να χρησιμοποιεί αναμονή σε εκρήγορση (busy wait) αντί για mutex έτσι ώστε να πετύχει το ίδιο αποτέλεσμα. 2
Λύσεις Άσκηση 1 #include <stdlib.h> #define T 5 void *thread_work(void *tid) { long mytid = (long)tid; srand(time(null) + mytid); long x = random() % 100 + 1; printf("thread %ld --> %ld\n", mytid, x); int main() { pthread_t threads[t]; for (long t = 0; t < T; t++) pthread_create(&threads[t], NULL, thread_work, (void *)t); for (long t = 0; t < T; t++) pthread_join(threads[t], NULL); pthreads_exercise01.c gcc pthreads_exercise01.c -o pthreads_exercise01 -lpthread./pthread_exercise01 Thread 4 --> 71 Thread 0 --> 10 Thread 3 --> 6 Thread 2 --> 77 Thread 1 --> 34 Άσκηση 2 #include <stdlib.h> int thread_num; long long x; long long sum = 0; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; void *thread_work(void *tid) { 3
long mytid = (long)tid; long long mysum = 0; long long stride = x / thread_num; long long left = mytid * stride + 1; long long right = mytid * stride + stride; if (right > x) x = right; for (long long i = left; i <= right; i++) mysum += i; printf("sum computed by thread %ld is %lld\n", mytid, mysum); pthread_mutex_lock(&lock); sum += mysum; pthread_mutex_unlock(&lock); int main(int argc, char *argv[]) { x = atoll(argv[1]); thread_num = atoi(argv[2]); pthread_t threads[thread_num]; for (long t = 0; t < thread_num; t++) pthread_create(&threads[t], NULL, thread_work, (void *)t); for (long t = 0; t < thread_num; t++) pthread_join(threads[t], NULL); printf("the total sum is %lld\n", sum); pthreads_exercise02.c gcc pthreads_exercise02.c -o pthreads_exercise02 -lpthread./pthreads_exercise02 10000000 4 sum computed by thread 1 is 9375001250000 sum computed by thread 0 is 3125001250000 sum computed by thread 2 is 15625001250000 sum computed by thread 3 is 21875001250000 The total sum is 50000005000000 Άσκηση 3 #include <stdlib.h> #include <time.h> #define N 1000 #define T 4 4
int A[N]; int B[N]; int sum = 0; pthread_mutex_t mx; void *thread_work(void *rank) { long myrank = (long)rank; for (int i = myrank * N / T; i < (myrank + 1) * N / T; i++) q += A[i] * B[i]; pthread_mutex_lock(&mx); pthread_mutex_unlock(&mx); int main() { srand(1821); for (int i = 0; i < N; i++) { A[i] = rand() % 10 + 1; B[i] = rand() % 10 + 1; for (int i = 0; i < N; i++) q += A[i] * B[i]; printf("dot product (serial)=%d\n", q); pthread_t threads[t]; pthread_mutex_init(&mx, NULL); for (long i = 0; i < T; i++) pthread_create(&threads[i], NULL, thread_work, (void *)i); for (int i = 0; i < T; i++) pthread_join(threads[i], NULL); printf("dot product (parallel)=%d\n", sum); pthread_mutex_destroy(&mx); pthreads_exercise03.c gcc pthreads_exercise03.c -o pthreads_exercise03 lpthread./pthreads_exercise03 dot product (serial)=29313 dot product (parallel)=29313 Άσκηση 4Α #include <math.h> 5
#include <stdlib.h> #include <time.h> int TOTAL_POINTS; // number of points int T; // number of threads int k = 0; // number of points inside the circle pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER; void *work(void *tid) { long mytid = (long)tid; srand48(time(null) * mytid); int points = TOTAL_POINTS / T; if ((mytid == T - 1) && (TOTAL_POINTS % T!= 0)) points += TOTAL_POINTS % T; int c = 0; for (int i = 0; i < points; i++) { double x, y, d; x = drand48(); // not thread safe y = drand48(); // not thread safe x = x * 2-1; y = y * 2-1; d = sqrt(x * x + y * y); if (d < 1) c++; printf("thread %ld points inside circle %d out of %d\n", mytid, c, points) ; pthread_mutex_lock(&mx); k += c; pthread_mutex_unlock(&mx); int main(int argc, char **argv) { TOTAL_POINTS = atoi(argv[1]); T = atoi(argv[2]); pthread_t *thread_handles; thread_handles = malloc(sizeof(pthread_t) * T); for (long i = 0; i < T; i++) pthread_create(&thread_handles[i], NULL, work, (void *)i); for (long i = 0; i < T; i++) pthread_join(thread_handles[i], NULL); double pi = (double)k / (double)total_points * 4; printf("pi estimation %f\n", pi); pthread_mutex_destroy(&mx); free(thread_handles); 6
pthreads_exercise04a.c gcc pthreads_exercise04b.c -o pthreads_exercise04b -lm -lpthread -std=gnu99./pthreads_exercise04a 300000000 10 Thread 0 points inside circle 23465263 out of 30000000 Thread 6 points inside circle 23468113 out of 30000000 Thread 9 points inside circle 23460020 out of 30000000 Thread 8 points inside circle 23466067 out of 30000000 Thread 7 points inside circle 23467442 out of 30000000 Thread 5 points inside circle 23446137 out of 30000000 Thread 4 points inside circle 23440407 out of 30000000 Thread 1 points inside circle 23444015 out of 30000000 Thread 3 points inside circle 23446295 out of 30000000 Thread 2 points inside circle 23443161 out of 30000000 PI estimation 3.1272921 Άσκηση 4Β (με χρήση της GNU Scientific Library) #include <math.h> #include <stdlib.h> #include <gsl/gsl_rng.h> // GNU Scientific Library int TOTAL_POINTS; // number of points int T; // number of threads int k = 0; // number of points inside the circle pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER; void *work(void *tid) { long mytid = (long)tid; gsl_rng *rng = gsl_rng_alloc(gsl_rng_default); gsl_rng_set(rng, time(null) * mytid); int points = TOTAL_POINTS / T; if ((mytid == T - 1) && (TOTAL_POINTS % T!= 0)) points += TOTAL_POINTS % T; int c = 0; for (int i = 0; i < points; i++) { double x, y, d; x = gsl_rng_uniform(rng); // thread_safe random number generation y = gsl_rng_uniform(rng); x = x * 2-1; y = y * 2-1; d = sqrt(x * x + y * y); 1 Λάθος αποτέλεσμα λόγω του ότι η drand48 δεν είναι thread_safe 7
; if (d < 1) c++; gsl_rng_free(rng); printf("thread %ld points inside circle %d out of %d\n", mytid, c, points) pthread_mutex_lock(&mx); k += c; pthread_mutex_unlock(&mx); int main(int argc, char **argv) { TOTAL_POINTS = atoi(argv[1]); T = atoi(argv[2]); pthread_t *thread_handles; thread_handles = malloc(sizeof(pthread_t) * T); for (long i = 0; i < T; i++) pthread_create(&thread_handles[i], NULL, work, (void *)i); for (long i = 0; i < T; i++) pthread_join(thread_handles[i], NULL); double pi = (double)k / (double)total_points * 4; printf("pi estimation %f\n", pi); pthread_mutex_destroy(&mx); free(thread_handles); pthreads_exercise04b.c gcc pthreads_exercise04b.c -o pthreads_exercise04b -lm -lpthread -lgsl -lgslcblas./pthreads_exercise04b 300000000 10 Thread 0 points inside circle 23562203 out of 30000000 Thread 8 points inside circle 23560752 out of 30000000 Thread 5 points inside circle 23558489 out of 30000000 Thread 2 points inside circle 23562997 out of 30000000 Thread 3 points inside circle 23563739 out of 30000000 Thread 4 points inside circle 23562246 out of 30000000 Thread 1 points inside circle 23560074 out of 30000000 Thread 6 points inside circle 23561736 out of 30000000 Thread 7 points inside circle 23561579 out of 30000000 Thread 9 points inside circle 23560569 out of 30000000 PI estimation 3.141525 8
Άσκηση 5 int A[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10; int sum = 0; pthread_mutex_t mx; void *thread1_work(void *arg) { for (int i = 0; i < 5; i++) pthread_mutex_lock(&mx); pthread_mutex_unlock(&mx); void *thread2_work(void *arg) { for (int i = 5; i < 10; i++) pthread_mutex_lock(&mx); pthread_mutex_unlock(&mx); int main() { pthread_t thread1, thread2; pthread_mutex_init(&mx, NULL); pthread_create(&thread1, NULL, thread1_work, NULL); pthread_create(&thread2, NULL, thread2_work, NULL); pthread_join(thread1, NULL); pthread_join(thread2, NULL); printf("the sum is %d\n", sum); pthread_mutex_destroy(&mx); gcc pthreads_exercise05.c -o pthreads_exercise05 lpthread./pthreads_exercise05 the sum is 55 9
Άσκηση 6 int A[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10; int sum = 0; int flag = 0; void *thread1_work(void *arg) { for (int i = 0; i < 5; i++) while(flag!=0); flag++; void *thread2_work(void *arg) { for (int i = 5; i < 10; i++) while(flag!=1); int main() { pthread_t thread1, thread2; pthread_create(&thread1, NULL, thread1_work, NULL); pthread_create(&thread2, NULL, thread2_work, NULL); pthread_join(thread1, NULL); pthread_join(thread2, NULL); printf("the sum is %d\n", sum); gcc pthreads_exercise06.c -o pthreads_exercise06 lpthread./pthreads_exercise05 the sum is 55 10