ΕΚΔΟΣΗ 1.1 ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ ΕΠΙΜΕΛΕΙΑ: Β. ΤΣΑΚΑΝΙΚΑΣ, Β. ΤΑΜΠΑΚΑΣ
ΠΕΡΙΕΧΟΜΕΝΑ ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ... 2 Ο αλγόριθμος Polling... 2 Ο Κυματικός Αλγόριθμος... 3 Εργαστηριακή Άσκηση... 5 Βήμα 1: Ο αλγόριθμος Polling 1 η έκδοση... 5 Βήμα 2: Ο αλγόριθμος Polling 2 η Έκδοση... 6 Βήμα 3: Ο αλγόριθμος Wave... 8 Βήμα 4: Ο αλγόριθμος Polling 3 η Έκδοση... 9 Δραστηριότητες... 11 Βιβλιογραφία... 12 ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 1
ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ Ο ΑΛΓΟΡΙΘΜΟΣ POLLING Ο αλγόριθμος Polling είναι ένας ιδιαίτερα χρήσιμος αλγόριθμος, εξαιτίας της απλότητας του και της επεκτασιμότητάς του. Η τοπολογία στην οποία εφαρμόζεται μπορεί να είναι πλήρης γράφος ή αστέρας. Στον αλγόριθμο υπάρχει ένας μόνο αρχικοποιητής. Στην περίπτωση του αστέρα ο αρχικοποιητής είναι στο κέντρο. Η πληροφορία μεταφέρεται από τον αρχικοποιητή στους υπόλοιπους κόμβους. Ο αρχικοποιητής αρχικά στέλνει το token σε όλους τους γείτονες και στη συνέχεια περιμένει να λάβει το token από όλους τους γείτονες. Όταν γίνει αυτό τότε εκτελεί ένα τοπικό κώδικα (decide) που εξαρτάται από τον γενικότερο αλγόριθμο. Οι μη-αρχικοποιητές περιμένουν να λάβουν το token και στη συνέχεια στέλνουν το token στον αρχικοποιητή. Στον παρακάτω πίνακα εμφανίζεται ο αλγόριθμος Polling, τόσο για τον αρχικοποιητή όσο και για τους υπόλοιπους κόμβους. ΕΙΚΌΝΑ 1: Ο ΑΛΓΟΡΙΘΜΟΣ POLLING ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 2
initiator var recp: integer; recp:= 0; begin for all q Neighp do send<token> to q while recp< # Neighp do begin receive<token>; recp:=recp+1; end decide; end. non-initiator begin receive<token>from q; send<token> to q; end. Ο ΚΥΜΑΤΙΚΟΣ ΑΛΓΟΡΙΘΜΟΣ Οι κυματικοί αλγόριθμοι ΚΑ -(wave algorithms) γενικά χρησιμοποιούνται για την μετάδοση της πληροφορίας σε όλο το δίκτυο. Είναι δομικά/βασικά πρωτόκολλα που χρησιμοποιούνται συνήθως σε πιο σύνθετα προβλήματα ως εργαλείο (υπορουτίνες) μεταφοράς της πληροφορίας ή συγχρονισμού. Οι κυματικοί αλγόριθμοι είναι γενικά χρήσιμο στις περιπτώσεις όπου: Αναμετάδοση μιας πληροφορίας (π.χ. ένα start ή ένα terminate μήνυμα ή ένα μήνυμα με συγκεκριμένο περιεχόμενο) που πρέπει να φτάσει σε όλους τους κόμβους. Υλοποίηση σφαιρικού συγχρονισμού σε όλο το δίκτυο (π.χ. με τη λήψη ενός μηνύματος <x> κάθε διεργασία- κόμβος εκτελεί ένα συγκεκριμένο βήμα). Υπολογισμός μιας συνάρτησης που κάθε κόμβος κατέχει ένα μέρος της εισόδου του (π.χ. υπολογισμός του μεγίστου στο δίκτυο). Ένας κυματικός αλγόριθμος έχει τις παρακάτω ιδιότητες: Οδηγείται πάντα σε τερματισμό. Οδηγείται πάντα σε μια απόφαση σε ένα τουλάχιστον κόμβο. Η απόφαση έπεται πάντοτε μετά από ένα γεγονός (που έχει σχέση με το κυματικό πρωτόκολλο) σε κάθε κόμβο. Ο κυματικός αλγόριθμος σε φυσική γλώσσα μπορεί να παρουσιαστεί ως εξής: ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 3
1. Ο αρχικοποιητής στέλνει το token στον επόμενο γείτονά του. Ο κάθε κόμβος έχει αίσθηση του προσανατολισμού και γνωρίζει ποιος είναι ο προηγούμενος και ποιος είναι ο επόμενος. Στη συνέχεια ο αρχικοποιητής μπλοκάρεται περιμένοντας να παραλάβει το token. Όταν παραλάβει το token (θα το λάβει από τον προηγούμενο γείτονα) τότε εκτελεί ένα τοπικό κώδικα (decide) που εξαρτάται από τον γενικότερο αλγόριθμο. 2. Οι μη αρχικοποιητές μόλις λάβουν το token (το λαμβάνουν από τον προηγούμενο κόμβο) το αναμεταδίδουν στον επόμενο κόμβο. ΕΙΚΌΝΑ 2: ΚΥΜΑΤΙΚΟΣ ΑΛΓΟΡΙΘΜΟΣ initiator begin send<token> to nextp; receive<token>; decide; end; non-initiator begin receive<token>; send<token>to nextp; end; ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 4
ΕΡΓΑΣΤΗΡΙΑΚΗ ΆΣΚΗΣΗ Σε αυτό το τμήμα της εργαστηριακής άσκησης θα δοκιμάσουμε να υλοποιήσουμε κάποιες εκδόσεις του αλγορίθμου Polling και να δοκιμάσουμε την επίδοσή τους. Κατά την διάρκεια εκτέλεσης του εργαστηριακού οδηγού θα πρέπει να συμπληρώνετε παράλληλα και την εργαστηριακή σας αναφορά, πρότυπο της οποίας μπορείτε να βρείτε στο openclass του μαθήματος. Η εργαστηριακή αναφορά θα πρέπει να έχει παραδοθεί την ημέρα πριν το επόμενο εργαστήριο, σύμφωνα με τις οδηγίες. Βήμα 1: Ο αλγόριθμος Polling 1 η έκδοση Στο πρώτο βήμα της εργαστηριακής άσκησης θα προσπαθήσουμε υλοποιήσουμε μία απλή έκδοση του αλγορίθμου Polling. Σε αυτή την έκδοση, κάθε διεργασία όταν ενεργοποιείται από τον αρχικοποιητή πρέπει να εκτυπώνει το κατάλληλο μήνυμα. Ο αρχικοποιητής επίσης, θα πρέπει να εκτυπώνει το κατάλληλο μήνυμα στην αρχή και στο τέλος του πρωτοκόλλου. Στον κώδικα δεν θα πρέπει να χρησιμοποιήσετε broadcasting δυνατότητες επικοινωνίας αλλά μόνο point-to-point. Για αυτό το λόγο: 1. Συνδεθείτε σε ένα λειτουργικό Linux (Ubuntu ή LUbuntu). 2. Ανοίξτε ένα terminal 3. Δημιουργήστε έναν φάκελο MPI_ABCD, όπου ABCD το μητρώο σας. Για να το πετύχετε αυτό, εκτελέστε την εντολή mkdir MPI_ABCD. Στην συνέχεια μπείτε στον φάκελο εκτελώντας την εντολή cd MPI_ABCD. 4. Δημιουργήστε το αρχείο time.c εκτελώντας την εντολή gedit polling1.c 5. Αντιγράψτε το παρακάτω πρόγραμμα στο αρχείο σας #include <mpi.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { MPI_Init(NULL, NULL); //Arxikopoioume to MPI int world_rank; MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); int world_size; MPI_Comm_size(MPI_COMM_WORLD, &world_size); int ack = 0; int next; // o epomenos komvos if (world_rank == 0) { printf("-------polling PROTOCOL STARTING-------\n"); next = world_rank; for (next = 0; next < world_size; next++) { if (next!= starter) { MPI_Send(&ack, 1, MPI_INT, next, 0, MPI_COMM_WORLD); printf("starter process %d awakes process %d\n", world_rank, next); ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 5
MPI_Recv(&ack, 1, MPI_INT, next, 0, MPI_COMM_WORLD, printf("ok Starter process %d. Process %d is awake!\n", world_rank, next); printf("-------polling PROTOCOL ENDING-------\n"); if (world_rank!= 0) { MPI_Recv(&ack, 1, MPI_INT, starter, 0, MPI_COMM_WORLD, MPI_Send(&ack, 1, MPI_INT, starter, 0, MPI_COMM_WORLD); MPI_Finalize(); 6. Αφού αποθηκεύσετε και κλείσετε το αρχείο, μεταγλωττίστε το εκτελώντας την εντολή mpicc polling1.c o polling1 7. Εάν δεν έγινε κάποιο λάθος, ο μεταγλωττιστής δεν θα πρέπει να βγάλει κανένα μήνυμα. Τότε, μπορείτε να εκτελέσετε το πρόγραμμα, τρέχοντας την εντολή mpirun np 10./polling1. Σε αυτή την εντολή, η παράμετρος np καθορίζει το πλήθος των διεργασιών που θα δημιουργηθούν στον «κόσμο» μας. 8. Αντιγράψτε την έξοδο του προγράμματος στην εργαστηριακή αναφορά σας. Τι παρατηρείτε; Βήμα 2: Ο αλγόριθμος Polling 2 η Έκδοση Στον αλγόριθμο που παρουσιάστηκε στο προηγούμενο βήμα, ο αρχικοποιητής ήταν η διεργασία 0. Σε αυτή την έκδοση του αλγορίθμου, θα μπορούμε να δηλώσουμε ως αρχικοποιητή όποιον κόμβο θέλουμε. Συνεπώς: 1. Ανοίξτε ένα terminal 2. Μπείτε στον φάκελο εκτελώντας την εντολή cd MPI_ABCD. 3. Δημιουργήστε το αρχείο polling2.c εκτελώντας την εντολή gedit polling2.c 4. Αντιγράψτε το παρακάτω πρόγραμμα στο αρχείο σας #include <mpi.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { int starter = 11;//Edo dilwnoyme poios komvos theloume na einai arxikopoihtis //Fysika tha prepei na einai mikrotero apo to world_size! MPI_Init(NULL, NULL); //Arxikopoioume to MPI ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 6
int world_rank; MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); int world_size; MPI_Comm_size(MPI_COMM_WORLD, &world_size); int ack = 0; int next; // o epomenos komvos if (world_rank == starter) { printf("-------polling PROTOCOL STARTING-------\n"); next = world_rank; for (next = 0; next < world_size; next++) { if (next!= starter) { MPI_Send(&ack, 1, MPI_INT, next, 0, MPI_COMM_WORLD); printf("starter process %d awakes process %d\n", world_rank, next); MPI_Recv(&ack, 1, MPI_INT, next, 0, MPI_COMM_WORLD, printf("ok Starter process %d. Process %d is awake!\n", world_rank, next); printf("-------polling PROTOCOL ENDING-------\n"); if (world_rank!= starter) { MPI_Recv(&ack, 1, MPI_INT, starter, 0, MPI_COMM_WORLD, MPI_Send(&ack, 1, MPI_INT, starter, 0, MPI_COMM_WORLD); MPI_Finalize(); 5. Αφού αποθηκεύσετε και κλείσετε το αρχείο, μεταγλωττίστε το εκτελώντας την εντολή mpicc polling2.c o polling2 6. Εάν δεν έγινε κάποιο λάθος, ο μεταγλωττιστής δεν θα πρέπει να βγάλει κανένα μήνυμα. Τότε, μπορείτε να εκτελέσετε το πρόγραμμα, τρέχοντας την εντολή mpirun np 10./polling2. Σε αυτή την εντολή, η παράμετρος np καθορίζει το πλήθος των διεργασιών που θα δημιουργηθούν στον «κόσμο» μας. 7. Αντιγράψτε την έξοδο του προγράμματος στην εργαστηριακή αναφορά σας. ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 7
Βήμα 3: Ο αλγόριθμος Wave Σε αυτό το βήμα της εργαστηριακής άσκησης θα προσπαθήσουμε υλοποιήσουμε μία απλή έκδοση του κυματικού αλγόριθμο. Σε αυτή την έκδοση, θα υποθέσουμε δακτύλιο μεγέθους Ν με προσανατολισμό δεξιόστροφο. Στον δακτύλιο υπάρχει ένας αρχικοποιητής (θα επιλέξουμε έναν συγκεκριμένο επεξεργαστή). Κάθε επεξεργαστής όταν κατέχει το token πρέπει να εκτυπώνει το κατάλληλο μήνυμα. Ο αρχικοποιητής επίσης θα πρέπει να εκτυπώνει το κατάλληλο μήνυμα στην αρχή και στο τέλος του πρωτοκόλλου. Για αυτό το λόγο: 1. Συνδεθείτε σε ένα λειτουργικό Linux (Ubuntu ή LUbuntu). 2. Ανοίξτε ένα terminal 3. Δημιουργήστε έναν φάκελο MPI_ABCD, όπου ABCD το μητρώο σας. Για να το πετύχετε αυτό, εκτελέστε την εντολή mkdir MPI_ABCD. Στην συνέχεια μπείτε στον φάκελο εκτελώντας την εντολή cd MPI_ABCD. 4. Δημιουργήστε το αρχείο time.c εκτελώντας την εντολή gedit wave1.c 5. Αντιγράψτε το παρακάτω πρόγραμμα στο αρχείο σας #include <mpi.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { MPI_Init(NULL, NULL); int world_rank; MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); int world_size; MPI_Comm_size(MPI_COMM_WORLD, &world_size); int token = 0; int previous = world_rank - 1; int next = world_rank + 1; if (previous == -1) previous = world_size - 1; if (next >= world_size) next = 0; if (world_rank == 0) { token = 1; MPI_Send(&token, 1, MPI_INT, next, 0, MPI_COMM_WORLD); MPI_Recv(&token, 1, MPI_INT, previous, 0, MPI_COMM_WORLD, printf("starter process %d received token %d from process %d\n", world_rank, token, previous); printf("bye BYE\n\n"); if (world_rank!= 0) { MPI_Recv(&token, 1, MPI_INT, previous, 0, MPI_COMM_WORLD, printf("process %d received token %d from process %d\n", world_rank, token, previous); MPI_Send(&token, 1, MPI_INT, next, 0, MPI_COMM_WORLD); token = 0; ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 8
MPI_Finalize(); 6. Αφού αποθηκεύσετε και κλείσετε το αρχείο, μεταγλωττίστε το εκτελώντας την εντολή mpicc wave1.c o wave1 7. Εάν δεν έγινε κάποιο λάθος, ο μεταγλωττιστής δεν θα πρέπει να βγάλει κανένα μήνυμα. Τότε, μπορείτε να εκτελέσετε το πρόγραμμα, τρέχοντας την εντολή mpirun np 10./wave1. Σε αυτή την εντολή, η παράμετρος np καθορίζει το πλήθος των διεργασιών που θα δημιουργηθούν στον «κόσμο» μας. 8. Αντιγράψτε την έξοδο του προγράμματος στην εργαστηριακή αναφορά σας. Τι παρατηρείτε; Βήμα 4: Ο αλγόριθμος Polling 3 η Έκδοση Σε αυτό το βήμα θα τροποποιήσουμε το προηγούμενο πρόγραμμα, έτσι ώστε κάθε διεργασία να εκτελεί ένα κρίσιμο τμήμα πριν συνεχίσει στην αποστολή του token. Υποθέστε πως κάθε κόμβος του κατανεμημένου συστήματος υποστηρίζει κάθε χρονική στιγμή έναν αριθμό ενεργών διεργασιών χρηστών. Ο αριθμός αυτός ανήκει στο διάστημα {1..30. Θα χρησιμοποιήσουμε τη συνάρτηση rand( ) για να προσομοιώσουμε τον αριθμό αυτό σε κάθε επεξεργαστή. Έτσι, θα τροποποιήσουμε τον κώδικα του βήματος 2, ώστε να υπολογίζει το σύνολο των ενεργών διεργασιών χρηστών του δικτύου, μια ορισμένη χρονική στιγμή. Υποθέστε πως ο αριθμός των ενεργών διεργασιών χρηστών σε κάθε διεργασία δεν μεταβάλλεται κατά την ώρα εκτέλεσης του πρωτοκόλλου υπολογισμού. 1. Ανοίξτε ένα terminal 2. Μπείτε στον φάκελο MPI_ABCD. 3. Δημιουργήστε το αρχείο polling3.c εκτελώντας την εντολή gedit polling3.c 4. Αντιγράψτε το παρακάτω πρόγραμμα στο αρχείο σας #include <mpi.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <limits.h> int main(int argc, char** argv) { int starter = 11;//Edo dilwnoyme poios komvos theloume na einai arxikopoihtis //Fysika tha prepei na einai mikrotero apo to world_size! MPI_Init(NULL, NULL); //Arxikopoioume to MPI int world_rank; MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); int world_size; MPI_Comm_size(MPI_COMM_WORLD, &world_size); int ack = 0; int next; // o epomenos komvos srand(time(null) + world_rank); int nproc = rand()%30 + 1; int np; if (world_rank == starter) { int sum = 0; ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 9
printf("-------polling PROTOCOL STARTING-------\n"); next = world_rank; for (next = 0; next < world_size; next++) { if (next!= starter) { MPI_Send(&ack, 1, MPI_INT, next, 0, MPI_COMM_WORLD); printf("starter process %d awakes process %d\n", world_rank, next); MPI_Recv(&np, 1, MPI_INT, next, 0, MPI_COMM_WORLD, sum = sum + np; printf("ok Starter process %d. Process %d is awake and has %d running processes!\n", starter, next, np); printf("total PROCESSES are %d\n", sum); printf("-------polling PROTOCOL ENDING-------\n"); if (world_rank!= starter) { MPI_Recv(&ack, 1, MPI_INT, starter, 0, MPI_COMM_WORLD, // printf("nproc = %d\n", nproc); MPI_Send(&nproc, 1, MPI_INT, starter, 0, MPI_COMM_WORLD); MPI_Finalize(); 5. Προσπαθήστε να προβλέψετε την έξοδο του προγράμματος 6. Αφού αποθηκεύσετε και κλείσετε το αρχείο, μεταγλωττίστε το εκτελώντας την εντολή mpicc polling3.c o polling3 7. Εάν δεν έγινε κάποιο λάθος, ο μεταγλωττιστής δεν θα πρέπει να βγάλει κανένα μήνυμα. Τότε, μπορείτε να εκτελέσετε το πρόγραμμα, τρέχοντας την εντολή mpirun np 10./polling3 8. Αντιγράψτε την έξοδο του προγράμματος στην εργαστηριακή αναφορά σας. ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 10
ΔΡΑΣΤΗΡΙΟΤΗΤΕΣ Προσπαθήστε να λύσετε τις παρακάτω δραστηριότητες. Οι απαντήσεις κάθε δραστηριότητας θα πρέπει να εμφανίζονται στην εργαστηριακή σας αναφορά. 1. Μετατρέψτε το παράδειγμα του βήματος 2, έτσι ώστε ο αρχικοποιητής να εκκινεί την διαδικασία εκλογής αρχηγού κάθε 60 δευτερόλεπτα. 2. Μετατρέψτε το παράδειγμα του βήματος 3 έτσι ώστε να ο αρχικοποιητής να υπολογίζει το χρονικό διάστημα που απαιτείται για να ολοκληρωθεί ο αλγόριθμος. ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 11
ΒΙΒΛΙΟΓΡΑΦΙΑ "MPI: The Complete Reference" by Snir, Otto, Huss-Lederman, Walker, and Dongarra, MIT Press (also in Postscript and html) "Using MPI: Portable Parallel Programming with the Message-Passing Interface, " by Gropp, Lusk and Skjellum, MIT Press Designing and Building Parallel Programs, by Ian Foster, Addison-Wesley, 1995. Parallel Programming with MPI, by Peter Pacheco, Morgan-Kaufmann, 1997. MPI θεωρία και Εφαρμογές, A. Μάργαρης, Εκδόσεις Τζιόλα, 2008. Homepage: http:// www.mcs.anl.gov/mpi ΜΑΙΟΣ, 2018 ΕΙΣΑΓΩΓΗ ΣΤΑ ΚΑΤΑΝΕΜΗΜΕΝΑ ΣΥΣΤΗΜΑΤΑ 12