Υ07 Παράλληλα Συστήματα 2017-18 27/3/2018 Προγραμματισμός συστημάτων κοινόχρηστης μνήμης (IΙ)
Shared address space / shared variables Τι χρειάηεται κανείσ για να προγραμματίςει ςε αυτό το μοντζλο: Οντότθτεσ εκτζλεςθσ (νιματα, διεργαςίεσ) Δθμιουργία, διαχείριςθ Όχι άμεση ζννοια στο OpenMP Κοινζσ μεταβλθτζσ μεταξφ των οντοτιτων Οριςμόσ μεταβλθτϊν (τι είναι κοινό και πωσ ορίηεται) Τισ διαβάηουν και τισ τροποποιοφν όλεσ οι διεργαςίεσ Οι καθολικζς μεταβλητζς (και όχι μόνο) στο OpenMP Αμοιβαίοσ αποκλειςμόσ Π.χ. κλειδαριζσ Κλειδαριζς και στο OpenMP Συγχρονιςμόσ Π.χ. κλιςεισ φραγισ (barrier calls) Κλήσεις φραγής, άμεσες και ζμμεσες 2
Γιατί OpenMP; Απλό! «Αυξθτικό» (δθλαδι απλά προςκζτεισ λίγο-λίγο παραλλθλιςμό ςτο υπάρχον ςειριακό πρόγραμμα) Όχι πάντα τόςο απλό, βζβαια! Υποςτθρίηεται ςχεδόν παντοφ πλζον, ιδιαίτερα δθμοφιλζσ Πρόςφατα (v4.0, v4.5) θ εμβζλειά του επεκτείνεται και ςτισ επιπλζον ςυςκευζσ / devices ενόσ ςυςτιματοσ (ςυνεπεξεργαςτζσ, επιταχυντζσ, GPUs, κλπ) 3
Υπενκφμιςθ: υπολογιςμόσ του π = 3,14 Αρικμθτικι ολοκλιρωςθ 1 π 0 4 1 x2 4 3,5 3 3 2,5 2,5 2 2 1,5 1,5 1 1 0,5 0,5 0 0 0 0 0,1 1 2 3 4 5 6 7 0,2 0,3 0,4 0,5 0,6 0,7 διάστημα i (π λάτος διαστημάτων x W = 0,1) 8 0,8 9 10 0,9 1 #define N 1000000 N 1 4W 2 1 i 0 1 (i 2)W double pi = 0.0, W = 1.0/N; int main() { int i; for (i = 0; i < N; i++) pi += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); printf("pi = %.10lf\n", pi); return 0; 4 Y07 -- ΠΑΡΑΛΛΗΛΑ Τ ΣΗΜΑΣΑ
Με νιματα, βελτιςτοποιθμζνο #define NPROCS 2 /* dual core */ #define N 10000000 /* Για ακρίβεια (ίδια με ςειριακό) */ #define WORK N/NPROCS double pi = 0.0, W = 1.0/N; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; void *thrfunc(void *iter) { int i, me = (int) iter; double mysum = 0.0; for (i = me*work; i < (me+1)*work; i++) mysum += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); pthread_mutex_lock(&lock); pi += mysum; pthread_mutex_unlock(&lock); int main() { int i; pthread_t tids[nprocs]; for (i = 0; i < NPROCS; i++) /* νήματα = # επεξεργατών */ pthread_create(&tids[i], NULL, thrfunc, (void *) i); for (i = 0; i < NPROCS; i++) pthread_join(tids[i], NULL); printf("pi = %.10lf\n", pi); return 0; 5
Με OpenMP #include <omp.h> #define N 1000000 double pi = 0.0, W = 1.0/N; int main () { int i; #pragma omp parallel for reduction(+:sum) for (i=0; i < N; i++) #define N 1000000 pi += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); double pi = 0.0, W = 1.0/N; printf("pi = %.10lf\n", pi); return 0; int main() { int i; for (i = 0; i < N; i++) pi += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); printf("pi = %.10lf\n", pi); return 0; 6
Ειςαγωγι ςτο OpenMP OpenMP: API για τθ ςυγγραφι πολυνθματικϊν εφαρμογϊν Σφνολο οδηγιών προσ τον μεταγλωττιςτι και ςυναρτήςεων βιβλιοκικθσ, διακζςιμο ςτον προγραμματιςτι παράλλθλων ςυςτθμάτων Διευκολφνει τθ ςυγγραφι πολυνθματικϊν προγραμμάτων ςε Fortran, C και C++, χωρίσ να τροποποιεί τθ βαςικι γλϊςςα Πρότυπο που ςυγκεντρϊνει τθν εμπειρία αρκετϊν χρόνων ςε προγραμματιςμό πολυεπεξεργαςτικϊν ςυςτθμάτων Εδϊ και > 15 χρόνια υποςτιριξθ από μεγάλεσ εταιρείεσ / οργανιςμοφσ: Intel, SUN/ORACLE, IBM, HP, SGI, GNU GCC >= 4.2 Επίςθσ, ερευνθτικοί compilers: Omni (Ιαπωνία), NANOS/Mercurium (Ιςπανία) OpenUH (ΗΠΑ), OMPi (Ελλάδα - UoI) 7
Προγραμματιςτικό Μοντζλο Παραλλθλιςμόσ τφπου Fork-Join: Το νιμα-αρχθγόσ δθμιουργεί ομάδα νθμάτων ςφμφωνα με τισ ανάγκεσ. Σε κάκε περιοχι του κϊδικα που χρειάηεται: 1. Δθμιουργεί νιματα 2. Συμμετζχει ςτουσ υπολογιςμοφσ 3. Περιμζνει τον τερματιςμό όλων των νθμάτων τθσ ομάδασ Ο παραλλθλιςμόσ προςτίκεται βακμιαία Το ακολουκιακό πρόγραμμα εξελίςςεται ςε παράλλθλο πρόγραμμα 8
Τυπικι Χριςθ Σθμαντικόσ ςτόχοσ του OpenMP αποτελεί θ εφκολθ παραλλθλοποίθςθ βρόχων επανάλθψθσ: Βρεσ τα πιο χρονοβόρα loops. Μοίραςε τισ επαναλιψεισ μεταξφ νθμάτων. Διαίρεςθ loop μεταξφ πολλαπλϊν νθμάτων int main() { double Res[1000]; for (int i=0;i<1000;i++) { do_huge_comp(res[i]);... int main() { double Res[1000]; #pragma omp parallel for for (int i=0;i<1000;i++) { do_huge_comp(res[i]);... Ακολουκιακό πρόγραμμα Παράλλθλο πρόγραμμα 9
Σφνταξθ Οδθγιϊν Οι περιςςότερεσ «εντολζσ» OpenMP είναι directives (οδθγίεσ) προσ τον compiler. Για τθν C και C++, δίνονται ωσ pragmas και ζχουν τθ μορφι: #pragma omp construct [clause [clause] ] Για τθ Fortran, τα directives ζχουν μία από τισ ακόλουκεσ μορφζσ σχολίων: C$OMP construct [clause [clause] ]!$OMP construct [clause [clause] ] *$OMP construct [clause [clause] ] Αφοφ οι οδθγίεσ είναι pragmas ι ςχόλια: ζνα πρόγραμμα OpenMP μπορεί να μεταγλωττιςτεί από compilers που δεν υποςτθρίηουν OpenMP οι τελευταίοι απλά αγνοοφν τα directives Προκφπτει «νόμιμο», ςειριακό πρόγραμμα 10
Παράλλθλα Τμιματα Νιματα δθμιουργοφνται ςτο OpenMP (ςτθ C/C++) με τθν οδθγία omp parallel. Για παράδειγμα, για να δθμιουργθκεί ζνα παράλλθλο τμιμα με 4 νιματα: double A[1000]; #pragma omp parallel num_threads(4) { int ID = omp_get_thread_num(); pooh(id,a); Κάκε νιμα εκτελεί για λογαριαςμό του τον κϊδικα μζςα ςτο δομθμζνο block του παράλλθλου τμιματοσ Κάκε νιμα καλεί τθν pooh(id) για ID = 0 ζωσ 3 11
Παράλλθλα Τμιματα Κάκε νιμα εκτελεί για λογαριαςμό του τον ίδιο κϊδικα Ζνα μοναδικό αντίγραφο του Α μοιράηεται (κοινόχρθςτο) μεταξφ των νθμάτων Η εκτζλεςθ ςυνεχίηεται μόνο όταν ζχουν τελειϊςει όλα τα νιματα (barrier) double A[1000]; double A[1000]; #pragma omp parallel num_threads(4) { int ID = omp_get_thread_num(); pooh(id,a); printf("all done\n"); (4 threads) pooh(0,a) pooh(1,a) pooh(2,a) pooh(3,a) printf("all done\n"); 12
Μερικζσ Πρϊτεσ Λεπτομζρειεσ Dynamic mode (default): Ο αρικμόσ των νθμάτων που χρθςιμοποιοφνται για τθν εκτζλεςθ παράλλθλων τμθμάτων μπορεί να διαφζρει μεταξφ διαφορετικϊν τμθμάτων Ο οριςμόσ του αρικμοφ των νθμάτων (omp_set_num_threads()) αφορά ςτον μζγιςτο αρικμό νθμάτων και ενδεχομζνωσ θ εκτζλεςθ να γίνει με λιγότερα νιματα Non-dynamic mode: Ο αρικμόσ των νθμάτων είναι ακριβώσ αυτόσ που κακορίηεται από τον προγραμματιςτι (δεν επιτρζπεται ςτον μεταφραςτι να «παίξει») Το OpenMP υποςτθρίηει εμφωλευμζνα παράλλθλα τμιματα, όμωσ Ζνασ compiler ενδεχομζνωσ να επιλζξει να εκτελζςει ςειριακά όλα τα επίπεδα μετά το 1ο 13
Οδθγίεσ Διαμοίραςθσ Ζργου (workshare directives) Ακολουκιακόσ κϊδικασ for(i=0;i<n;i++) { a[i] = a[i] + b[i]; Παραλλθλοποιθμζνοσ με OpenMP, τμθματικι δρομολόγθςθ: #pragma omp parallel { int 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 ζχει κάτι πιο εφκολο: #pragma omp parallel #pragma omp for schedule(static) for(i=0;i<n;i++) { a[i] = a[i] + b[i]; 14
Οδθγίεσ Διαμοίραςθσ Ζργου: for Το omp for κατανζμει τισ επαναλιψεισ ενόσ loop μεταξφ των νθμάτων μιασ ομάδασ. Υποχρεωτικά ακολουκεί βρόχοσ for. #pragma omp parallel {... #pragma omp for for (I=0;I<N;I++) { ΝEAT_STUFF(I);... Εξ οριςμοφ υπονοείται barrier ςτο τζλοσ του omp for Για να αφαιρεκεί το barrier χρθςιμοποιοφμε τθν φράςθ nowait ςτθν οδθγία omp for. 15
Οδθγίεσ Διαμοίραςθσ Ζργου: sections Η δομι διαμοίραςθσ ζργου omp sections ανακζτει ζνα διαφορετικό δομθμζνο block ςε κάκε νιμα τθσ ομάδασ #pragma omp parallel {... #pragma omp sections {... #pragma omp section x_calculation(); #pragma omp section y_calculation(); #pragma omp section z_calculation(); Εξ οριςμοφ υπονοείται barrier ςτο τζλοσ του omp sections Για να αφαιρεκεί το barrier χρθςιμοποιοφμε τθν φράςθ nowait 16
Οδθγίεσ «Διαμοίραςθσ» Ζργου: single Η οδθγία διαμοίραςθσ ζργου omp single ανακζτει τον κϊδικα που ακολουκεί ςε ζνα και μοναδικό νιμα τθσ ομάδασ #pragma omp parallel {... #pragma omp single {... calc(); Οποιοδιποτε από τα νιμα ςυναντιςει το single, μπορεί να το εκτελζςει ενϊ τα υπόλοιπα όχι. Εξ οριςμοφ υπονοείται barrier ςτο τζλοσ του omp single Για να αφαιρεκεί το barrier χρθςιμοποιοφμε τθ φράςθ nowait 17
Συνδυαςμοί οδθγιϊν (combined directives) Για διευκόλυνςθ, χριςθ ενόσ pragma αντί δφο. Συνδυάηεται θ οδθγία omp parallel με δομζσ διαμοίραςθσ ζργου omp for ι omp sections. Παράδειγμα: #pragma omp parallel for for (i=0;i<n;i++) { NEAT_STUFF(i); Δεν υπάρχει parallel single (δεν ζχει και νόθμα) 18
Υπολογιςμόσ του π (μζχρι τϊρα) #define N 1000000 double pi = 0.0, W = 1.0/N; int main() { int i; for (i = 0; i < N; i++) pi += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); printf("pi = %.10lf\n", pi); return 0; #define N 1000000 double pi = 0.0, W = 1.0/N; int main () { #pragma omp parallel { int i, mysum = 0.0; #pragma omp for for (i=0; i < N; i++) mysum += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); #pragma omp critical pi += mysum; printf("pi = %.10lf\n", pi); return 0; 19
Περιβάλλον δεδομζνων (Ι) Οι global μεταβλθτζσ είναι κοινόχρθςτεσ μεταξφ των νθμάτων Όμωσ δεν είναι τα πάντα κοινά... Οι μεταβλθτζσ ςτθ ςτοίβα του κάκε νιματοσ είναι ιδιωτικζσ Οι μεταβλθτζσ που ορίηονται μζςα ςε ζνα δομθμζνο block εντολϊν είναι ιδιωτικζσ. Το OpenMP επιτρζπει τθν αλλαγι των χαρακτθριςτικών κοινοχρθςίασ (sharing attributes) των μεταβλθτϊν 20
Περιβάλλον δεδομζνων (ΙΙ) Τα χαρακτθριςτικά κοινοχρθςίασ των μεταβλθτϊν κακορίηονται είτε ζμμεςα (implicitly) είτε άμεςα (explilcitly) Ζμμεςα κακορίηονται ανάλογα με το πϊσ ορίηονται / χρθςιμοποιοφνται. Για παράδειγμα, ςτισ παράλλθλεσ περιοχζσ: όποια μεταβλθτι είναι οριςμζνθ πριν από μία παράλλθλθ περιοχι και χρθςιμοποιείται μζςα ςε αυτιν, κεωρείται αυτόματα ωσ κοινόχρθςτθ μεταξφ των νθμάτων τθσ ομάδασ, ακόμα και αν δεν είναι global. Ο προγραμματιςτισ μπορεί, όμωσ, να παρζμβει και να κακορίςει άμεςα τθν κοινοχρθςία των μεταβλθτϊν με ειδικζσ φράςεισ (clauses). 21 Y07 -- ΠΑΡΑΛΛΗΛΑ Τ ΣΗΜΑΣΑ
Φράςεισ κακοριςμοφ χαρακτθριςτικϊν κοινοχρθςίασ (Ι) Ο προγραμματιςτισ μπορεί να κακορίςει ρθτά τα χαρακτθριςτικά αποκικευςθσ των μεταβλθτϊν χρθςιμοποιϊντασ μία από τισ ακόλουκεσ φράςεισ shared(<μεταβλητέσ>) private(<μεταβλητέσ>) firstprivate(<μεταβλητέσ>) threadprivate(<μεταβλητέσ>) Η τιμι μιασ ιδιωτικισ μεταβλθτισ εντόσ μίασ περιοχισ διαμοίραςθσ ζργου μπορεί να «μεταδοκεί» εκτόσ τθσ περιοχισ με τθν: lastprivate(<μεταβλητέσ>) H default ςυμπεριφορά μπορεί να μεταβλθκεί με τθ: default(private shared none) Όλεσ οι εντολζσ δεδομζνων εφαρμόηονται ςε παράλλθλα τμιματα και δομζσ διαμοίραςθσ ζργου εκτόσ τθσ shared(), θ οποία εφαρμόηεται μόνο ςε παράλλθλα τμιματα. Όλεσ οι παραπάνω εντολζσ ζχουν ιςχφ ςτο lexical extent τθσ εντολισ OpenMP (δθλαδι ανάμεςα από τα άγκιςτρα που κακορίηουν το block). 22
Φράςεισ κακοριςμοφ χαρακτθριςτικϊν κοινοχρθςίασ (ΙΙ) shared(x,y) Οι μεταβλθτζσ x, y κα είναι κοινόχρθςτεσ ςτθν παράλλθλθ περιοχι που ακολουκεί. private(x,y) Οι μεταβλθτζσ x, y κα είναι ιδιωτικζσ για κάκε νιμα ςτθν περιοχι που ακολουκεί. firstprivate(x,y) Οι μεταβλθτζσ x, y κα είναι ιδιωτικζσ για κάκε νιμα ςτθν περιοχι που ακολουκεί, αλλά κα αρχικοποιθκοφν με τθν τιμι που ζχει θ αντίςτοιχθ αρχικι (original) μεταβλθτι. lastprivate(x,y) Οι μεταβλθτζσ x, y κα είναι ιδιωτικζσ για κάκε νιμα ςτθν περιοχι που ακολουκεί, θ οποία είναι υποχρεωτικά omp for ι omp sections. Στο τζλοσ τθσ περιοχισ, θ αντίςτοιχθ αρχικι (original) μεταβλθτι κα τροποποιθκεί από το νιμα που κα εκτελζςει τθν τελευταία επανάλθψθ του loop (omp for) ι το τελευταίο section (omp sections). threadprivate(x,y) Δεν είναι φράςθ, αλλά αυτόνομθ οδηγία που τοποκετείται κοντά ςτο ςθμείο που δθλϊνονται οι μεταβλθτζσ. Οι μεταβλθτζσ x, y κα είναι ιδιωτικζσ ςε κάκε νιμα που κα δθμιουργθκεί (ορίηεται μόνο μία φορά, ςτο ςθμείο που γίνεται θ διλωςθ των global μεταβλθτϊν x και y). 23
Παράδειγμα φράςεων δεδομζνων Παράδειγμα με χριςθ των private() και firsprivate() int A, B, C; A = B = C = 1; #pragma omp parallel shared(a) private(b) firstprivate(c) { #pragma omp single A++; B++; C++; printf("%d, %d, %d", A,B,C); printf("%d, %d, %d", A,B,C); Τι κα τυπωκεί μζςα ςτο παράλλθλο τμιμα; Το κάκε νιμα κα τφπωνε: 2, <τυχαία τιμή>, 2 Τι κα τυπωκεί μετά το παράλλθλο τμιμα? 2, 1, 1 Αν είχαμε #pragma omp single nowait τι κα τυπωνόταν; 24
Φράςθ default Κανονικά, όλεσ οι μεταβλθτζσ πρζπει να προςδιοριςτοφν μζςα από κάποια φράςθ (shared, private, firstprivate κλπ) προκειμζνου να καταλάβει ο μεταφραςτισ πϊσ να τισ χειριςτεί Το OpenMP επιτρζπει να ΜΗΝ προςδιοριςτοφν (ζμμεςοσ κακοριςμόσ χαρακτθριςτικϊν κοινοχρθςίασ). Όλεσ αυτζσ οι μεταβλθτζσ κεωροφνται shared Μπορεί να αλλάξει ο ζμμεςοσ κακοριςμόσ με τθ φράςθ default(). default(shared): όςεσ δεν ορίηονται ρθτά, κεωροφνται shared. default(private): όςεσ δεν ορίηονται ρθτά, κεωροφνται private. default(none): όςεσ δεν ορίηονται ρθτά, προκαλοφν ςυντακτικό λάκοσ κατά τθ μετάφραςθ αναγκάηει τον προγραμματιςτι να ορίςει ρθτά τα πάντα. 25
Παράδειγμα Είναι ςωςτόσ ο υπολογιςμόσ του π; Σωςτζσ επιλογζσ: #define N 1000000 double pi = 0.0, W = 1.0/N; int main () { int i, mysum = 0.0; #pragma omp parallel { #pragma omp for for (i=0; i < N; i++) mysum += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); #pragma omp critical pi += mysum; printf("pi = %.10lf\n", pi); return 0; int main () { int i, mysum = 0.0; #pragma omp parallel private(i)\ firstprivate(mysum) { int main () { #pragma omp parallel shared(w,pi) { int i, mysum = 0.0; 26
Οδθγία threadprivate Μετατρζπει τα κοινόχρθςτα global δεδομζνα ςε ιδιωτικά για κάκε νιμα C: File scope και static variables Διαφορετικι ςυμπεριφορά από το private() Με το private() οι global μεταβλθτζσ αποκρφπτονται. Το threadprivate() διατθρεί το global scope ςε κάκε νιμα Οι μεταβλθτζσ threadprivate() μποροφν να αρχικοποιθκοφν χρθςιμοποιϊντασ τθ φράςθ copyin(). 27
Φράςθ δεδομζνων reduction Επθρεάηει ςτθν ουςία τον τρόπο «διαμοίραςθσ» των μεταβλθτϊν: reduction(op : list) Οι μεταβλθτζσ ςτο list πρζπει να είναι shared ςτο παράλλθλο τμιμα που βριςκόμαςτε. Εντόσ μια δομισ parallel ι διαμοίραςθσ εργαςίασ: Δθμιουργείται ιδιωτικό αντίγραφο κάκε μεταβλθτισ τθσ λίςτασ και αρχικοποιείται (ανάλογα με τθν πράξθ op π.χ. 0 για + ) Τα ιδιωτικά αντίγραφα ςυνδυάηονται και δίνουν τθν τελικι τιμι ςτθν αντίςτοιχθ (κοινόχρθςτθ) αρχικι μεταβλθτι ςτο τζλοσ τθσ δομισ. 28
Υπολογιςμόσ του π #define N 1000000 double pi = 0.0, W = 1.0/N; #define N 1000000 double pi = 0.0, W = 1.0/N; int main() { int i; for (i = 0; i < N; i++) pi += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); printf("pi = %.10lf\n", pi); return 0; int main () { #pragma omp parallel { int i, mysum = 0.0; #pragma omp for for (i=0; i < N; i++) mysum += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); #pragma omp critical pi += mysum; printf("pi = %.10lf\n", pi); return 0; 29
Υπολογιςμόσ του π #define N 1000000 double pi = 0.0, W = 1.0/N; #define N 1000000 double pi = 0.0, W = 1.0/N; int main() { int i; for (i = 0; i < N; i++) pi += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); printf("pi = %.10lf\n", pi); return 0; #define N 1000000 double pi = 0.0, W = 1.0/N; int main () { #pragma omp parallel reduction(+: pi) { int i; #pragma omp for for (i=0; i < N; i++) pi += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); printf("pi = %.10lf\n", pi); return 0; int main () { #pragma omp parallel { int i, mysum = 0.0; #pragma omp for for (i=0; i < N; i++) mysum += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); #pragma omp critical pi += mysum; printf("pi = %.10lf\n", pi); return 0; 30
Υπολογιςμόσ του π #define N 1000000 double pi = 0.0, W = 1.0/N; #define N 1000000 double pi = 0.0, W = 1.0/N; int main() { int main () { int i; #pragma omp parallel for (i = 0; i < N; i++) { pi += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); int i, mysum = 0.0; printf("pi = %.10lf\n", pi); #pragma omp for return 0; for (i=0; i < N; i++) mysum += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); #pragma omp critical #define N 1000000 pi += mysum; double pi = 0.0, W = 1.0/N; printf("pi = %.10lf\n", pi); int main () { return 0; int i; #pragma omp parallel for private(i) reduction(+:pi) for (i=0; i < N; i++) pi += 4*W / (1 + (i+0.5)*(i+0.5)*w*w); printf("pi = %.10lf\n", pi); return 0; 31
Εξιςορρόπθςθ φόρτου Αποτελεί βαςικό παράγοντα απόδοςθσ Για ςυνθκιςμζνεσ λειτουργίεσ, π.χ. πρόςκεςθ διανυςμάτων, θ εξιςορρόπθςθ εργαςίασ δεν αποτελείται ηιτθμα Για λιγότερο ομαλζσ λειτουργίεσ πρζπει να δοκεί ιδιαίτερθ ςθμαςία ςτθν διαμοίραςθ τθσ εργαςίασ μεταξφ των νθμάτων Παράδειγμα μθ ομαλϊν (irregular) λειτουργιϊν: Πολλαπλαςιαςμόσ αραιϊν πινάκων Παράλλθλεσ αναηθτιςεισ ςε μία διαςυνδεδεμζνθ λίςτα Για τζτοιεσ περιπτϊςεισ, θ δομι διαμοίραςθ for χρθςιμοποιείται με τθν οδθγία schedule που κακορίηει διάφορουσ αλγορίκμουσ δρομολόγθςθσ των επαναλιψεων 32
Οδθγία schedule Χριςθ schedule ( static dynamic guided [, chunk] ) schedule (runtime) static [,chunk] Διαμοιράηει τισ επαναλιψεισ, που ζχουν χωριςτεί ςε τμιματα μεγζκουσ "chunk«, μεταξφ των νθμάτων με κυκλικό τρόπο Αν δεν ορίηεται το "chunk", αυτό ορίηεται κατά προςζγγιςθ ίςο με N/P και κάκε νιμα εκτελεί ζνα τμιμα επαναλιψεων dynamic [,chunk] Διαχωρίηει τισ επαναλιψεισ ςε τμιματα μεγζκουσ "chunk Κάκε νιμα μόλισ τελειϊςει ζνα τμιμα, παίρνει δυναμικά το επόμενο guided [,chunk] Παρόμοια με dynamic, αλλά το μζγεκοσ του τμιματοσ μειϊνεται εκκετικά ςε κάκε βιμα το μζγεκοσ του τμιματοσ είναι ανάλογο του (# unassigned iterations / # threads), αλλά όχι λιγότερο από chunk. runtime Ο αλγόρικμοσ δρομολόγθςθσ κακορίηεται κατά τον χρόνο εκτζλεςθσ ελζγχοντασ τθ μεταβλθτι περιβάλλοντοσ OMP_SCHEDULE 33
Παράδειγμα 34
Αμοιβαίοσ αποκλειςμόσ και ςυγχρονιςμόσ To OpenMP ορίηει τισ ακόλουκεσ οδθγίεσ: atomic Για ατομικι (αδιαίρετθ) πράξθ ςε μία μεταβλθτι (π.χ. αφξθςθ) critical Για οριςμό κρίςιμων περιοχϊν barrier Για ςυγχρονιςμό νθμάτων flush Για ςυνζπεια μνιμθσ (το μοντζλο του OpenMP υποκζτει εξαιρετικά χαλαρι ςυνζπεια) master Για εκτζλεςθ κϊδικα από τον αρχθγό μίασ ομάδασ νθμάτων (ςαν το single, μόνο που πάντα το εκτελεί το νιμα 0 και δεν υποννοεί barrier ςτο τζλοσ) 35
Συγχρονιςμόσ flush Η εντολι flush δθλϊνει ζνα ςθμείο ςτο οποίο το νιμα επιχειρεί να δθμιουργιςει ςυνεπι εικόνα τθσ μνιμθσ. Όλεσ οι πράξεισ μνιμθσ (αναγνϊςεισ και εγγραφζσ) που ορίηονται πριν από αυτό το ςθμείο πρζπει να ολοκλθρωκοφν. Όλεσ οι πράξεισ μνιμθσ (αναγνϊςεισ και εγγραφζσ) που ορίηονται μετά από αυτό το ςθμείο πρζπει να εκτελεςτοφν μετά το flush Οι μεταβλθτζσ ςε registers ι write buffers πρζπει να εγγραφοφν ςτθ μνιμθ Τα ορίςματα του flush είναι τα ονόματα των μεταβλθτϊν που κα πρζπει να γίνουν flush. Αν δεν υπάρχουν ορίςματα όλεσ οι ορατζσ ςτο νιμα μεταβλθτζσ γίνονται flush. Πρόκειται για memory fence που επιβάλλει ςυνζπεια μνιμθσ 36
Υπονοοφμενοσ ςυγχρονιςμόσ Barrier υπονοείται αμζςωσ μετά το τζλοσ των παρακάτω οδθγιϊν: parallel for (εκτόσ αν είχε δοκεί θ φράςθ nowait) sections (εκτόσ αν είχε δοκεί θ φράςθ nowait) single (εκτόσ αν είχε δοκεί θ φράςθ nowait) Flush υπονοείται αμζςωσ μετά το τζλοσ των παρακάτω οδθγιϊν: parallel for sections single critical barrier ordered 37
Συναρτιςεισ βιβλιοκικθσ Συναρτιςεισ locks omp_init_lock(), omp_set_lock(), omp_unset_lock(), omp_test_lock() Συναρτιςεισ περιβάλλοντοσ χρόνου εκτζλεςθσ: Αλλαγι/Ζλεγχοσ του αρικμοφ των νθμάτων omp_set_num_threads(), omp_get_num_threads(), omp_get_thread_num(), omp_get_max_threads() Ενεργοποίθςθ/απενεργοποίθςθ εμφωλευμζνου παραλλθλιςμοφ και dynamic mode omp_set_nested(), omp_set_dynamic(), omp_get_nested(), omp_get_dynamic() Ζλεγχοσ εκτζλεςθσ ςε παράλλθλο τμιμα omp_in_parallel() Αρικμόσ επεξεργαςτϊν ςτο ςφςτθμα omp_num_procs() 38
Συναρτιςεισ βιβλιοκικθσ Για να ελεγχκεί ο αρικμόσ των νθμάτων που εκτελοφν ζνα πρόγραμμα αρχικά απενεργοποιείται το dynamic mode και κατόπιν κακορίηεται ο αρικμόσ των νθμάτων. #include <omp.h> void main() { omp_set_dynamic(0); omp_set_num_threads(4); /* or num_threads() clause in parallel */ #pragma omp parallel { int id=omp_get_thread_num(); do_lots_of_stuff(id); 39
Χριςθ Κϊδικασ OpenMP #include <omp.h> #include <stdio.h> void main() { #pragma omp parallel { printf( Hello world from thread %d of %d\n, omp_get_thread_num(), omp_get_num_threads(); Παραγωγι εκτελζςιμου αρχείου για OMPi, GNU GCC 4.2, Intel Compiler) $ ompicc o hello hello.c $ gcc fopenmp o hello hello.c $ icc openmp o hello hello.c 40
Χριςθ Εκτζλεςθ $ export OMP_NUM_THREADS=4 $./hello Hello world from thread 0 of 4 Hello world from thread 2 of 4 Hello world from thread 1 of 4 Hello world from thread 3 of 4 $ export OMP_NUM_THREADS=1 $./hello Hello world from thread 0 of 1 41
Παράδειγμα (τι κα τυπϊςει;) #include <stdio.h> #include <omp.h> int main() { int x = 2; #pragma omp parallel num_threads(2) shared(x) { if (omp_get_thread_num() == 0) { x = 5; else { printf("1: Thread# %d: x = %d\n", omp_get_thread_num(),x ); #pragma omp barrier if (omp_get_thread_num() == 0) { printf("2: Thread# %d: x = %d\n", omp_get_thread_num(),x ); else { printf("3: Thread# %d: x = %d\n", omp_get_thread_num(),x ); return 0; 42
Παϋράδειγμα: πίνακασ επί πίνακα (4 CPUs) for (i = 0; i < Ν; i++) { for (j = 0; j < Ν; j++) for (k = sum = 0; k < N; k++) sum += Α[i][k]*B[k][j]; C[i][j] = sum; ΧΡΟΝΟΣ: 130msec #pragma omp parallel for for (i = 0; i < Ν; i++) { for (j = 0; j < Ν; j++) for (k = sum = 0; k < N; k++) sum += Α[i][k]*B[k][j]; C[i][j] = sum; ΧΡΟΝΟΣ: 700msec (λάκοσ ςτθν κοινοχρθςία μεταβλθτών) #pragma omp parallel for private(j,k,sum) for (i = 0; i < Ν; i++) { for (j = 0; j < Ν; j++) for (k = sum = 0; k < N; k++) sum += Α[i][k]*B[k][j]; C[i][j] = sum; ΧΡΟΝΟΣ: 40msec 43
OpenMP tasks
OpenMP 3.0 Στο OpenMP 3.0 ξεκακάριςαν κάποιεσ λεπτομζρειεσ, προςτζκθκαν κάποιεσ νζεσ ςυναρτιςεισ που μποροφν να κλθκοφν από τα προγράμματα (π.χ. εφρεςθ του nesting level). Το βαςικότερο όμωσ ιταν θ προςκικθ των tasks. 45
OpenMP Tasks Γενικι ιδζα: «οριςμόσ» μιασ δουλείασ που πρζπει να γίνει από κάποιο thread, κάποια ςτιγμι (και όχι οπωςδιποτε άμεςα από το νιμα που τθν όριςε) Βολεφει πολφ ςτθν παραλλθλοποίθςθ πολλϊν εφαρμογϊν Επίςθσ με αυτό τον τρόπο μπορεί ςε αρκετζσ περιπτϊςεισ να αποφευχκεί θ χριςθ εμφωλευμζνων παράλλθλων περιοχϊν που δεν τισ χειρίηονται καλά οι περιςςότεροι compilers 46
Απλι εφαρμογι: παραλλθλοποίθςθ λειτουργιϊν ςε ςυνδεδεμζνθ λίςτα Πϊσ παραλλθλοποιείται ςτο OpenMP 2.5? Πρϊτα ζνα πζραςμα για να βρεκεί το πλικοσ των επαναλιψεων Στθν ςυνζχεια #pragma omp parallel for Θα πρζπει να φυλάμε επίςθσ ζναν πίνακα με όλουσ τουσ pointers 47
Με tasks 48
OpenMP tasks Ζνα task ζχει: Κϊδικα που πρζπει να εκτελεςτεί: #pragma omp task { κϊδικασ Μεταβλθτζσ πάνω ςτισ οποίεσ κα δουλζψει Αφοφ κα εκτελεςτεί πικανϊσ αργότερα, κα πρζπει να «κουβαλάει» μαηί του και τισ τιμζσ των μεταβλθτϊν που υπιρχαν κατά τον οριςμό του Ζνα κακοριςμζνο νιμα Που κα το εκτελζςει τον κϊδικα Όχι όμωσ πάντα προ-κακοριςμζνο. 49
OpenMP tasks Δφο φάςεισ: (α) οριςμόσ/πακετάριςμα και (β) εκτζλεςθ To νιμα που ςυναντά το #pragma omp task πρζπει να δθμιουργιςει μία δομι που περιζχει τον κϊδικα αλλά και τα δεδομζνα με τισ τρζχουςεσ τιμζσ τουσ Κάποια ςτιγμι, κάποιο νιμα κα πάρει το «πακζτο» και κα το εκτελζςει (ο κόποσ για το πακετάριςμα δεν χρειάηεται αν το νιμα που ςυναντά το #pragma omp task εκτελζςει απευκείασ το task) 50
Γενικι ςφνταξθ 51
Φράςεισ Φράςθ if(ςυνθήκη) Αν θ ςυνκικθ είναι false τότε το νιμα εκτελεί άμεςα το task Π.χ. αν το κόςτοσ τθσ δθμιουργίασ ενόσ task είναι μεγάλο ςε ςχζςθ με τουσ υπολογιςμοφσ που περιλαμβάνει Ή π.χ. για να cache/memory affinity Φράςθ untied Κανονικά, ζνα task είναι «δεμζνο» ( tied ) με το νιμα που κα ξεκινιςει να το εκτελεί δθλαδι κα εκτελεςτεί από το νιμα αυτό μζχρι τζλουσ Σε ζνα ελεφκερο task ( untied ), θ εκτζλεςι του μπορεί να διακόπτεται και να ςυνεχίηει τθν εκτζλεςι του άλλο νιμα, κλπ. Λεπτομζρειεσ: Το πρόγραμμα είναι όλο ζνα task. Όταν ζνα νιμα ςυναντά μία παράλλθλθ περιοχι, φτιάχνει Ν tasks (implicit): Είναι tied, ζνα task ςε κάκε ζνα από τα N νιματα που κα δθμιουργθκοφν Ζνα task που εκτελείται μπορεί να δθμιουργιςει άλλα tasks 52
Συγχρονιςμόσ των tasks Στο τζλοσ ενόσ barrier, όλα τα tasks που ζχει δθμιουργιςει μία ομάδα νθμάτων κα ζχουν ολοκλθρωκεί. Το task που ςυναντάει task barrier (#pragma omp taskwait) μπλοκάρει μζχρι να ολοκλθρωκοφν όλα τα tasks που δθμιοφργθςε 53
Γενικι ςφνταξθ περιβάλλον δεδομζνων Αν δεν υπάρχει το default(), τότε οι global μεταβλθτζσ είναι κλαςικά shared. Οι υπόλοιπεσ κεωροφνται shared μόνο αν είναι shared ςε όλεσ τισ περιβάλλουςεσ περιοχζσ του κϊδικα μζχρι τθν πιο πρόςφατθ παράλλθλθ περιοχι. Αλλιϊσ είναι firstprivate. 54
fibonacci 55
fibonacci 56
fibonacci 57
fibonacci 58
Διάςχιςθ λίςτασ 59
Διάςχιςθ λίςτασ 60
Διάςχιςθ λίςτασ 61
Μερικά ακόμα για το OpenMP
Ελεφκεροι OpenMP compilers GCC Υποςτιριξθ tasks από τθν ζκδοςθ 4.3.x (όμωσ άςχθμθ υλοποίθςθ) Καλι υλοποίθςθ από ζκδοςθ 4.4.x ICC Intel compiler & tools Εξαιρετικά εργαλεία, ταχφτατα ςειριακά προγράμματα για x86 Ζχει εξαγοράςει τθν Cilk Arts SUNCC SUN C compiler (πλζον ORACLE) Γενικά καλζσ και ςτακερζσ επιδόςεισ, όχι όμωσ ο καλφτερεσ OMPi O «δικόσ» μασ, μζςα από πτυχιακζσ, μεταπτυχιακζσ και διδακτορικζσ εργαςίεσ Ανοιχτοφ κϊδικα, πολφ καλζσ επιδόςεισ, επεκτάςεισ κ.α. http://paragroup.cse.uoi.gr/ Άλλοι ερευνθτικοί, ελεφκερου κϊδικα: OpenUH Mercurium (Nanaos) 63
Εκδόςεισ του OpenMP 1997 1998 1999 2000 2002 2005 2008 2011 2013 2015 V1.0 for Fortran V1.0 for C/C++ V1.1 for Fortran V2.0 for Fortran V2.0 for C/C++ V2.5 unified Fortran C/C++ V3.0 Tasks V3.1 Tasking enhancements V4.0 Devices Cancellations Dependencies SIMD Reductions V4.5 64