Ε-85: Ειδικά Θέµατα Λογισµικού Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων Προγραµµατισµός Νηµάτων Χειµερινό Εξάµηνο 2009-10 «Επαναληπτικές Ασκήσεις» Παναγιώτης Χατζηδούκας (Π.Δ. 407/80) E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 1 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 2 Αρχικοποίηση µιας Φοράς pthread_once_t once_block = PTHREAD_ONCE_INIT; pthread_mutex_t mutex; /* Initialization routine */ void once_init_routine(void) pthread_mutex_init(&mutex, NULL); /* Thread start routine that calls pthread_once */ void *thread_routine(void *arg) pthread_once(&once_block, once_init_routine);... main() pthread_create(&t, NULL, thread_routine, NULL); pthread_once(&once_block, once_init_routine);... E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 3 Ιδιωτικά εδοµένα Νηµάτων pthread_key_t key; pthread_once_t key_once = PTHREAD_ONCE_INIT; void once_routine(void) pthread_key_create(&key, NULL); void routine() long *value; value = pthread_setspecific(key); void *thread_routine(void *) long *value; pthread_once(&key_once, once_routine); value = malloc(sizeof(long)); *value = (long)pthread_self(); pthread_setspecific(key, value); routine(); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 4 Ακύρωση Νηµάτων pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; void *test_wait(void *arg) pthread_setcancelstate(pthread_cancel_enable, NULL); pthread_setcanceltype(pthread_cancel_asynchronous, NULL); Fibonacci (1/3) fib( n) if (n <= 1) return n; else return fib(n-1) + fib(n-2); pthread_mutex_lock(&mut); /* already locked */ return NULL; main()... pthread_mutex_lock(&mut); pthread_create(&pth, NULL, test_wait, NULL); sleep(5); pthread_cancel(pth); /* cancel blocked thread */ pthread_mutex_unlock(&mut); pthread_join(pth, NULL); /* join cancelled thread */ E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 5 void fib( n, *res) r1, r2; if (n <= 1) *res = n; else fib(n-1, &r1); fib(n-2, &r2); *res = r1 + r2; E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 6 1
struct fib_arg n; res; ; Fibonacci (2/3) void fib(struct arg *a) struct fib_arg a1, a2; r1, r2; if (a->n <= 1) a->res = a->n; else a1->n = a->n-1; a2->n = a->n-2; fib(&a1); fib(&a2); a->res = a1.res + a2.res; struct fib_arg n; res; ; Fibonacci (3/3) void *fib(void *arg) struct fib_arg *a = (struct fib_arg *) arg; struct fib_arg a1, a2; pthread_t t1, t2; r1, r2; if (a->n <= 1) a->res = a->n; else a1->n = a->n-1; a2->n = a->n-2; pthread_create(&t1, NULL, fib, (void *) &a1); pthread_create(&t2, NULL, fib, (void *) &a2); pthread_join(t1, NULL); pthread_join(t2, NULL); a->res = a1.res + a2.res; E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 7 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 8 #define N 10 void *routine(void *arg) long j; id = () arg; Σειρά Εκτέλεσης (1/2) prf("hello from thread:%d\n", id); main() pthread_t t[n]; for (i = 0; i < N; i++) pthread_create(&t[i], NULL, routine, (void *)i); for (i = 0; i < 10; i++) pthread_join(t[i], NULL); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 9 pthread_mutex_t m[n]; pthread_cond_t c[n]; void *routine(void *arg) long j; id = () arg; Σειρά Εκτέλεσης (2/2) if (id > 0) pthread_mutex_lock(&m[id]); pthread_cond_wait(&m[id], &c[id]); prf("hello from thread:%d\n", id); if (id > 0) pthread_mutex_unlock(&m[id]); if (id < N) pthread_cond_signal(&c[id+1]); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 10 Αναδροµικές Κλειδαριές (1/4) othr_init_nest_lock(othr_nest_lock_t *lock); othr_destroy_nest_lock(othr_nest_lock_t *lock); othr_set_nest_lock(othr_nest_lock_t *lock); othr_unset_nest_lock(othr_nest_lock_t *lock); typedef struct pthread_mutex_t lock; /* real lock */ pthread_mutex_t ilock; /* data lock */ pthread_cond_t cond; count; othr_t owner; othr_nest_lock_t; Αναδροµικές Κλειδαριές (2/4) othr_init_nest_lock(othr_nest_lock_t *lock) pthread_mutex_init(&lock->ilock, NULL); pthread_mutex_init(&lock->lock, NULL); lock->count = 0; pthread_cond_init(&lock->cond, 0); othr_destroy_nest_lock(othr_nest_lock_t *lock) pthread_mutex_destroy(&lock->lock); pthread_cond_destroy(&lock->cond); pthread_mutex_destroy(&lock->ilock); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 11 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 12 2
Αναδροµικές Κλειδαριές (3/4) othr_set_nest_lock(othr_nest_lock_t *lock) pthread_mutex_lock(&lock->ilock); if (pthread_mutex_trylock(&lock->lock) == 0) /* lock it */ lock->owner = pthread_self(); /* Get ownership */ lock->count++; else if (pthread_equal(lock->owner, pthread_self())) /* mine */ lock->count++; else /* someone else */ while ( pthread_mutex_trylock(&lock->lock) ) pthread_cond_wait(&lock->cond, &lock->ilock); lock->owner = pthread_self(); lock->count++; pthread_mutex_unlock(&lock->ilock); Αναδροµικές Κλειδαριές (4/4) othr_unset_nest_lock(othr_nest_lock_t *lock) pthread_mutex_lock(&lock->ilock); if (pthread_equal(lock->owner,pthread_self()) && lock->count > 0) lock->count--; if (lock->count == 0) pthread_mutex_unlock(&lock->lock); pthread_cond_signal(&lock->cond); pthread_mutex_unlock(&lock->ilock); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 13 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 14 Μέγιστο Πίνακα (1/4) double find_max(double *A, N) double maxi; maxi = A[0]; for (i = 0; i < N; i++) if (maxi < A[i]) maxi = A[i]; return maxi; struct thr_arg double *A; N; double *gmax; pthread_mutex_t *lock; *flag; id; nthr; ; Μέγιστο Πίνακα (2/4) void compute_bounds( N, me, nthr, *low, *high) *low = me*(n/nthr); *high = (me+1)*(n/nthr); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 15 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 16 void *find_max_(void *arg) struct thr_arg *a = (struct thr_arg *) arg; double *A = a->a; N = a->n, me = a->id, nthr = a->nthr; low, high; double lmax; compute_bounds(n, me, nthr, &low, &high); lmax = A[low]; for (i = low+1; i < high; i++) if (lmax < A[i]) lmax = A[i]; pthread_mutex_lock(a->lock); if (*a->flag == 0) *a->flag = 1; *a->gmax = lmax; else if (*a->gmax < lmax) *a->gmax = lmax; pthread_mutex_unlock(a->lock); Μέγιστο Πίνακα (3/4) E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 17 Μέγιστο Πίνακα (4/4) double find_max(double *A, N) double maxi; nthr = 2; flag = 0; struct thr_arg *a; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; pthread_t t[2]; for (i = 0; i < 2; i++) a = malloc(sizeof(struct thr_arg)); a->a = A; a->n = N; a->gmax = &maxi; a->lock = &m; a->flag = &flag; a->id = i; a->nthr = 2; pthread_create(&t[i], NULL, find_max_, a); for (i = 0; i < 2; i++) pthread_join(t[i], NULL); return maxi; E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 18 3
Μοντέλο Προγραµµατισµού OpenMP Χρησιµότητα οµών ιαµοίρασης Ακολουθιακός κώδικας for(i=0;i<n;i++) a[i] = a[i] + b[i]; Παράλληλο τµήµα OpenMP id, i, Nthrds, istart, iend; id = omp_get_thread_num(); Nthrds = omp_get_num_threads(); istart = id * N / Nthrds; iend = (id+1) * N / Nthrds; for(i=istart;i<iend;i++) a[i] = a[i] + b[i]; Παράλληλο τµήµα OpenMP µε omp for για διαµοίραση έργου #pragma omp for schedule(static) for(i=0;i<n;i++) a[i] = a[i] + b[i]; E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 19 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 20 Πρόγραµµα π Ακολουθιακή έκδοση static long num_steps = 100000; double step; double x, pi, sum = 0.0; for (i=1;i<= num_steps; i++) x = (i-0.5)*step; sum = sum + 4.0/(1.0+x*x); pi = step * sum; Έκδοση µε Παράλληλο Τµήµα static long num_steps = 100000; double step; #define NUM_THREADS 2 double x, pi, sum[num_threads]; omp_set_num_threads(num_threads) double x; id; id = omp_get_thread_num(); for (i=id, sum[id]=0.0;i< num_steps; i=i+num_threads) x = (i+0.5)*step; sum[id] += 4.0/(1.0+x*x); for(i=0, pi=0.0;i<num_threads;i++) pi += sum[i] * step; E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 21 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 22 Έκδοση µε οµή ιαµοίρασης Έργου static long num_steps = 100000; double step; #define NUM_THREADS 2 double x, pi, sum[num_threads]; omp_set_num_threads(num_threads) double x; id; id = omp_get_thread_num(); sum[id] = 0.0; #pragma omp for for (i=id, i< num_steps; i++) x = (i+0.5)*step; sum[id] += 4.0/(1.0+x*x); for(i=0, pi=0.0;i<num_threads;i++) pi += sum[i] * step; Έκδοση µε reduction static long num_steps = 100000; double step; #define NUM_THREADS 2 double x, pi, sum = 0.0; omp_set_num_threads(num_threads) for reduction(+:sum) private(x) for (i=1;i<= num_steps; i++) x = (i-0.5)*step; sum = sum + 4.0/(1.0+x*x); pi = step * sum; E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 23 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 24 4
Περιβάλλον εδοµένων Παράδειγµα µε χρήση των PRIVATE και FIRSTPRIVATE A, B, C; A = B = C = 1; private(b) firstprivate(c) Μέσα στο παράλληλο τµήµα : Το A είναι κοινό µεταξύ των νηµάτων και ίσο µε 1 Τα B και C είναι ιδιωτικά σε κάθε νήµα. Το B έχει ακαθόριστη αρχική τιµή Το C έχει αρχική τιµή 1 Μετά το παράλληλο τµήµα : Οι τιµές των Β και C είναι ακαθόριστες Τι τυπώνει το παρακάτω πρόγραµµα; #include <stdio.h> main() x = 2; num_threads(2) shared(x) if (omp_get_thread_num() == 0) x = 5; else prf("1: Thread# %d: x = %d\n", omp_get_thread_num(),x ); #pragma omp barrier if (omp_get_thread_num() == 0) prf("2: Thread# %d: x = %d\n", omp_get_thread_num(),x ); else prf("3: Thread# %d: x = %d\n", omp_get_thread_num(),x ); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 25 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 26 Παράδειγµα void sub(float *x, npos) iam, nt, ipos, istart; private(iam,nt,ipos,istart) iam = omp_get_thread_num(); nt = omp_get_num_threads(); ipos = npos / nt; /* size of partition */ istart = iam * ipos; /* starting array index */ if (iam == nt-1) /* last thread may do more */ ipos = npos - istart; subdomain(x, istart, ipos); void main() float array[10000]; sub(array, 10000); void subdomain(float *x, istart, ipos) for (i = 0; i < ipos; i++) x[istart+i] = 123.456; Παράδειγµα nowait #include <math.h> void a8( n, m, float *a, float *b, float *y, float *z) #pragma omp for nowait for (i=1; i<n; i++) b[i] = (a[i] + a[i-1]) / 2.0; #pragma omp for nowait for (i=0; i<m; i++) y[i] = sqrt(z[i]); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 27 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 28 counter = 0; #pragma omp threadprivate(counter) increment_counter() counter++; return(counter); Παράδειγµα threadprivate increment_counter_2() static counter_2 = 0; #pragma omp threadprivate(counter_2) counter_2++; return(counter_2); Εµφωλευµένος παραλληλισµός void work( i, j) void good_nesting( n) i, j; default(shared) #pragma omp for for (i=0; i<n; i++) shared(i, n) #pragma omp for for (j=0; j < n; j++) work(i, j); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 29 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 30 5
Συναρτησιακός παραλληλισµός (1/2) V = alpha(); W = beta(); X = gamma(v, W); Y = delta(); prf( %f\n, epsilon(x,y)); sections V = alpha(); W = beta(); Y = delta(); X = gamma(v, W); prf( %f\n, epsilon(x,y)); Συναρτησιακός παραλληλισµός (2/2) s V = alpha(); W = beta(); s X = gamma(v, W); Y = delta(); prf( %f\n, epsilon(x,y)); E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 31 E-85: Ε.Θ.Λ: Προγραµµατισµός Συστηµάτων Υψηλών Επιδόσεων 32 6