Κατανεμημένος και Παράλληλος Προγραμματισμός Ηλίας Κ. Σάββας Αναπληρωτής Καθηγητής Τμήμα Μηχανικών Πληροφορικής ΤΕ, ΤΕΙ Θεσσαλίας Email: savvas@teilar.gr
Παράλληλος προγραμματισμός - OpenMP Παράλληλες αρχιτεκτονικές βασισμένες στην μνήμη Εισαγωγή στην Open Multi Processing Παραδείγματα Οδηγίες OpenMP OMP και MPI ΟMP vs MPI ΕνόητηταI #6 - OpenMP 2
Παράλληλες αρχιτεκτονικές βασισμένες στην μνήμη Κοινής μνήμης shared memory: Συμμετρικό σύστημα πολλών επεξεργαστών (Symmetric MultiProcessor system SMP) Κατανεμημένης μνήμης distributed memory: Κατανεμημένο σύστημα πολλών επεξεργαστών (Distributed MoultiProcessor system DMP) Υβριδικής μνήμης hybrid memory. ΕνόητηταI #6 - OpenMP 3
Αρχιτεκτονική κοινής μνήμης Οι παράλληλοι υπολογιστές κοινής μνήμης αν και διαφέρουν μεταξύ τους γενικά έχουν την δυνατότητα όλοι οι επεξεργαστές να έχουν προσπέλαση στην ίδια μνήμη. Αν και ο κάθε επεξεργαστής μπορεί να λειτουργεί αυτόνομα διαμοιράζεται όμως την ίδια μνήμη με όλους τους υπόλοιπους. Η όποια αλλαγή στην μνήμη επέλθει από κάποιον επεξεργαστή είναι ορατή και από όλους τους άλλους. (SMP) ΕνόητηταI #6 - OpenMP 4
Πλεονεκτήματα και μειονεκτήματα αρχιτεκτονικής κοινής μνήμης +Η καθολικά κοινή μνήμη παρέχει ένα αρκετά φιλικό προς τον χρήστη προγραμματιστικό περιβάλλον. +Η κοινή μνήμη μεταξύ των παράλληλων τμημάτων ενός προγράμματος είναι ταχύτατη λόγω της εγγύτητας της μνήμης προς τους επεξεργαστές. -Πρόβλημα κλιμάκωσης μεταξύ μνήμης και επεξεργαστών. Αύξηση του πλήθους των επεξεργαστών αυξάνει την κίνηση μεταξύ CPUs και μνήμης. -Ο συγχρονισμός γίνεται με ευθύνη του προγραμματιστή ώστε να είναι σίγουρη η ορθή χρήση της κοινής μνήμης. -Είναι εξαιρετικά δύσκολη και ακριβή η σχεδίαση και παραγωγή τέτοιου τύπου συστημάτων με τον ολοένα αυξανόμενο αριθμό επεξεργαστών. ΕνόητηταI #6 - OpenMP 5
Αρχιτεκτονική κατανεμημένης μνήμης (DMP) Κάθε επεξεργαστής διαθέτει την δική του αποκλειστική μνήμη. Κάθε επεξεργαστής λειτουργεί αυτόνομα. Όταν κάποιος επεξεργαστής χρειαστεί να προσπελάσει μνήμη άλλου επεξεργαστή είναι ευθύνη του προγραμματιστή για το πώς και πότε (συγχρονισμός, επικοινωνία). Απαιτείται κάποιο δίκτυο για να είναι εφικτή η επικοινωνία (μπορεί να είναι για παράδειγμα το ethernet) ΕνόητηταI #6 - OpenMP 6
Πλεονεκτήματα και μειονεκτήματα αρχιτεκτονικής κατανεμημένης μνήμης +Η μνήμη κλιμακώνεται ανάλογα με τον αριθμό των επεξεργαστών. Αύξηση του αριθμού των επεξεργαστών συνεπάγεται αύξηση και της συνολικής μνήμης. +Ο κάθε επεξεργαστής έχει ταχύτατη προσπέλαση στην δική του μνήμη. +Αποτελεί φθηνή λύση καθώς ο κάθε επεξεργαστής μπορεί να είναι ένας απλός και φθηνός υπολογιστής του εμπορίου. -Ο προγραμματιστής είναι ο υπεύθυνος για πολλές λεπτομέρειες που συσχετίζονται με την μεταφορά δεδομένων μεταξύ των υπολογιστικών κόμβων. -Μη ομοιόμορφος χρόνος προσπέλαση μνήμης καθώς εξαρτάται από το δίκτυο και την φυσική θέση της μνήμης που πρέπει να προσπελασθεί (Non-Uniform Memory Access time NUMA). ΕνόητηταI #6 - OpenMP 7
Αρχιτεκτονική υβριδικής (κοινής και κατανεμημένης) μνήμης Το τμήμα τη κοινής μνήμης αποτελεί μία SMP μηχανή. Όλοι οι επεξεργαστές μίας SMP μηχανής έχουν προσπέλαση στην ίδια μνήμη. Πολλαπλές SMP μηχανές οι οποίες επικοινωνούν μεταξύ τους μέσω κάποιου δικτύου αποτελούν το τμήμα του συστήματος με την κατανεμημένη μνήμη. Οι ταχύτεροι και μεγαλύτεροι υπολογιστές σήμερα χρησιμοποιούν αυτό το μοντέλο. +/-: ότι είναι κοινό και στις δύο αρχιτεκτονικές. ΕνόητηταI #6 - OpenMP 8
OpenMP Open Multi-Processing: Αποτελεί ένα application programming interface (API) το οποίο υποστηρίζει multi-platform shared memory multiprocessing Γλώσσες που υποστηρίζει: C, C++, και Fortran Λειτουργικά Συστήματα: Solaris, AIX, HP-UX, Linux, OS X, και Windows. Διαθέτει ένα σύνολο οδηγιών προς τους compilers ΕνόητηταI #6 - OpenMP 9
Πρώτο πρόγραμμα hello1.c #include <omp.h> #include <stdio.h> int main(int *argc, char **argv) { # pragma omp parallel gcc hello1.c o hello1 -fopenmp } printf("arithmos Thread: %d\n", omp_get_thread_num()); return 0; ubuntu@ubuntu:~/omp$./hello1 Arithmos Thread: 3 Arithmos Thread: 0 Arithmos Thread: 1 Arithmos Thread: 2 ubuntu@ubuntu:~/omp ΕνόητηταI #6 - OpenMP 10
Σχόλια hello1.c #include <omp.h> #include <stdio.h> int main(int *argc, char **argv) { } # pragma omp parallel printf("arithmos Thread: %d\n", omp_get_thread_n um()); return 0; omp.h η βιβλιοθήκη για την OMP. Δεν χρειάζεται να κάνουμε κάτι. Εμπεριέχεται στην gcc #pragma οδηγίες της OMP για παραλληλία τμήματος προγράμματος (γενικά οδηγίες στον compiler της C για διαδικασίες που εξαρτώνται από το λειτουργικό, υλικό, κλπ). Εάν είναι απαραίτητο: { κώδικας } omp_get_thread_num() αριθμός διεργασίας (myid). Πόσες διεργασίες: εάν δεν το προσδιορίσουμε τόσες όσοι και οι διαθέσιμοι πυρήνες ΕνόητηταI #6 - OpenMP 11
Βελτίωση: hello2.c #include <omp.h> ----- int P; // synolo diergasiwn # pragma omp parallel { } if (omp_get_thread_num() == 0 ) { P = omp_get_num_threads(); printf("\nsynolo DIERASIWN: %d\n\n", P); } #pragma omp parallel { printf("geia sas apo tin %d diergasia epi synolou %d\n", omp_get_thread_num(), P); if (omp_get_thread_num() == 2 ) printf("\talla egw, i %d diergasia, sas xaireto gia alli mia fora...!\n", omp_get_thread_num()); } return 0; } ΕνόητηταI #6 - OpenMP 12
hello2.c: εκτέλεση ubuntu@ubuntu:~/omp$ gcc hello2.c -o hello2 -fopenmp ubuntu@ubuntu:~/omp$./hello2 SYNOLO DIERASIWN: 4 Geia sas apo tin 1 diergasia epi synolou 4 Geia sas apo tin 3 diergasia epi synolou 4 Geia sas apo tin 2 diergasia epi synolou 4 Alla egw, i 2 diergasia, sas xaireto gia alli mia fora...! Geia sas apo tin 0 diergasia epi synolou 4 ΕνόητηταI #6 - OpenMP 13
Σχόλια hello2.c omp_get_num_threads(): σύνολο διεργασιών (αν δεν διαφοροποιηθεί, όσοι και οι πυρήνες) if (omp_get_thread_num() == 2 ): σε περίπτωση που μόνο κάποια διεργασία πρέπει να κάνει κάτι (συνήθως η master διεργασία = 0) # pragma { κώδικας 1} # pragma {κώδικας 2): για να εκτελεσθεί ο κώδικας 2 θα πρέπει όλες οι διεργασίες του κώδικα 1 να έχουν τερματίσει (barrier) ΕνόητηταI #6 - OpenMP 14
Επιπλέον βελτίωση: hello3.c int P; // synolo diergasiwn int myid; // i dirgasiaasia mou # pragma omp parallel private(myid) { myid = omp_get_thread_num(); printf("hello World apo tin diergasia %d\n", myid); if (myid == 0) { // Mono i vasiki diergasia (master) ektelei auto P = omp_get_num_threads(); printf("synolo DIERGASIWN= %d\n", P); } } ΕνόητηταI #6 - OpenMP 15
hello3.c: εκτέλεση ubuntu@ubuntu:~/omp$ gcc hello3.c -o hello3 fopenmp ubuntu@ubuntu:~/omp$./hello3 Hello World apo tin diergasia 0 SYNOLO DIERGASIWN = 4 Hello World apo tin diergasia 3 Hello World apo tin diergasia 2 Hello World apo tin diergasia 1 ubuntu@ubuntu:~/omp$ ΕνόητηταI #6 - OpenMP 16
Σχόλια hello3.c # pragma omp parallel private(myid): εμβέλεια μεταβλητής ιδιωτική (private) για κάθε διεργασία # pragma omp parallel shared(x): κοινή για όλες τις διεργασίες, αλλά και συνδυασμός: # pragma omp parallel private(i) shared(n, A, B, C): για C[N] = A[N] + B[N], i ο δείκτης του for ΕνόητηταI #6 - OpenMP 17
Γρήγορος οδηγός αναφοράς Οδηγία omp.h omp_get_num_thr eads() omp_get_thread_ num() #pragma omp parallel private(λίστα μεταβλητών) shared(λίστα μεταβλητών) Αποτέλεσμα Βιβλιοθήκη της OpenMP Εύρεση πλήθους διεργασιών (όσοι και οι πυρήνες αν δεν διαφοροποιηθεί) Εύρεση ποια διεργασία είμαι εγώ Αρχή παράλληλου κώδικα Εμβέλεια μεταβλητών: τοπικές σε κάθε διεργασία Εμβέλεια μεταβλητών: καθολικές (σε όλες τις διεργασίες) ΕνόητηταI #6 - OpenMP 18
MPI με OMP Μπορούμε να συνδυάσουμε MPI με OMP? (αυτό θα ήταν η πεμπτουσία του συνδυασμού κατανεμημένου και παράλληλου προγραμματισμού) ΝΑΙ! Επειδή η MPI λειτουργεί με κόμβους οι οποίοι θα μπορούσε να είναι αυτόνομες υπολογιστικές μονάδες (και ετερογενείς) και σε κάθε κόμβο να εκμεταλλευόμαστε τους (φυσικούς) πυρήνες του Προφανώς αποτελεί ένα πανίσχυρο εργαλείο! ΕνόητηταI #6 - OpenMP 19
Παράδειγμα: omp_mpi.c (1 από 3) #include <stdio.h> #include "mpi.h" #include <omp.h> int main(int *argc, char **argv) { int P, myid; //MPI: P=σύνολο κόμβων, myid ποιος είμαι int ego, diergasies; //OMP: ego=ποια διεργασία (πυρήνας // είμαι), diergasies=σύνολο πυρήνων ΕνόητηταI #6 - OpenMP 20
Παράδειγμα: omp_mpi.c (2 από 3) MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &P); MPI_Comm_rank(MPI_COMM_WORLD, &myid); ΕνόητηταI #6 - OpenMP 21
Παράδειγμα: omp_mpi.c (3 από 3) #pragma omp parallel default(shared) private(ego, diergasies) { diergasies = omp_get_num_threads(); ego = omp_get_thread_num(); printf("xaietismata apo tin diergasia %d se synolo %d kai apo ton komvo %d se synolo %d\n", ego, diergasies, myid, P); } MPI_Finalize(); } ΕνόητηταI #6 - OpenMP 22
Μεταγλώττιση και εκτέλεση (1 από 3) ilias@ilias-optiplex-9010:~/omp_programs$ mpicc mpi_omp.c -o mpi_omp fopenmp ilias@ilias-optiplex-9010:~/omp_programs$ mpirun -np 4./mpi_omp Δηλαδή: το τρέχουμε σε 4 κόμβους όπου ο κάθε κόμβος θα αξιοποιήσει τους πυρήνες του Αποτέλεσμα (ο κάθε κόμβος διαθέτει 8 πυρήνες): ΕνόητηταI #6 - OpenMP 23
Μεταγλώττιση και εκτέλεση (2 από 3) ilias@ilias-optiplex-9010:~/omp_programs$ mpicc mpi_omp.c -o mpi_omp -fopenmp ilias@ilias-optiplex-9010:~/omp_programs$ mpirun -np 4./mpi_omp Xairetismata apo tin diergasia 4 se synolo 8 kai apo ton komvo 2 se synolo 4 Xairetismata apo tin diergasia 1 se synolo 8 kai apo ton komvo 2 se synolo 4 Xairetismata apo tin diergasia 5 se synolo 8 kai apo ton komvo 2 se synolo 4 Xairetismata apo tin diergasia 2 se synolo 8 kai apo ton komvo 0 se synolo 4 Xairetismata apo tin diergasia 6 se synolo 8 kai apo ton komvo 0 se synolo 4 Xairetismata apo tin diergasia 4 se synolo 8 kai apo ton komvo 0 se synolo 4 Xairetismata apo tin diergasia 0 se synolo 8 kai apo ton komvo 0 se synolo 4 Xairetismata apo tin diergasia 6 se synolo 8 kai apo ton komvo 2 se synolo 4 Xairetismata apo tin diergasia 3 se synolo 8 kai apo ton komvo 2 se synolo 4 Xairetismata apo tin diergasia 3 se synolo 8 kai apo ton komvo 0 se synolo 4 ΕνόητηταI #6 - OpenMP 24
Μεταγλώττιση και εκτέλεση (3 από 3) Xairetismata apo tin diergasia 1 se synolo 8 kai apo ton komvo 0 se synolo 4 Xairetismata apo tin diergasia 2 se synolo 8 kai apo ton komvo 2 se synolo 4 Xairetismata apo tin diergasia 7 se synolo 8 kai apo ton komvo 2 se synolo 4 Xairetismata apo tin diergasia 5 se synolo 8 kai apo ton komvo 3 se synolo 4 Xairetismata apo tin diergasia 7 se synolo 8 kai apo ton komvo 3 se synolo 4 Xairetismata apo tin diergasia 1 se synolo 8 kai apo ton komvo 3 se synolo 4 Xairetismata apo tin diergasia 3 se synolo 8 kai apo ton komvo 3 se synolo 4 Xairetismata apo tin diergasia 7 se synolo 8 kai apo ton komvo 0 se synolo 4 Xairetismata apo tin diergasia 5 se synolo 8 kai apo ton komvo 0 se synolo 4 Xairetismata apo tin diergasia 0 se synolo 8 kai apo ton komvo 2 se synolo 4 Xairetismata apo tin diergasia 6 se synolo 8 kai apo ton komvo 1 se synolo 4 ------ ΕνόητηταI #6 - OpenMP 25
OMP vs MPI Να γραφεί πρόγραμμα με α) χρήση της MPI και β) χρήση της ΟΜP το οποίο θα εκτελεί το εσωτερικό γινόμενο δύο διανυσμάτων. Έστω Α[Ν] και Β[Ν] δύο διανύσματα μεγέθους Ν. To εσωτερικό γινόμενο c=axb δίνεται από τον τύπο: c N 1 i 0 a i b i ΕνόητηταI #6 - OpenMP 26
OMP: egd_omp.c (1 από 3) // Esoteriko ginomeno dianysmatwn #include <stdio.h> #include <stdlib.h> #include <omp.h> int main(int *argc, char **argv) { int myid, P; // my id kai arithmos diergasiwn int *A, *B; // ta dianysmata int N; // megethos dianysmatwn int i; // deiktis int sum=0; // edo tha sysoreythei to esoteriko ginomeno printf("\nn="); scanf("%d", &N); ΕνόητηταI #6 - OpenMP 27
OMP: egd_omp.c (2 από 3) // Dimiourgia dianysmatwn A = malloc(n * sizeof( int )); B = malloc(n * sizeof( int )); for (i=0; i<n; i++) A[i] = rand() % 10; for (i=0; i<n; i++) B[i] = rand() % 10; ΕνόητηταI #6 - OpenMP 28
OMP: egd_omp.c (3 από 3) // Esoteriko ginomeno sum = A x B # pragma omp parallel for private (i) shared(n, A, B) reduction(+:sum) for (i=0; i<n; i++) sum += A[i] * B[i]; printf("\n\nesoteriko ginomeno = %d\n\n", sum); } return 0; ΕνόητηταI #6 - OpenMP 29
MPI: egd_mpi.c Διαφοροποιήσεις: Πρέπει το Ν να εισαχθεί στον 0 κόμβο και να γίνει στην συνέχεια broadcast Πρέπει να διαμοιραστεί το for: for (i=myid; i<n; i+=p) // P αριθμός κόμβων Πρέπει ο κάθε κόμβος να χρησιμοποιεί μία τοπική μεταβλητή για το άθροισμα που του αντιστοιχεί: int sum_local=0; Πρέπει να γίνει reduce στον κόμβο 0 και στην μεταβλητή sum H εκτύπωση του sum να γίνει μόνο από τον κόμβο 0 ΕνόητηταI #6 - OpenMP 30
Ποιο είναι πιο εύκολο? OpenMP: γιατί «κρύβει» και διαχειρίζεται μόνη της πολλές διαδικασίες από τον προγραμματιστή, για παράδειγμα: Παραλληλοποίηση for (και στην MPI δεν είδαμε και την περίπτωση να είναι N % P!= 0) O master κόμβος πρέπει να υποδηλώνεται στην MPI και να διαχειρίζεται διαφορετικά. Και αυτό μέλημα του προγραμματιστή! Οι συμμετέχουσες διεργασίες ενεργοποιούνται αυτόματα στο βέλτιστο από την OMP (όσοι και οι πυρήνες) ΕνόητηταI #6 - OpenMP 31
Ποια είναι πιο χρήσιμη? Η MPI γιατί: Μπορεί να κλιμακώνει ανάλογα με τους συμμετέχοντες κόμβους Χρησιμοποιεί περισσότερους και εάν υπάρχει ανάγκη απομακρυσμένους και γεωγραφικά κατανεμημένους υπολογιστικούς πόρους ΒΕΛΤΙΣΤΟ: ο συνδυασμός τους!!! ΕνόητηταI #6 - OpenMP 32
Τέλος της # 06 ενότητας Ερωτήσεις; Χρησιμοποιείστε το email για όποιες επιπλέον απορίες / ερωτήσεις: savvas@teilar.gr Σημειώσεις μαθήματος (και όχι μόνο): https://e-class.teilar.gr/courses/cs386/ ΕνόητηταI #6 - OpenMP 33