mpi-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

114
Εργαστήριο Παράλληλου Υπολογισμού Τμήμα Πληροφορικής Σχολή Τεχνολογικών Εφαρμογών Τ.E.Ι. Αθήνας Message Passing Interface (MPI) Γραμματή Πάντζιου Μανώλης Κακατσάκης Βασίλης Μάμαλης Φεβρουάριος 2001

Upload: loneeagle110

Post on 18-Nov-2014

228 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Εργαστήριο Παράλληλου ΥπολογισμούΤμήμα ΠληροφορικήςΣχολή Τεχνολογικών Εφαρμογών Τ.E.Ι. Αθήνας

Message Passing Interface (MPI)

Γραμματή Πάντζιου Μανώλης Κακατσάκης

Βασίλης Μάμαλης

Φεβρουάριος 2001

Page 2: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

2

Page 3: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Οι σημειώσεις έχουν γραφεί με βάση τα κάτωθι βοηθήματα:

1. MPI: The complete reference, MIT Press

2. A User’s Guide to MPI, University of San Francisco

3. Course notes for MPI, Edinburgh Parallel Computing Centre

4. Installation Guide to mpich, Argonne National Laboratory

http://www.mcs.anl.gov/mpi/mpich/docs

3

Page 4: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

ΠΕΡΙΕΧΟΜΕΝΑ

1. Εισαγωγή...................................................................71.1 Πρότυπο παράλληλου προγραμματισμού με πέρασμα μηνυμάτων......71.2 Τι είναι το MPI....................................................................................71.3 Οι σκοποί του MPI.............................................................................81.4 Τι παρέχεται και τι όχι με το MPI.......................................................9

2. MPI Προγράμματα.................................................102.1 MPI χειριστές....................................................................................102.2 Επιστροφή λαθών..............................................................................112.3 Συμβάσεις ονοματολογίας και διασύνδεση με C και FORTRAN......112.4 Τύποι Δεδομένων του MPI................................................................12

2.4.1 Βασικοί τύποι δεδομένων............................................................122.4.2 Ειδικοί τύποι δεδομένων.............................................................132.4.3 Κανόνες για το ταίριασμα των τύπων δεδομένων........................14

2.5 Αρχικοποίηση του MPI.....................................................................142.5.1 Η συνάρτηση MPI_Initialized.....................................................15

2.6 Τερματισμός του MPI.......................................................................152.6.1 Η συνάρτηση MPI_Abort............................................................16

2.7 Η έννοια του Communicator.............................................................162.7.1 Η συνάρτηση MPI_Comm_size..................................................172.7.2 Τάξη (rank) διεργασίας και η συνάρτηση MPI_Comm_rank.......172.7.3 Ομάδα (Group) διεργασίας..........................................................18Η συνάρτηση MPI_Group_size............................................................18Η συνάρτηση MPI_Group_rank...........................................................19Η συνάρτηση MPI_Comm_group........................................................19

2.8 Δομή ενός προγράμματος MPI σε C..................................................192.9 Δομή ενός προγράμματος MPI σε Fortran........................................222.10 Η συνάρτηση MPI_Get_processor_name........................................232.11 Προκαθορισμένες σταθερές.............................................................242.12 Αδιαφανή αντικείμενα (Opaque Objects).........................................242.13 Είδη MPI συναρτήσεων..................................................................252.14 Παράμετροι κλήσης.........................................................................25

3. Επικοινωνία Κόμβου με Κόμβο.............................273.1 Γενικά............................................................................................... 273.2 Μηνύματα.........................................................................................273.3 Επικοινωνία κόμβου με κόμβο (point to point)..................................28

3.3.1 Κανόνες που διέπουν την επικοινωνία κόμβου με κόμβο............29

4

Page 5: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

3.4 Αναστέλλουσα (Blocking) επικοινωνία.............................................29Η συνάρτηση MPI_Send...................................................................30Η συνάρτηση MPI_Recv..................................................................30

3.4.1 Η έννοια της ολοκλήρωσης των MPI_Send και MPI_Recv.........313.4.2 Επιλογή ενός μηνύματος.............................................................323.4.3 Σχόλια στο blocking receive........................................................333.4.4 Η συνάρτηση MPI_Probe............................................................343.4.5 Οι συναρτήσεις MPI_Pack και MPI_Unpack.............................34

3.5 Παραδείγματα κώδικα.......................................................................363.5.1 Blocking Send και Receive..........................................................363.5.2 Αποστολή πολλών μηνυμάτων σε μια διεργασία.........................373.5.3 Ανταλλαγή μηνυμάτων μεταξύ δύο διεργασιών (Ping - pong). . .393.5.4 Μέγιστο μέγεθος ενδιάμεσης μνήμης..........................................403.5.5 Υπολογισμός του π......................................................................43

3.6 Εκτέλεση ενός προγράμματος MPI...................................................473.7 Συμπεράσματα από την αναστέλλουσα επικοινωνία..........................47

4. Μη Αναστέλλουσα Επικοινωνία............................484.1 Γενικά............................................................................................... 484.2 Κίνητρο για μη αναστέλλουσα επικοινωνία.......................................484.3 Κλήσεις μη αναστέλλουσας επικοινωνίας και αντικείμενο request....494.4 Συναρτήσεις μη αναστέλλουσας επικοινωνίας...................................50

4.4.1 Συναρτήσεις έναρξης επικοινωνίας (posting send και receive)....50Η συνάρτηση MPI_Isend.................................................................50Η συνάρτηση MPI_Irecv.................................................................51

4.4.2 Συναρτήσεις ολοκλήρωσης επικοινωνίας....................................52Η συνάρτηση MPI_Wait..................................................................52Η συνάρτηση MPI_Test...................................................................53

4.5 Ακύρωση επικοινωνίας......................................................................534.6 Έλεγχος για ολοκλήρωση πολλών επικοινωνιών...............................54

4.6.1 Η συνάρτηση MPI_Waitany.......................................................554.6.2 Η συνάρτηση MPI_Testany........................................................564.6.3 Η συνάρτηση MPI_Waitall........................................................564.6.4 Η συνάρτηση MPI_Testall.........................................................574.6.5 Η συνάρτηση MPI_Waitsome....................................................574.6.6 Η συνάρτηση MPI_Testsome.....................................................58

4.7 Σύγκριση αναστέλλουσας και μη αναστέλλουσας επικοινωνίας........584.8 Καταστάσεις επικοινωνίας................................................................59

4.8.1 Standard send..............................................................................604.8.2 Synchronous send........................................................................60

Οι συναρτήσεις MPI_Ssend και MPI_Issend...................................614.8.3 Buffered send..............................................................................61

Η συνάρτηση MPI_Ibsend...............................................................62Οι συναρτήσεις MPI_Buffer_attach και MPI_Buffer_detach............62

4.8.4 Ready send.................................................................................63Η συνάρτηση MPI_Rsend................................................................64

5. Συλλογική Επικοινωνία..........................................655.1 Γενικά............................................................................................... 65

5

Page 6: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

5.2 Είδη συναρτήσεων συλλογικής επικοινωνίας....................................655.3 Σύγκριση point-to-point και συλλογικής επικοινωνίας......................665.4 Συναρτήσεις συλλογικής επικοινωνίας..............................................67

Η συνάρτηση MPI_Barrier..................................................................67Η συνάρτηση MPI_Bcast.....................................................................67Η συνάρτηση MPI_Gather...................................................................69Η συνάρτηση MPI_Scatter...................................................................70Η συνάρτηση MPI_Allgather...............................................................71Η συνάρτηση MPI_Alltoall..................................................................72

5.5 Παράδειγμα συλλογικής επικοινωνίας...............................................72

6. Εικονικές Τοπολογίες.............................................756.1 Γενικά............................................................................................... 756.2 Βασικές συναρτήσεις διαχείρησης καρτεσιανών τοπολογιών............76

Η συνάρτηση MPI_Cart_create............................................................76Οι συναρτήσεις MPI_CART_RANK και MPI_CART_COORDS........77Η συνάρτηση MPI_Cart_sub................................................................77

Παράρτημα..................................................................781. Εγκατάσταση του MPI........................................................................78

1.1 Το πρόγραμμα tstmachines.............................................................802. Μεταγλώττιση.....................................................................................803. Εκτέλεση............................................................................................. 814. Οι συναρτήσεις του MPI σε κατηγορίες..............................................82

4.1 Συναρτήσεις διαχείρισης του περιβάλλοντος....................................824.2 Συναρτήσεις επικοινωνίας Point-to-Point.......................................824.3 Συναρτήσεις συλλογικής επικοινωνίας............................................844.4 Συναρτήσεις διαχείρισης ομάδων....................................................854.5 Συναρτήσεις διαχείρισης Communicators.......................................854.6 Συναρτήσεις για τους παραγόμενους τύπους δεδομένων...................864.7 Συναρτήσεις διαχείρισης εικονικών τοπολογιών..............................864.8 Διάφορες συναρτήσεις...................................................................87

5. MPI σταθερές...................................................................................... 875.1 Αποτελέσματα συναρτήσεων...........................................................875.2 Μηνύματα λάθους...........................................................................885.3 Ορίσματα κλήσης............................................................................885.4 Τύποι δεδομένων............................................................................895.5 Συμβολικές σταθερές......................................................................89

6

Page 7: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

1. Εισαγωγή

1.1 Πρότυπο παράλληλου προγραμματισμού με πέρασμα μηνυμάτων

Στο πρότυπο παράλληλου προγραμματισμού με πέρασμα μηνυμάτων (message

passing model), ο παράλληλος υπολογισμός αποτελείται από έναν αριθμό διεργασιών κάθε

μία από τις οποίες επεξεργάζεται κάποια «τοπικά» δεδομένα. Κάθε διεργασία έχει τις δικές

της καθαρά τοπικές μεταβλητές, και δεν υπάρχει η δυνατότητα κάποια διεργασία να έχει

άμεση πρόσβαση στην τοπική μνήμη μιας άλλης διεργασίας. Η επικοινωνία μεταξύ των

διεργασιών και η ανταλλαγή δεδομένων, προκειμένου να συνεργαστούν στην επίλυση ενός

προβλήματος, επιτυγχάνεται με την αποστολή και την παραλαβή μηνυμάτων.

Θα πρέπει να παρατηρήσουμε ότι το πρότυπο

αναφέρεται σε διεργασίες οι οποίες δεν τρέχουν αναγκαστικά, σε διαφορετικούς

επεξεργαστές. Στη συνέχεια, θα υποθέτουμε ότι διαφορετικές διεργασίες τρέχουν σε

διαφορετικούς επεξεργαστές και θα χρησιμοποιούμε εναλλακτικά, και τους δύο όρους

(διεργασία και επεξεργαστής).

μπορεί να υλοποιηθεί σε μια ποικιλία διαφορετικών μηχανών όπως για παράδειμα, σε

πολυεπεξεργαστές με κοινή μνήμη, σε δίκτυα σταθμών εργασίας (networks of

workstations), και ακόμη σε μηχανές με ένα μόνον επεξεργαστή.

επιτρέπει περισσότερο έλεγχο πάνω στη θέση και τη ροή των δεδομένων μέσα σε ένα

παράλληλο πρόγραμμα από ότι ένα πρότυπο κοινής μνήμης. `Ετσι, τα προγράμματα τα

οποία χρησιμοποιούν πέρασμα μηνυμάτων, συνήθως, έχουν καλύτερη απόδοση.

1.2 Τι είναι το MPI

Το MPI (Message Passing Interface) είναι μία βιβλιοθήκη συναρτήσεων (σε C) ή

υπορουτινών (σε Fortran) για τη συγγραφή παράλληλων προγραμμάτων με πέρασμα

μηνυμάτων σε C ή FORTRAN σε μια μεγάλη ποικιλία συστημάτων. Περιλαμβάνει ένα

μεγάλο αριθμό συναρτήσεων οι οποίες διαχειρίζονται την επικοινωνία μεταξύ των

επεξεργαστών.

Ένα πρόγραμμα MPI αποτελείται από πολλές διεργασίες που εκτελούνται παράλληλα.

Κάθε διεργασία εκτελεί ένα τμήμα του ίδιου προγράμματος και χρησιμοποιεί τα δικά της

7

Page 8: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

δεδομένα για να κάνει τους υπολογισμούς της. Άρα, κάθε διεργασία χρησιμοποιεί το δικό

της χώρο διευθύνσεων χωρίς αυτό να σημαίνει ότι δεν υπάρχουν υλοποιήσεις του MPI για

επεξεργαστές με διαμοιραζόμενη μνήμη. Ο αριθμός των διεργασιών που δημιουργούνται σε

ένα πρόγραμμα MPI, είναι σταθερός και καθορίζεται από τον προγραμματιστή. Κατά τη

διάρκεια της εκτέλεσης δεν μπορεί να δημιουργήσει καινούργιες διεργασίες ή να

καταστρέψει κάποια από τις υπάρχουσες. Δηλαδή, το πρόγραμμα πρέπει να εκτελεστεί από

την αρχή για να οριστεί ξανά ο καινούργιος αριθμός διεργασιών. Ωστόσο, νεώτερες

υλοποιήσεις του MPI υποστηρίζουν τη δημιουργία και καταστροφή διεργασιών κατά το

χρόνο εκτέλεσης (run-time process management).

Η μεταφορά δεδομένων από τις μεταβλητές μιας διεργασίας στις μεταβλητές μιας

άλλης διεργασίας γίνεται με πέρασμα μηνυμάτων από τη μία διεργασία στην άλλη. Την

αποστολή των μηνυμάτων την αναλαμβάνει το MPI. Ο προγραμματιστής καλεί τις

συναρτήσεις αποστολής και λήψης (ή παραλαβής) μηνυμάτων μέσα από το πρόγραμμά του

αλλά, δεν ασχολείται με τις λεπτομέρειες της επικοινωνίας μεταξύ των διεργασιών. Ωστόσο,

ο προγραμματιστής όταν εκτελεί την εφαρμογή, μπορεί να δώσει ως παράμετρο τον αριθμό

των διεργασιών που θα δημιουργηθούν για την επίλυση του προβλήματος.

Το πρότυπο MPI-1 ορίσθηκε την `Ανοιξη του 1994. Αξίζει να σημειωθεί ότι:

Το πρότυπο καθορίζει τα ονόματα, τις παραμέτρους κλήσεις και τις εξόδους

συναρτήσεων και υπορουτινών προκειμένου να καλούνται από Fortran και C κώδικα,

αντίστοιχα. `Ολες οι υλοποιήσεις του MPI πρέπει να υπακούουν σε αυτούς τους

κανόνες, προκειμένου να εξασφαλίζεται η μεταφερσιμότητα (portability).

Η λεπτομερής υλοποίηση της βιβλιοθήκης αφήνεται στους κατασκευαστές, οι οποίοι

είναι έτσι, ελεύθυεροι να παράγουν βέλτιστες εκδόσεις (optimized versions) για τις

μηχανές τους.

Το πρότυπο έχει γίνει αποδεκτό από πολλούς φορείς και ερευνητές τόσο στη βιομηχανία

των υπολογιστών όσο και στα πανεπιστήμια. Υλοποιήσεις του υπάρχουν διαθέσιμες για

μια μεγάλη ποικιλία από πλατφόρμες.

Το πρότυπο MPI-2 έχει επίσης, ορισθεί. `Εχει χαρακτηριστικά και δυνατότητες που

δεν περιλαμβάνονται στο MPI-1, όπως, εργαλεία για παράλληλη Ι/Ο, διασύνδεση με τη C++

και τη Fortran 90, και διαχείρηση δυναμικών διεργασιών. Στη συνέχεια χρησιμοποιώντας

τον όρο MPI θα αναφερόμαστε στο MPI-1.

1.3 Οι σκοποί του MPI

Ο βασικός σκοπός του MPI είναι να δώσει στους προγραμματιστές ένα καλά ορισμένο

πρότυπο παρέχοντας ένα σύνολο συναρτήσεων για τη συγγραφή παραλλήλων

προγραμμάτων με πέρασμα μηνυμάτων. Τα MPI προγράμματα είναι μεταφέρσιμα σε πολλές

αρχιτεκτονικές υπολογιστών και ο εκτελέσιμος κώδικας που παράγεται, είναι αρκετά

αποδοτικός. Η μεγάλη ποικιλία συναρτήσεων που παρέχει το MPI, οδηγεί σε ευέλικτα

προγράμματα που μπορούν εύκολα να τροποποιηθούν και να συντηρηθούν. Το MPI

παρέχει:

8

Page 9: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Διαφορετικούς τύπους επικοινωνίας μεταξύ των διεργασιών. Αποτελεσματική

επικοινωνία μεταξύ των διεργασιών.

`Ενα ασφαλές πρότυπο επικοινωνίας μεταξύ των διεργασιών (blocking send-receive).

Ειδικές ρουτίνες για κοινές «συλλογικές» (collective) λειτουργίες.

Δυνατότητα χειρισμού τοπολογιών και τύπων δεδομένων που ορίζονται από το χρήστη.

Δυνατότητα ανάπτυξης και εκτέλεσης του κώδικα σε παράλληλα συστήματα με

διαφορετική αρχιτεκτονική καθώς επίσης, και σε ανομοιογενή δίκτυα σταθμών

εργασίας χωρίς προβλήματα συμβατότητας.

Ανεξαρτησία από τη γλώσσα προγραμματισμού που χρησιμοποιούμε. Οι συναρτήσεις

του MPI δεν εμπλέκονται με τις συναρτήσεις της γλώσσας. Για παράδειγμα, η

συνάρτηση printf της C εξακολουθεί να λειτουργεί κανονικά, όταν εκτελείται ένα MPI

πρόγραμμα.

Εύκολα μεταφέρσιμο κώδικα. Αρκεί να υπάρχει η βιβλιοθήκη του MPI για το

συγκεκριμένο σύστημα και ένας μεταγλωττιστής της C ή της FORTRAN.

.

1.4 Τι παρέχεται και τι όχι με το MPI

Το MPI παρέχει υποστήριξη για τα παρακάτω:

Επικοινωνία κόμβου με κόμβο (point-to-point).

Συλλογική επικοινωνία.

Οργάνωση των διεργασιών σε ομάδες (group).

Οργάνωση των επικοινωνιών σε communicators.

Τοπολογίες επικοινωνίας διεργασιών.

Δυνατότητα διασύνδεσης με C και FORTRAN.

Ωστόσο, δεν παρέχει υποστήριξη για:

Αποσφαλμάτωση (debugging).

Εργαλεία αυτόματης παραγωγής πηγαίου κώδικα.

Πολυνηματικές εφαρμογές.

Διαχείριση I/O.

Δυναμική δημιουργία και καταστροφή διεργασιών κατά το χρόνο εκτέλεσης.

Αρχική κατανομή των διεργασιών στους επεξεργαστές.

Το MPI είναι μια βιβλιοθήκη συναρτήσεων και όχι ένα λειτουργικό σύστημα. Ο ρόλος

του περιορίζεται στην επικοινωνία των διεργασιών με τη χρήση αυτών και μόνο των

συναρτήσεων. Σε χαμηλό επίπεδο, οι συναρτήσεις του MPI συνεργάζονται με τις

συναρτήσεις επικοινωνίας του λειτουργικού συστήματος για να γίνει η μετάδοση των

μηνυμάτων.

9

Page 10: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

2. MPI Προγράμματα

Το MPI αποτελεί μια βιβλιοθήκη. Μια διεργασία MPI αποτελείται από ένα

πρόγραμμα σε FORTRAN ή C το οποίο επικοινωνεί με άλλες διεργασίες MPI

χρησιμοποιώντας MPI συναρτήσεις . Οι ρουτίνες του MPI παρέχουν στον προγραμματιστή

μια συνεπή προγραμματιστική διασύνδεση σε μια μεγάλη ποικιλία συστημάτων. Ο τρόπος

που θα γίνει η ανάθεση των διεργασιών στους επεξεργαστές, δεν ορίζεται από το πρότυπο

του MPI διότι, εξαρτάται από το συγκεκριμένο σύστημα που χρησιμοποιείται. Κάθε

υλοποίηση παρέχει τη δική της μέθοδο για την κατανομή των διεργασιών. Ωστόσο, σε όλες

τις υλοποιήσεις παρέχεται η συνάρτηση MPI_Init η οποία ενεργοποιεί το MPI. Μέσω αυτής

της συνάρτησης, η αρχικοποίηση του MPI καθώς και η αρχική κατανομή των διεργασιών

στους επεξεργαστές γίνονται, από τη μεριά του προγραμματιστή, με τον ίδιο τρόπο.

2.1 MPI χειριστές

Το MPI διατηρεί εσωτερικές δομές δεδομένων οι οποίες σχετίζονται με την

επικοινωνία. Αυτές προσπελαύνονται από το χρήστη με τη βοήθεια των χειριστών. Οι

χειριστές επιστρέφονται στον προγραμματιστή από κάποιες MPI συναρτήσεις και μπορούν

να χρησιμοποιηθούν σε κλήσεις κάποιων άλλων συναρτήσεων.

Στη C, οι χειριστές είναι ακέραιοι αριθμοί ή δείκτες σε ειδικά ορισμένους τύπους

δεδομένων που έχουν ορισθεί μέ το typedef. Στη Fortran, οι χειριστές είναι ακέραιοι αριθμοί

ή πιθανά, πίνακες ακεραίων αριθμών. Δύο παραδείγματα χειριστών είναι τα εξής:

MPI_SUCCESS - Ακέραιος και στη C και στη Fortran που χρησιμοποιείται για τον

έλεγχο των κωδικών λάθους.

MPI_COMM_WORLD – Στη C, ένα αντικείμενο τύπου MPI_COMM (ένας

communicator). Στη Fortran, ένας ακέραιος. Και στις δύο περιπτώσεις, πρόκειται για

έναν προκαθορισμένο communicator που αποτελείται από όλες τις

διεργασίες/επεξεργαστές.

Στους χειριστές μπορούμε να αναθέσουμε τιμή χρησιμοποιώντας το συνηθισμένο

τελεστή ανάθεσης τιμής της FORTRAN ή της C.

10

Page 11: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

2.2 Επιστροφή λαθών

Σε γενικές γραμμές, κάθε συνάρτηση MPI που καλείται σε ένα πρόγραμμα C,

επιστρέφει έναν ακέραιο (int) ο οποίος δείχνει την κατάσταση εξόδου της κλήσης της

συνάρτησης

int err;

. . .

err = MPI_Init(&argc, &argv);

. . .

Στη FORTRAN οι ρουτίνες περιέχουν και μια πρόσθετη παράμετρο την IERR στην

οποία επιστρέφεται ο κωδικός λάθους:

INTEGER IERR

. . .

CALL MPI_INIT(IERR)

Στην περίπτωση που η κλήση της συνάρτησης ολοκληρώθηκε επιτυχώς, ο κωδικός

λάθους που επιστρέφεται, είναι ένας ακέραιος ίσος με την προκαθορισμένη σταθερά

MPI_SUCCESS. `Ετσι, στην περίπτωση για παράδειγμα, της C μπορούμε να ελέγξουμε για

επιτυχή ολοκλήρωση ως εξής:

if ( err = = MPI_SUCCESS) {

. . . routine ran correctly . . . }

}

Η εξ’ ορισμού ενέργεια του MPI όταν προκύψει ένα λάθος είναι να σταματήσει την

εκτέλεση του παράλληλου προγράμματος παρά να επιστρέψει απλώς τον κωδικό του λάθους.

Όμως, αυτή η εξ’ ορισμού ενέργεια, μπορεί να αλλάξει. Η τιμή του κωδικού λάθους

εξαρτάται από τη συγκεκριμένη αρχιτεκτονική του συστήματος στο οποίο εκτελείται το

πρόγραμμα MPI, και προσδιορίζει το είδος του λάθους που συνέβει. Επειδή τα λάθη που

μπορεί να προκύψουν σε ένα πρόγραμμα MPI εξαρτώνται από τη συγκεκριμένη

αρχιτεκτονική του συστήματος, δεν έχει καθοριστεί ένα πλήρες σύνολο λαθών και

αντίστοιχων κωδικών λάθους.

2.3 Συμβάσεις ονοματολογίας και διασύνδεση με C και FORTRAN

Στα ονόματα που χρησιμοποιεί το MPI έχουν ακολουθηθεί ορισμένες συμβάσεις οι

οποίες διευκολύνουν την ανάγνωση ενός προγράμματος και τη διόρθωση ορισμένων λαθών

11

Page 12: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

στον κώδικα. Όλα τα ονόματα των συναρτήσεων και σταθερών του MPI αρχίζουν με το

πρόθεμα MPI_. Τα ονόματα ρουτινών στη FORTRAN γράφονται με κεφαλαία γράμματα,

ενώ στη C γράφονται με μικρά και κεφαλαία (ο πρώτος χαρακτήρας κάθε λέξης κεφαλαίος

και οι υπόλοιποι με μικρά). Όταν όμως, η ρουτίνα περιγράφεται στην τεκμηρίωση του MPI,

ανεξάρτητα από τη γλώσσα, όλοι οι χαρακτήρες είναι κεφαλαίοι. Τα ονόματα των σταθερών

γράφονται πάντα με κεφαλαία και στις δύο γλώσσες.

Στη FORTRAN, οι χειριστές είναι τύπου INTEGER και οι πίνακες έχουν ως πρώτο

στοιχείο τους το 1. Στη C, για κάθε τύπο χειριστή έχει γίνει μια δήλωση typedef. Έτσι

υπάρχουν ξεχωριστοί τύποι χειριστή για κάθε είδος αντικειμένου. Π.χ., MPI_Datatype,

MPI_Comm, κ.λπ. Οι πίνακες έχουν ως πρώτο στοιχείο τους το 0.

Ορισμένα ορίσματα σε κάποιες ρουτίνες μπορούν να είναι οποιουδήποτε τύπου (για

παράδειγμα, integer, real κ.λπ.). Για παράδειγμα, στην τεκμηρίωση της ρουτίνας

MPI_ROUTINE σε FORTRAN, η περιγραφή είναι η εξής:

MPI_ROUTINE (MY_ARGUMENT, IERR)

<type> MY_ARGUMENT

Αυτό δείχνει ότι το MY_ARGUMENT μπορεί να είναι οποιουδήποτε τύπου. Στη C τα

ορίσματα αυτού του είδους απλά δηλώνονται σαν void *, δηλαδή δείκτης προς δεδομένα

οποιουδήποτε τύπου.

2.4 Τύποι Δεδομένων του MPI

Εφ’ όσον το MPI είναι κατά βάση, μια βιβλιοθήκη που ουσιαστικά, προσθέτει

συναρτήσεις στη C και στη FORTRAN, είναι λογικό να υποστηρίζει τους τύπους δεδομένων

της κάθε γλώσσας. Πράγματι, οι βασικοί τύποι δεδομένων του MPI είναι οι ίδιοι με τους

βασικούς τύπους δεδομένων της κάθε γλώσσας. Απλώς, έχουν διαφορετικά ονόματα, όπως

φαίνεται και από τους πίνακες που ακολουθούν.

2.4.1 Βασικοί τύποι δεδομένων

Στη C , οι βασικοί τύποι δεδομένων του MPI και οι αντίστοιχοι τύποι της C είναι:

Τύπος του MPI Τύπος της C

MPI_CHAR signed char

MPI_SHORT signed short int

MPI_INT signed int

12

Page 13: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

MPI_LONG signed long int

MPI_UNSIGNED_CHAR unsigned char

MPI_UNSIGNED_SHORT unsigned short int

MPI_UNSIGNED unsigned int

MPI_UNSIGNED_LONG unsigned long int

MPI_FLOAT float

MPI_DOUBLE double

MPI_LONG_DOUBLE long double

MPI_BYTE

MPI_PACKED

Στη Fortran, , οι βασικοί τύποι δεδομένων του MPI και οι αντίστοιχοι τύποι της

Fortran είναι:

Τύπος του MPI Τύπος της FORTRAN

MPI_INTEGER INTEGER

MPI_REAL REAL

MPI_DOUBLE_PRECISION DOUBLE PRECISION

MPI_COMPLEX COMPLEX

MPI_LOGICAL LOGICAL

MPI_CHARACTER CHARACTER

MPI_BYTE

MPI_PACKED

Οι τύποι δεδομένων MPI_BYTE και MPI_PACKED δεν αντιστοιχούν σε κάποιο τύπο

της C ή της Fortran. Το MPI_BYTE παριστάνει 8 δυαδικά ψηφία. Χρησιμοποιείται όταν

θέλουμε να αποστέλλουμε μηνύματα με bit-πεδία, όπου, η τιμή κάθε bit παριστάνει την τιμή

ενός flag. Αυτός ο τύπος μπορεί να χρησιμοποιηθεί για την αποστολή πληροφοριών ελέγχου

στον παραλήπτη. Αν και η αποστολή πληροφοριών ελέγχου μπορεί να γίνει και με τη χρήση

άλλων τύπων δεδομένων (π.χ., int), το MPI_BYTE είναι μικρότερο σε μέγεθος από το int

και άρα, η μετάδοση γίνεται γρηγορότερα. Ο τύπος MPI_PACKED χρησιμοποιείται για την

αποστολή και τη λήψη συνεπτυγμένων μηνυμάτων.

2.4.2 Ειδικοί τύποι δεδομένων

Στη C, το MPI παρέχει αρκετούς ειδικούς τύπους δεδομένων (δομές). Ως παράδειγμα,

αναφέρουμε τους εξής:

MPI_Comm - communicator

MPI_Status - δομή η οποία περιέχει πληροφορία για την κατάσταση (status) των MPI

κλήσεων

13

Page 14: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

MPI_Datatype

Οι παραπάνω τύποι χρησιμοπoιούνται στις δηλώσεις μεταβλητών. Για παράδειγμα, η

μεταβλητή new_comm μπορεί να δηλωθεί τύπου MPI_Comm ως εξής:

MPI_Comm new_comm;

Στη Fortran, οι αντίστοιχοι τύποι είναι όλοι ακέραιοι (INTEGER).

2.4.3 Κανόνες για το ταίριασμα των τύπων δεδομένων

Όταν γίνεται αποστολή ενός μηνύματος ενός συγκεκριμένου τύπου δεδομένων, η

διεργασία-παραλήπτης αναμένει να λάβει τον ίδιο τύπο. Για παράδειγμα, αν μια διεργασία

στέλνει ένα μήνυμα τύπου MPI_INTEGER, ο παραλήπτης πρέπει να καθορίσει ότι θα λάβει

τύπο MPI_INTEGER. Διαφορετικά, η επικοινωνία είναι λανθασμένη και η συμπεριφορά του

προγράμματος ακαθόριστη. Ακόμα, η μεταβλητή που περιέχει το προς μετάδοση μήνυμα,

πρέπει να είναι του ίδιου τύπου με τον τύπο του μηνύματος που καθορίστηκε. Δηλαδή, δεν

μπορούμε να καθορίσουμε τύπο MPI_INTEGER και να στείλουμε μήνυμα τύπου float ή

char. Η εξαίρεση του κανόνα είναι οι τύποι δεδομένων MPI_PACKED και MPI_BYTE, οι

οποίοι μπορούν να χρησιμοποιηθούν στη θέση οποιουδήποτε τύπου, αρκεί βέβαια, να

ταιριάζει το μέγεθος. Για παράδειγμα, δεν μπορούμε να εκχωρήσουμε τιμή τύπου float σε

μεταβλητή τύπου MPI_BYTE γιατί ο τύπος float καταλαμβάνει περισσότερα από ένα byte

στη μνήμη.

2.5 Αρχικοποίηση του MPI

Για να χρησιμοποιηθεί το MPI σε ένα πρόγραμμα C, πρέπει στην αρχή του να υπάρχει

η παρακάτω δήλωση προεπεξεργαστή:

#include «mpi.h»

ή

#include <mpi.h>

ανάλογα με το αν το αρχείο mpi.h βρίσκεται στον τρέχοντα κατάλογο ή στον κατάλογο που

περιέχει τα header αρχεία του μεταγλωττιστή. Αυτός ο header περιέχει τα prototypes των

συναρτήσεων καθώς και τον ορισμό των σταθερών και των δομών δεδομένων που

χρειάζονται σε ένα πρόγραμμα MPI. Στη FORTRAN, το αντίστοιχο αρχείο καλείται

mpif.inc.

Για να ξεκινήσει μια εφαρμογή MPI, το πρόγραμμα πρέπει να καλέσει τη συνάρτηση

MPI_Init. Η συνάρτηση αυτή ενεργοποιεί το MPI και δημιουργεί τις δομές δεδομένων που

14

Page 15: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

είναι απαραίτητες για την επικοινωνία των διεργασιών (για παράδειγμα, τον εξ’ ορισμού

Communicator MPI_COMM_WORLD). Η MPI_Init πρέπει να κληθεί μόνο μια φορά και

πριν από κάθε άλλη κλήση συνάρτησης MPI. Δεύτερη κλήση της αποτελεί λάθος. Η δήλωση

της είναι:

int MPI_Init(int *argc, char **argv)

Η κλήση της γίνεται με τις παραμέτρους που περνιούνται αυτόματα από τον

μεταγλωττιστή της C στη main, δηλαδή

MPI_Init(&argc, &argv);

Το argc είναι ο αριθμός των παραμέτρων που δίνονται στη γραμμή εντολών όταν

εκτελείται το πρόγραμμα. Το argv είναι ένας πίνακας χαρακτήρων ο οποίος περιέχει αυτές

τις παραμέτρους. Όταν εκτελείται το πρόγραμμα, το λειτουργικό σύστημα περνάει αυτόματα

αυτές τις παραμέτρους στη main. Έτσι, μπορούμε από τον κώδικα του προγράμματος να

χρησιμοποιήσουμε αυτές τις παραμέτρους. Μετά την κλήση της MPI_Init, όλες οι διεργασίες

που δημιουργήθηκαν, θα έχουν ένα αντίγραφο των ορισμάτων της main.

Η κλήση αυτή δημιουργεί τον αριθμό των διεργασιών που δίνουμε ως παράμετρο στη

γραμμή εντολών και τις αναθέτει στους διαθέσιμους επεξεργαστές. Οι λεπτομέρειες της

ανάθεσης εξαρτώνται από τη συγκεκριμένη υλοποίηση του MPI. Στη FORTRAN η

συνάρτηση δέχεται ως όρισμα μόνο έναν κωδικό λάθους τον οποίο και επιστρέφει μετά την

κλήση.

MPI_INIT(IERROR)

INTEGER IERROR

2.5.1 Η συνάρτηση MPI_Initialized

Μια συνάρτηση σχετική με την MPI_Init είναι η MPI_Initialized η οποία επιστρέφει

true αν έχει κληθεί η MPI_Init. Είναι χρήσιμη σε προγράμματα όπου ένα μέρος τους

εκτελείται τοπικά σε έναν επεξεργαστή (για παράδειγμα εισαγωγή αρχικών δεδομένων), ενώ

το υπόλοιπο εκτελείται παράλληλα με τη βοήθεια του MPI. Η δήλωση είναι:

int MPI_Initialized(int *flag)

Το αποτέλεσμα επιστρέφεται στην παράμετρο flag.

2.6 Τερματισμός του MPI

Πριν τερματιστεί ένα πρόγραμμα πρέπει να καλέσει τη συνάρτηση MPI_Finalize().

Όπως δείχνει και το όνομα της, η MPI_Finalize() κάνει την αντίστροφη λειτουργία από την

MPI_Init. Δηλαδή, καταστρέφει τις δομές που είχαν δεσμευθεί από την MPI_Init. Η

15

Page 16: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

συνάρτηση δεν ακυρώνει τις επικοινωνίες που βρίσκονται σε εξέλιξη, γι’ αυτό, πριν την

καλέσουμε, πρέπει να έχουν ολοκληρωθεί όλες οι επικοινωνίες. Αυτό είναι ευθύνη του

προγραμματιστή. Μετά την κλήση της, καμμία άλλη MPI συνάρτηση δεν μπορεί να κληθεί

ούτε καν η MPI_Init. Η MPI_Finalize πρέπει να καλείται από όλες τις διεργασίες. Αν

κάποια διεργασία δεν φθάσει σε αυτή την εντολή, το πρόγραμμα θα κρεμάσει. Η συνάρτηση

δεν δέχεται παραμέτρους και η κλήση της είναι:

MPI_Finalize();

2.6.1 Η συνάρτηση MPI_Abort

Το MPI παρέχει τη συνάρτηση MPI_Abort η οποία τερματίζει όλες τις διεργασίες που

περιέχονται σε ένα συγκεκριμένο communicator. Η σύνταξή της είναι η παρακάτω:

int MPI_Abort(MPI_Comm comm, int err)

Όπου:

comm Ο communicator ο οποίος περιέχει την ομάδα των διεργασιών που

θέλουμε να τερματίσουμε.

err Στην επιστροφή περιέχει έναν κωδικό λάθους.

Ο κωδικός λάθους συνήθως, δεν χρησιμοποιείται στις περισσότερες υλοποιήσεις του

MPI. Η ρουτίνα αυτή είναι λίγο περίεργη γιατί η συμπεριφορά της για comm MPI_COMM_WORLD είναι ακαθόριστη. Σε άλλες υλοποιήσεις η περίπτωση comm MPI_COMM_WORLD αντιμετωπίζεται σαν comm == MPI_COMM_WORLD και σε άλλες

σαν τον Communicator που ορίσαμε. Πάντως, αν δοθεί comm = MPI_COMM_WORLD

προκαλείται τερματισμός ολόκληρου του παράλληλου προγράμματος.

2.7 Η έννοια του Communicator

Ο communicator είναι ένας χειριστής (handle) ο οποίος αντιπροσωπεύει μία ομάδα

(group) διεργασιών που μπορούν να επικοινωνούν μεταξύ τους. Οι διεργασίες αυτές δεν

είναι απαραίτητο να έχουν δημιουργηθεί από την εκτέλεση ενός μόνο προγράμματος MPI.

Δηλαδή, είναι δυνατή η επικοινωνία διεργασιών οι οποίες ανήκουν σε διαφορετικά

παράλληλα προγράμματα. Αρκεί να ανήκουν όλες στον ίδιο communicator.

Όλες οι κλήσεις που έχουν να κάνουν με επικοινωνία διεργασιών, έχουν ως μία από

τις παραμέτρους τους το όνομα ενός communicator. Για να επικοινωνήσουν δύο ή

περισσότερες διεργασίες, πρέπει απαραίτητα, να ανήκουν στον ίδιο communicator.

Για τα περισσότερα προγράμματα MPI, αρκεί ένας βασικός communicator που

ονομάζεται MPI_COMM_WORLD. Αυτός ο communicator δημιουργείται αυτόματα από το

MPI και περιέχει όλες τις διεργασίες που δημιουργούνται όταν αρχίζει η εκτέλεση του

16

Page 17: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

προγράμματος. Με αυτό τον τρόπο, εξασφαλίζουμε ότι οποιαδήποτε διεργασία μπορεί να

επικοινωνήσει με οποιαδήποτε άλλη. Ως τόσο, μπορούμε να ορίσουμε επιπλέον

communicators οι οποίοι θα αποτελούνται από υποσύνολα των διαθέσιμων διεργασιών. Η

χρησιμότητα των πολλών communicators, έγκειται στο ότι μπορούμε να οργανώσουμε τις

διεργασίες σε διαφορετικές τοπολογίες. Για παράδειγμα, εκτός από τον

MPI_COMM_WORLD communicator, μπορούμε να δημιουργήσουμε και έναν δεύτερο, ο

οποίος θα περιέχει τις διεργασίες οργανωμένες σύμφωνα με τη δομή ενός πίνακα δύο

διαστάσεων. Με αυτή την τοπολογία, κάθε διεργασία χαρακτηρίζεται από ένα ζεύγος

συντεταγμένων (i, j) το οποίο δείχνει τη θέση της στον πίνακα. Έτσι, κάθε διεργασία μπορεί

εάν αυτό είναι επιθυμητό, να επικοινωνήσει μόνο με τις 4 γειτονικές της διεργασίες. Η

παραπάνω τοπολογία καλείται καρτεσιανή, και μπορεί να απλοποιήσει τον αλγόριθμο

πολλών προγραμμάτων ειδικά, όταν γίνεται διανομή των δεδομένων στις διεργασίες και

συλλογή των επιμέρους αποτελεσμάτων από κάθε μία.

2.7.1 Η συνάρτηση MPI_Comm_size

Για να βρούμε το μέγεθος ενός communicator, δηλαδή τον αριθμό των διεργασιών

που υπάρχουν σ’ αυτόν, καλούμε τη συνάρτηση MPI_Comm_size.

int MPI_Comm_size(MPI_Comm Comm, int *size);

όπου Comm είναι ο communicator το μέγεθος του οποίου επιστρέφεται στο size. Ένα τυπικό

παράδειγμα κλήσης είναι:

MPI_Comm_size(MPI_COMM_WORLD, &size);

Μετά την κλήση, η μεταβλητή size περιέχει τον αριθμό των διεργασιών που

περιέχονται στον communicator MPI_COMM_WORLD, δηλαδή, τον αριθμό όλων των

διεργασιών του MPI προγράμματος.

2.7.2 Τάξη (rank) διεργασίας και η συνάρτηση MPI_Comm_rank

`Οπως αναφέρθηκε παραπάνω, κάθε διεργασία μπορεί να ανήκει σε περισσότερους

από έναν communicator. Αν ένας communicator έχει n διεργασίες, τότε οι διεργασίες αυτές

είναι αριθμημένες διαδοχικά από το 0 έως το n-1. Ο αριθμός που αντιστοιχεί σε κάθε

διεργασία, καλείται τάξη (rank) της διεργασίας στο συγκεκριμένο communicator. Η τάξη της

διεργασίας-παραλήπτη ή/και της διεργασίας-αποστολέα περνιέται ως παράμετρος στις

συναρτήσεις αποστολής και παραλαβής (π.χ., στις συναρτήσεις MPI_Send και MPI_Recv)

κατά τη μετάδοση των μηνυμάτων.

Η τάξη μιας διεργασίας εξαρτάται από τον communicator στον οποίο ανήκει. Δηλαδή,

η ίδια διεργασία μπορεί να έχει διαφορετική τάξη σε έναν communicator και διαφορετική σε

κάποιον άλλο. Το αν θα διατηρηθεί ή όχι η τάξη μιας διεργασίας σε διαφορετικούς

17

Page 18: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

communicators, εξαρτάται από το πρόγραμμα που εκτελείται, καθώς, το MPI μπορεί να

αλλάξει την τάξη προκειμένου να βελτιστοποιήσει τις επιδόσεις του προγράμματος.

Μπορούμε να βρούμε την τάξη μιας διεργασίας σε έναν communicator καλώντας τη

συνάρτηση MPI_Comm_rank η οποία έχει δηλωθεί ως εξής:

int MPI_Comm_rank(MPI_Comm Comm, int rank);

Το Comm είναι ο communicator στον οποίο ανήκει η διεργασία. Στην παράμετρο rank

επιστρέφεται η τάξη της διεργασίας η οποία κάνει την κλήση. Επειδή η τάξη επιστρέφεται

μέσω παραμέτρου, πρέπει στη συνάρτηση να περάσουμε τη διεύθυνση της παραμέτρου. Άρα

η κλήση είναι:

MPI_Comm_rank(MPI_COMM_WORLD, &rank);

Μέσα στον κώδικα της συνάρτησης η τιμή της παραμέτρου μπορεί να τροποποιηθεί,

εφόσον, είναι γνωστή η διεύθυνσή της, για να επιστραφεί το αποτέλεσμα. Αυτό ισχύει για

όλες τις συναρτήσεις που επιστρέφουν αποτέλεσμα μέσω μιας παραμέτρου αντί μέσω του

ονόματός τους. Μέσω των παραμέτρων μια συνάρτηση μπορεί να επιστρέψει πολλά

αποτελέσματα, το καθένα σε διαφορετική παράμετρο. Μέσω του ονόματός της φυσικά

επιστρέφει μόνο μια τιμή, συνήθως έναν κωδικό λάθους ή μια τιμή που δείχνει ότι η κλήση

ήταν επιτυχής.

2.7.3 Ομάδα (Group) διεργασίας

Κάθε communicator περιέχει μια ομάδα (group) από διεργασίες. Στην

πραγματικότητα, μια ομάδα είναι τοπική για μία συγκεκριμένη διεργασία. Δηλαδή, η ομάδα

η οποία περιέχεται σε έναν communicator είναι προσυμφωνημένη μεταξύ των διεργασιών τη

στιγμή που δημιουργείται ο communicator. Έτσι, η ομάδα που περιέχεται στο

MPI_COMM_WORLD, περιέχει όλες τις διεργασίες του παράλληλου προγράμματος. Από

την πλευρά του προγραμματιστή, η ομάδα και ο communicator είναι τα ίδια. Η χρησιμότητα

της ομάδας θα γίνει κατανοητή όταν θα γίνει αναφορά στη συλλογική επικοινωνία

διεργασιών.

Η συνάρτηση MPI_Group_size

Η συνάρτηση αυτή είναι αντίστοιχη με την MPI_Comm_size για μια ομάδα. Δηλαδή,

επιστρέφει τον αριθμό των διεργασιών μιας ομάδας. Η σύνταξη της είναι

int MPI_Group_size(MPI_Group group, int *size)

18

Page 19: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

‘Όπου:

group Η ομάδα για την οποία ζητάμε τον αριθμό των διεργασιών.

size Εδώ επιστρέφεται ο αριθμός των διεργασιών της ομάδας που ζητάμε.

Αν η κλήση γίνει με group = MPI_GROUP_EMPTY, στην επιστροφή το size θα είναι

0. Η κλήση με group = MPI_GROUP_NULL είναι λάθος.

Η συνάρτηση MPI_Group_rank

Επιστρέφει την τάξη της διεργασίας η οποία την καλεί από μια συγκεκριμένη ομάδα.

Η σύνταξη είναι:

int MPI_Group_rank(MPI_Group group, int *rank)

Όπου:

group Η ομάδα της διεργασίας που κάνει την κλήση.

rank Επιστρέφει την τάξη.

Αν η διεργασία δεν είναι μέλος της συγκεκριμένης ομάδας, η συνάρτηση επιστρέφει

την τιμή της σταθεράς MPI_UNDEFINED.

Η συνάρτηση MPI_Comm_group

Καθορίζει την ομάδα που σχετίζεται με ένα δεδομένο communicator. Η σύνταξή της

είναι:

int MPI_Comm_group(MPI_Comm comm, MPI_Group *group)

Όπου:

comm Ο communicator για τον οποίο ζητάμε την ομάδα.

group Εδώ επιστρέφεται ένας δείκτης προς την ομάδα.

2.8 Δομή ενός προγράμματος MPI σε C

Η δομή ενός προγράμματος MPI σε C είναι η εξής:

#include «mpi.h»

/* Άλλες δηλώσεις #include */

.

19

Page 20: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

.

.

/* Ορισμός καθολικών μεταβλητών, δομών δεδομένων, συναρτήσεων κτλ */

.

.

.

main(int argc, char **argv)

{

.

.

.

/* Ορισμός μεταβλητών, δομών δεδομένων κτλ */

.

.

.

/* Αρχικοποίηση του MPI */

/* Δεν πρέπει να καλέσουμε άλλες συναρτήσεις του MPI πιο πριν */

MPI_Init(&argc, &argv);

.

.

.

/* Υλοποίηση του αλγορίθμου της εφαρμογής με κλήση συναρτήσεων C και

MPI */

.

.

.

/* Τερματισμός του MPI */

/* Δεν πρέπει να καλέσουμε άλλες συναρτήσεις του MPI μετά από αυτή */

MPI_Finalize();

/* Επιστροφή στο λειτουργικό σύστημα */

exit(0);

}

Ακολουθεί ένα συγκεκριμένο παράδειγμα:

#include "mpi.h"

#include <stdio.h>

int main(argc, argv)

20

Page 21: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

int argc;

char *argv[ ];

{

int numtasks, rank, rc;

rc = MPI_Init(&argc,&argv);

if (rc != 0) {

printf ("Σφάλμα στην αρχικοποίηση του MPI. Εξοδος.\n");

MPI_Abort(MPI_COMM_WORLD, rc);

}

MPI_Comm_size(MPI_COMM_WORLD,&numtasks);

MPI_Comm_rank(MPI_COMM_WORLD,&rank);

printf ("Αριθμός διεργασιών = %d My rank= %d\n", numtasks,rank);

/**********************************/

/* Το κυρίως πρόγραμμα */

/**********************************/

MPI_Finalize();

}

Το πρόγραμμα προσπαθεί να αρχικοποιήσει τη βιβλιοθήκη του MPI καλώντας την

MPI_Init. Αν για κάποιο λόγο αυτό δεν είναι δυνατόν, στη μεταβλητή rc επιστρέφεται

κάποιος κωδικός λάθους. Το πρόγραμμα τερματίζει με την κλήση της MPI_Abort. Αν η

κλήση της MPI_Init επιστρέψει 0, τότε το MPI αρχικοποιήθηκε επιτυχώς, και το σύστημα

δημιουργεί τον default communicator MPI_COMM_WORLD. Κατόπιν, εμφανίζεται ο

αριθμός των διεργασιών του communicator καθώς και η τάξη της διεργασίας που κάλεσε την

MPI_Comm_rank. Η βιβλιοθήκη του MPI κλείνει με την MPI_Finalize().

Η έξοδος του προγράμματος με 10 διεργασίες είναι η εξής

Αριθμός διεργασιών = 10 My rank= 0

Αριθμός διεργασιών = 10 My rank= 2

Αριθμός διεργασιών = 10 My rank= 1

Αριθμός διεργασιών = 10 My rank= 4

Αριθμός διεργασιών = 10 My rank= 3

Αριθμός διεργασιών = 10 My rank= 6

Αριθμός διεργασιών = 10 My rank= 5

Αριθμός διεργασιών = 10 My rank= 9

Αριθμός διεργασιών = 10 My rank= 8

Αριθμός διεργασιών = 10 My rank= 7

21

Page 22: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

`Οπως φαίνεται από την παραπάνω πιθανή έξοδο του προγράμματος, οι διεργασίες δεν

δημιουργούνται με τη σειρά που δείχνει η τάξη τους αλλά με τυχαία σειρά.

2.9 Δομή ενός προγράμματος MPI σε Fortran

Στη συνέχεια, δίνεται η δομή ενός προγράμματος MPI σε FORTRAN καθώς και ένα

παράδειγμα κώδικα.

PROGRAM simple

include ‘mpif.h’

integer errcode

C Initialize MPI

call MPI_INIT (errcode)

C Main part of program ...

call MPI_FINALIZE (errcode)

end

program simple

include 'mpif.h'

integer numtasks, rank, ierr, rc

call MPI_INIT(ierr)

if (ierr .ne. 0) then

print *,'Σφάλμα στην αρχικοποίηση του MPI. Έξοδος.'

call MPI_ABORT(MPI_COMM_WORLD, rc, ierr)

end if

call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)

call MPI_COMM_SIZE(MPI_COMM_WORLD, numtasks, ierr)

print *, ' Αριθμός διεργασιών =',numtasks,' My rank=',rank

C ****** Το κυρίως πρόγραμμα ******

22

Page 23: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

call MPI_FINALIZE(ierr)

end

Όπως είναι φανερό, η μόνη ουσιαστική διαφορά είναι ότι στη FORTRAN το MPI έχει

υλοποιηθεί με κλήση υπορουτινών και όχι με κλήση συναρτήσεων. Άρα, η επιστροφή λαθών

γίνεται μέσω παραμέτρου και όχι μέσω του ονόματος της ρουτίνας.

2.10 Η συνάρτηση MPI_Get_processor_name

Όταν τρέχει ένα παράλληλο πρόγραμμα, συνήθως, δεν γνωρίζουμε σε ποιο

επεξεργαστή εκτελείται κάθε διεργασία. Ειδικά στο MPI, μας ενδιαφέρει περισσότερο η

διεργασία (η τάξη της) παρά σε ποιον επεξεργαστή εκτελείται. Πολλές φορές όμως,

χρειάζεται να γνωρίζουμε το όνομα ενός συγκεκριμένου επεξεργαστή. Γι’ αυτό, μας

παρέχεται η συνάρτηση MPI_processor_name η οποία επιστρέφει το όνομα του επεξεργαστή

ο οποίος καλεί τη συνάρτηση. Η σύνταξή της είναι:

int MPI_Get_processor_name(char *name, int *resultlen)

Όπου:

name Η ενδιάμεση μνήμη στην οποία επιστρέφεται ένα string με το όνομα

του επεξεργαστή.

resultlen Το μήκος του string σε χαρακτήρες.

Η ενδιάμεση μνήμη name πρέπει να έχει μέγεθος τουλάχιστον, όσο είναι η τιμή της

σταθεράς MPI_MAX_PROCESSOR_NAME. Η συνάρτηση αυτή είναι χρήσιμη όταν το

σύστημά μας περιλαμβάνει επεξεργαστές διαφορετικής αρχιτεκτονικής, ο καθένας με τις

ιδιαιτερότητές του. Πριν κάνουμε οτιδήποτε έχει σχέση με αυτές τις ιδιαιτερότητες, ίσως να

χρειάζεται να γνωρίζουμε το όνομα του επεξεργαστή. Το τι ακριβώς επιστρέφεται από τη

συνάρτηση και με ποια μορφή, εξαρτάται από το λειτουργικό σύστημα και την

αρχιτεκτονική του συγκεκριμένου επεξεργαστή.

Θα πρέπει να έχουμε υπ’ όψιν ότι μπορούμε να έχουμε διαφορετικές αρχιτεκτονικές

επεξεργαστών σε ένα παράλληλο σύστημα, αλλά δεν μπορούμε να έχουμε δυο διαφορετικές

υλοποιήσεις του MPI στο ίδιο σύστημα. Δεν είναι δηλαδή, σίγουρο ότι οι διαφορετικές

υλοποιήσεις μπορούν να συνεργαστούν μεταξύ τους. Το MPI καθορίζει μια

προγραμματιστική διασύνδεση (δηλαδή, τις παραμέτρους και τύπο δεδομένων που

επιστρέφει κάθε συνάρτηση) αλλά, δεν καθορίζει το πρωτόκολλο που χρησιμοποιείται για

τη μετάδοση των μηνυμάτων. Ενδέχεται, δυο διαφορετικές υλοποιήσεις να χρησιμοποιούν

και διαφορετικά πρωτόκολλα επικοινωνίας και άρα, να είναι αδύνατον να συνεργαστούν

μεταξύ τους στο ίδιο σύστημα.

23

Page 24: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

2.11 Προκαθορισμένες σταθερές

Για να είναι ένα πρόγραμμα πιο ευανάγνωστο, πολλές παράμετροι κλήσης

συναρτήσεων έχουν ένα συμβολικό όνομα. Παράδειγμα αποτελούν οι σταθερές

MPI_ANY_TAG, MPI_ANY_SOURCE και ο χειριστής προς τον προκαθορισμένο

communicator MPI_COMM_WORLD. Με τη χρήση σταθερών σε παραμέτρους κλήσης,

αποφεύγονται τα λάθη, διότι ο προγραμματιστής δεν χρειάζεται να θυμάται την τιμή μιας

σταθεράς αλλά μόνο το όνομά της.

Επίσης, οι κωδικοί λάθους που επιστρέφονται από μια συνάρτηση, έχουν μία

συμβολική σταθερά που τους αντιστοιχεί. Σίγουρα, είναι ευκολότερο να θυμόμαστε ότι μια

επιτυχημένη κλήση επιστρέφει την τιμή της σταθεράς MPI_SUCCESS παρά ότι επιστρέφει

π.χ., 0 ή κάποιον άλλον αριθμό. Οι σταθερές που χρησιμοποιεί το MPI, παρουσιάζονται στο

Παράρτημα χωρισμένες σε κατηγορίες.

Επειδή όλες οι σταθερές και οι συναρτήσεις στο MPI έχουν όνομα που αρχίζει από

MPI_ καλό είναι να αποφεύγεται ο ορισμός μεταβλητών με όνομα που αρχίζει από MPI_

γιατί μπορεί να προκληθεί σύγχυση. Αν στο πρόγραμμα υπάρχουν ονόματα μεταβλητών ή

συναρτήσεων που αρχίζουν από MPI_ , τότε μπορεί να χρησιμοποιηθεί το πρόθεμα PMPI_

αντί για το MPI_.

2.12 Αδιαφανή αντικείμενα (Opaque Objects)

Το MPI χρησιμοποιεί μνήμη για την αποθήκευση δικών του δομών δεδομένων οι

οποίες λέγονται αδιαφανή αντικείμενα. Εκεί αποθηκεύονται οι communicators, οι ομάδες, η

ενδιάμεση μνήμη αποστολής και λήψης μηνυμάτων, οι τύποι δεδομένων που ορίζονται από

το χρήστη κ.λπ. Η μνήμη αυτή δεν είναι άμεσα προσπελάσιμη από τον προγραμματιστή, γι’

αυτό και τα αντικείμενα λέγονται αδιαφανή. Η προσπέλαση ενός αδιαφανούς αντικειμένου

γίνεται μέσω δεικτών που λέγονται χειριστές (handles). Οι χειριστές ορίζονται από τον

προγραμματιστή και άρα, βρίσκονται στο χώρο διευθύνσεων της διεργασίας που

προσπελαύνει το αντικείμενο. Στη C το MPI ορίζει διαφορετικό είδος χειριστή για κάθε

είδος αντικειμένου. Ο χειριστής χρησιμοποιείται μόνο από τη διεργασία που δημιούργησε το

αντικείμενο. Διαφορετικές διεργασίες κατά κανόνα, χρησιμοποιούν διαφορετικό χειριστή για

να προσπελάσουν το ίδιο αντικείμενο.

Μπορούμε να εκχωρήσουμε τιμή σε έναν χειριστή καθώς και να συγκρίνουμε δυο

χειριστές μεταξύ τους. Γενικά, ένας χειριστής μπορεί να χρησιμοποιηθεί όπως και μια

μεταβλητή. Υπάρχουν συναρτήσεις για τη δημιουργία, την προσπέλαση και την καταστροφή

ενός συγκεκριμένου είδους αδιαφανούς αντικειμένου. Οι συναρτήσεις αυτές δέχονται σαν

παράμετρο τον χειριστή του αντικειμένου.

Όταν καλείται μια συνάρτηση καταστροφής ενός αντικειμένου, αυτό παύει να είναι

προσπελάσιμο από το χρήστη και ο χειριστής ακυρώνεται. Όμως, το MPI δεν αποδεσμεύει

αμέσως τη μνήμη. Απλώς θέτει μια σημαία (flag) ότι το αντικείμενο μπορεί να διαγραφεί.

24

Page 25: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Έτσι, αν κάποια λειτουργία που αφορά το αντικείμενο, βρίσκεται σε εκκρεμότητα τη στιγμή

που καλείται η συνάρτηση καταστροφής, πρώτα ολοκληρώνεται η λειτουργία και μετά

καταστρέφεται το αντικείμενο.

Το MPI παρέχει μερικά προκαθορισμένα αδιαφανή αντικείμενα καθώς και τους

αντίστοιχους χειριστές. Αυτά τα αντικείμενα υπάρχουν όσο εκτελείται ένα MPI πρόγραμμα

και ο προγραμματιστής δεν μπορεί να τα καταστρέψει. Ένα παράδειγμα προκαθορισμένου

αδιαφανούς αντικειμένου αποτελεί ο communicator MPI_COMM_WORLD καθώς και

άλλες δομές δεδομένων που σχετίζονται με αυτόν.

2.13 Είδη MPI συναρτήσεων

Οι συναρτήσεις του MPI μπορούν να χωριστούν σε κατηγορίες ανάλογα με τη

μεταβολή της κατάστασης των διεργασιών οι οποίες τις καλούν. Ακόμα μπορούν να

κατηγοριοποιηθούν ανάλογα με το είδος των παραμέτρων που δέχονται. Γενικά, οι

συναρτήσεις του MPI μπορούν να χωριστούν στις εξής κατηγορίες:

Τοπικές (local). Είναι οι συναρτήσεις οι οποίες δεν απαιτούν επικοινωνία με άλλες

διεργασίες. Δηλαδή οι τυχόν αλλαγές που επιφέρουν στην εκτέλεση του

προγράμματος μας, περιορίζονται μόνο στη διεργασία που τις καλεί. Τοπικές είναι οι

κλήσεις που δημιουργούν αντικείμενα προσπελάσιμα από την καλούσα διεργασία. Για

παράδειγμα, η δημιουργία ενός buffer για την αποθήκευση των μηνυμάτων, γίνεται με

μια τοπική κλήση.

Μη-τοπικές (non-local). Είναι οι συναρτήσεις οι οποίες απαιτούν την κλήση

συναρτήσεων και σε άλλες διεργασίες. Πολλές συναρτήσεις επικοινωνίας είναι μη-

τοπικές. Για παράδειγμα, η μετάδοση μηνυμάτων απαιτεί κλήση συνάρτησης και στον

αποστολέα και στον παραλήπτη.

Αναστέλλουσες (blocking). Είναι οι συναρτήσεις οι οποίες αναστέλλουν την εκτέλεση

της διεργασίας που τις καλεί.

Μη αναστέλλουσες (non-blocking). Όπως φαίνεται και από το όνομα, είναι οι

συναρτήσεις οι οποίες επιτρέπουν στην καλούσα διεργασία να συνεχίσει την εκτέλεσή

της.

Συλλογικές (collective). Όταν η κλήση αφορά όλες τις διεργασίες μιας ομάδας. Για

παράδειγμα, αποστολή του ίδιου μηνύματος σε όλες τις διεργασίες μιας ομάδας

(Broadcast).

2.14 Παράμετροι κλήσης

Όσον αφορά τις παραμέτρους κλήσης, οι συναρτήσεις χωρίζονται σε τρεις κατηγορίες:

1. Συναρτήσεις που χρησιμοποιούν αλλά δεν μεταβάλλουν την τιμή των παραμέτρων.

Δηλαδή, οι παράμετροι είναι μόνο για διάβασμα (Read only). Σ’ αυτές, οι παράμετροι

περνιούνται με τιμή (by value).

25

Page 26: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

2. Συναρτήσεις που χρησιμοποιούν και μπορούν να μεταβάλλουν τις παραμέτρους τους

(Read-Write). Σ’ αυτές, οι παράμετροι περνιούνται με αναφορά (by reference) με τη

χρήση του μοναδιαίου τελεστή & της C.

3. Συναρτήσεις που μεταβάλλουν την τιμή κάποιων παραμέτρων τους για να επιστρέψουν

αποτέλεσμα. Στις συναρτήσεις αυτές οι παράμετροι δεν παρέχουν καμία πληροφορία

εισόδου. Απλώς, χρησιμοποιούνται για να επιστραφεί το αποτέλεσμα της κλήσης (Write-

only). Και σ’ αυτές, οι παράμετροι περνιούνται με αναφορά.

Οι περισσότερες συναρτήσεις του MPI επιστρέφουν αποτέλεσμα μέσω μίας

παραμέτρου. Το όνομα της συνάρτησης επιστρέφει ένα κωδικό λάθους ή την τιμή

MPI_SUCCESS που δείχνει ότι η κλήση ολοκληρώθηκε με επιτυχία.

26

Page 27: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

3. Επικοινωνία Κόμβου με Κόμβο

3.1 Γενικά

Ο βασικός μηχανισμός επικοινωνίας του MPI είναι η μετάδοση δεδομένων

(μηνυμάτων) μεταξύ δύο διεργασιών (η μία στέλνει και η άλλη λαμβάνει). Το MPI παρέχει

ένα σύνολο συναρτήσεων για τη μετάδοση και λήψη ενός συγκεκριμένου τύπου δεδομένων.

Σε μια επικοινωνία μπορούν να εμπλέκονται δύο ή και περισσότερες διεργασίες. Όταν

εμπλέκονται μόνο δύο διεργασίες, λέμε ότι έχουμε μία επικοινωνία μεταξύ δύο σημείων ή

επικοινωνία κόμβου με κόμβο (Point-to-Point Communication). Όταν εμπλέκονται

περισσότερες από δύο διεργασίες, λέμε ότι έχουμε μια συλλογική επικοινωνία (Collective

Communication). Το MPI παρέχει διαφορετικά είδη συναρτήσεων για κάθε είδος

επικοινωνίας.

3.2 Μηνύματα

Κάθε MPI μήνυμα αποτελείται από δύο μέρη: από το φάκελο (envelope) και από το

κυρίως μήνυμα (message body). Ο φάκελος ενός MPI μηνύματος περιλαμβάνει την εξής

πληροφορία:

1. Το όνομα της διεργασίας-αφετηρίας από την οποία στέλνεται το μήνυμα (source).

2. Το όνομα της διεργασίας-προορισμού στην οποία κατευθύνεται το μήνυμα

(destination).

3. Τον communicator στον οποίον ανήκουν οι διεργασίες αφετηρίας και προορισμού

(communicator).

4. Μία ετικέτα (tag) η οποία μπορεί να χρησιμοποιηθεί προκειμένου η διεργασία-

παραλήπτης να ξεχωρίσει το είδος του μηνύματος. Έτσι, αν ληφθούν δύο

μηνύματα από την ίδια διεργασία, με τη χρήση της ετικέτας ο παραλήπτης μπορεί

να τα διακρίνει.

Το κυρίως μήνυμα αποτελείται από την εξής πληροφορία:

1. Τη διεύθυνση αρχής της ενδιάμεσης μνήμης (buffer) όπου μπορούν να βρεθούν τα

προς αποστολή δεδομένα στην περίπτωση μιας λειτουργίας αποστολής μηνύματος

27

Page 28: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

(send operation), ή πρόκειται να αποθηκευθούν τα προς παραλαβή δεδομένα στην

περίπτωση λειτουργίας παραλαβής μηνύματος (receive operation).

2. Τον τύπο των δεδομένων του μηνύματος (datatype). Στις απλές περιπτώσεις,

αυτός μπορεί να είναι ένας βασικός τύπος δεδομένων της C ή της Fortran (int ή

INTEGER, float ή REAL, κ.λπ.). Σε άλλες περιπτώσεις, μπορεί να είναι ένας

τύπος δεδομένων που ορίσθηκε από το χρήστη με τη χρήση των βασικών τύπων.

`Ενας τέτοιος τύπος δεδομένων στην περίπτωση για παράδειγμα, της C μπορεί να

είναι ανάλογος με μία δομή (structure) της γλώσσας και να περιέχει δεδομένα τα

οποία δεν βρίσκονται αναγκαστικά, σε διαδοχικές θέσεις μνήμης. Η δυνατότητα

χρήσης τύπων δεδομένων που ορίζονται από το χρήστη, στην πραγματικότητα,

μας εξασφαλίζει ότι μπορούμε να προσδιορίσουμε απόλυτα οποιοδήποτε

περιεχόμενο στο μήνυμα.

3. Τον αριθμό των αντικειμένων (count) του τύπου δεδομένων ο οποίος

προσδιορίσθηκε προηγουμένως, που πρόκειται να αποσταλούν/παραλαβούν.

3.3 Επικοινωνία κόμβου με κόμβο (point to point)

Η επικοινωνία κόμβου με κόμβο (point to point) είναι η απλούστερη μορφή

επικοινωνίας διεργασιών που διαθέτει το MPI. Όπως έχει ήδη αναφερθεί, στην επικοινωνία

κόμβου με κόμβο εμπλέκονται μόνο δύο διεργασίες που επικοινωνούν. Η διεργασία-

αποστολέας στέλνει ένα μήνυμα στη διεργασία-παραλήπτη. Για να σταλεί ένα μήνυμα, η

διεργασία-αποστολέας (source) καλεί κάποια συνάρτηση αποστολής, παραμέτροι της οποίας

είναι η τάξη (rank) της διεργασίας-παραλήπτη (destination) καθώς και ο communicator στον

οποίο ανήκει η διεργασία-παραλήπτης.

Η διεργασία-παραλήπτης λαμβάνει το μήνυμα καλώντας κάποια συνάρτηση

παραλαβής ή λήψης του μηνύματος. Αυτό σημαίνει ότι η αποστολή ενός μηνύματος δεν

υποχρεώνει τον παραλήπτη να το λάβει. Πάντα, η λήψη γίνεται με πρωτοβουλία του

παραλήπτη. Έτσι, αν για κάποιο λόγο, ο παραλήπτης δεν μπορεί να λάβει ένα μήνυμα μπορεί

απλά, να το αγνοήσει.

Επειδή η μετάδοση μηνυμάτων είναι μια σχετικά αργή διαδικασία σε σχέση με την

εκτέλεση του υπόλοιπου προγράμματος, το MPI παρέχει δύο βασικές μορφές κόμβου με

κόμβο επικοινωνίας. Την αναστέλλουσα (blocking) και την μη αναστέλλουσα (non-

blocking) επικοινωνία. Στην αναστέλλουσα επικοινωνία, η συνάρτηση αποστολής και η

συνάρτηση παραλαβής μηνυμάτων αναστέλλουν την εκτέλεση της διεργασίας που τις καλεί,

έως ότου ολοκληρωθεί η διαδικασία αποστολής ή παραλαβής, αντίστοιχα. Στη μη

αναστέλλουσα επικοινωνία η συνάρτηση αποστολής και η συνάρτηση παραλαβής

μηνυμάτων επιστρέφουν αμέσως, χωρίς να αναστέλλουν την εκτέλεση της διεργασίας που

τις καλεί, άσχετα με το αν έχει ολοκληρωθεί η διαδικασία αποστολής ή παραλαβής,

αντίστοιχα. Η έννοια της ολοκλήρωσης της διαδικασίας αποστολής ή παραλαβής

προσδιορίζεται ανάλογα με την κατάσταση επικοινωνίας (communication mode). Το MPI

υποστηρίζει τέσσερις καταστάσεις επικοινωνίας οι οποίες ορίζουν κριτήρια προκειμένου να

προσδιορισθεί πότε μια διαδικασία επικοινωνίας (δηλαδή, μια διαδικασία αποστολής ή μια

28

Page 29: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

διαδικασία παραλαβής) έχει ολοκληρωθεί. Οι καταστάσεις επικοινωνίας θα συζητηθούν στο

επόμενο κεφάλαιο.

3.3.1 Κανόνες που διέπουν την επικοινωνία κόμβου με κόμβο

Οι υλοποιήσεις του MPI βασίζονται στους εξής κανόνες για τη σωστή διεξαγωγή της

επικοινωνίας κόμβου με κόμβο.

Διατήρηση της σειράς των μηνυμάτων.

Έστω ότι έχουμε δυο MPI διεργασίες. Η διεργασία Α στέλνει δυο μηνύματα στη διεργασία

Β η οποία βρίσκεται στον ίδιο communicator με την Α. Τότε τα δυο μηνύματα θα φτάσουν

εγγυημένα, με τη σειρά με την οποία εστάλησαν.

Ο κανόνας ισχύει και όταν αποστέλλονται περισσότερα από δυο μηνύματα. Πάντα, θα

λαμβάνονται με τη σειρά αποστολής τους.

Αν έχουμε ένα send και ένα αντίστοιχο receive, τότε τουλάχιστον το ένα από τα δυο κάποτε

ολοκληρώνεται.

Δηλαδή, αν έχουμε ένα send και ένα receive δεν είναι δυνατόν η ολοκλήρωση και των δύο

να αναβάλλεται συνεχώς, διότι τότε, η εφαρμογή οδηγείται σε αδιέξοδο (deadlock) Αν

κάποια από τις δύο κλήσεις αναγκαστεί να περιμένει, συνήθως συμβαίνει ένα από τα

παρακάτω.

Το send λαμβάνεται από μια τρίτη διεργασία η οποία έτυχε να εκτελεί ένα αντίστοιχο

receive. Σ’ αυτή την περίπτωση, το send ολοκληρώνεται αλλά το receive της δεύτερης

διεργασίας όχι.

Μια τρίτη διεργασία στέλνει ένα μήνυμα το οποίο λαμβάνεται από τη δεύτερη

διεργασία. Σ’ αυτή την περίπτωση, το receive ολοκληρώνεται αλλά το send της πρώτης

διεργασίας όχι.

3.4 Αναστέλλουσα (Blocking) επικοινωνία

Στην αναστέλλουσα επικοινωνία, η συνάρτηση αποστολής και η συνάρτηση

παραλαβής μηνυμάτων αναστέλλουν την εκτέλεση της διεργασίας που τις καλεί, έως ότου

ολοκληρωθεί η διαδικασία αποστολής ή παραλαβής, αντίστοιχα. Υπάρχουν δύο βασικές

συναρτήσεις οι οποίες υλοποιούν την αναστέλλουσα επικοινωνία. Η MPI_Send και η

MPI_Recv οι οποίες χρησιμοποιούνται για αποστολή και λήψη μηνυμάτων αντίστοιχα. Και

οι δύο συναρτήσεις αναστέλλουν τη διαδικασία που τις καλεί, αφού καμμιά τους δεν

επιστρέφει έως ότου η αντίστοιχη διαδικασία επικοινωνίας (αποστολής ή λήψης)

ολοκληρωθεί. Η έννοια της ολοκλήρωσης των διαδικασιών επικοινωνίας των κλήσεων των

MPI_Send και MPI_Recv συζητείται στην επόμενη παράγραφο.

29

Page 30: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Η συνάρτηση MPI_Send

Η συνάρτηση αυτή στέλνει ένα μήνυμα αναστέλλοντας την εκτέλεση της διεργασίας-

αποστολέα. Η σύνταξή της είναι η εξής:

int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm);

Όπου:

buf Η διεύθυνση αρχής της ενδιάμεσης μνήμης όπου βρίσκεται το προς

αποστολή μήνυμα.

count Αριθμός στοιχείων προς αποστολή (μέγεθος της ενδιάμεσης μνήμης).

datatype Τι είδους δεδομένα αποστέλλονται (MPI_INT, MPI_CHAR, κ.λπ.)

dest Η τάξη της διεργασίας-παραλήπτη.

tag Είναι ένα είδος σχολίου που καθορίζει τον τύπο του μηνύματος.

comm Ο communicator στον οποίο ανήκει η διεργασία-παραλήπτης.

Η ενδιάμεση μνήμη αποτελείται από count διαδοχικά στη μνήμη στοιχεία τύπου

datatype, το πρώτο από τα οποία βρίσκεται στη διεύθυνση buf. Προφανώς, το datatype

δείχνει και το μέγεθος κάθε στοιχείου προς αποστολή. Το μέγεθος της ενδιάμεσης μνήμης

δείχνει τον αριθμό των στοιχείων που περιέχονται σ’ αυτήν, και όχι το μέγεθός της σε bytes.

Έτσι, είναι δυνατή η μεταφορά των προγραμμάτων MPI σε αρχιτεκτονικές που δεν είναι

οργανωμένες με βάση το byte.

Η συνάρτηση MPI_Recv

Η συνάρτηση αυτή λαμβάνει ένα μήνυμα αναστέλλοντας την εκτέλεση της

διεργασίας-παραλήπτη. Η σύνταξή της είναι η εξής:

int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag,

MPI_Comm comm, MPI_Status *status)

Οι περισσότερες παράμετροι είναι παρόμοιες με τις αντίστοιχες της MPI_Send. Πιο

αναλυτικά:

buf Η διεύθυνση αρχής της ενδιάμεσης μνήμης στην οποίο θα αποθηκευθεί

το μήνυμα.

count Ο μέγιστος αριθμός στοιχείων προς λήψη.

datatype Τι είδους δεδομένα θα ληφθούν σε κάθε στοιχείο.

source H τάξη της διεργασίας-αποστολέα.

tag Ο τύπος του μηνύματος.

comm Ο communicator στον οποίο ανήκει η διεργασία αποστολέας.

status Επιστρέφει πληροφορίες σχετικά με το αποτέλεσμα του receive.

30

Page 31: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Η ενδιάμεση μνήμη λήψης πρέπει να είναι μεγαλύτερη ή τουλάχιστον, ίση με το

μέγεθος του μηνύματος. Διαφορετικά, θα προκύψει λάθος, ο κωδικός του οποίου,

επιστρέφεται στο status. Αν ένα μήνυμα είναι μικρότερο από το μέγεθος της ενδιάμεσης

μνήμης δεν υπάρχει πρόβλημα. Το μήνυμα αποθηκεύεται στις πρώτες θέσεις της ενδιάμεσης

μνήμης και οι υπόλοιπες παραμένουν αμετάβλητες. Η μεταβλητή count δηλώνει το μέγιστο

αριθμό στοιχείων που θα ληφθούν. Συνήθως, ως τιμή της μεταβλητής count δίνεται το

μέγεθος της ενδιάμεσης μνήμης το οποίο επιστρέφεται από τη συνάρτηση sizeof της C.

Πολλές φορές όμως, ο αριθμός των στοιχείων που λαμβάνονται είναι μικρότερος. Η

ενδιάμεση μνήμη (buffer) δηλώνεται μεγαλύτερη ή ίση με το μεγαλύτερο αναμενόμενο

μήνυμα για ασφάλεια.

Η μεταβλητή status είναι μια δομή (struct) τύπου MPI_Status τα πεδία του οποίου

περιγράφονται παρακάτω. Η status επιστρέφει πληροφορίες σχετικά με το μέγεθος του

ληφθέντος μηνύματος. Ωστόσο, το μέγεθος δεν είναι άμεσα διαθέσιμο ως ένα πεδίο της

δομής. Για να βρούμε το μέγεθος ενός ληφθέντος μηνύματος, καλούμε τη συνάρτηση

MPI_Get_count η σύνταξη της οποίας είναι η εξής.

int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count)

Όπου:

status Μεταβλητή που έχει περαστεί στην MPI_Recv.

datatype Το είδος των δεδομένων που έχουν ληφθεί από την MPI_Recv.

count Επιστρέφει το μέγεθος του μηνύματος.

Η MPI_Get_count δέχεται σαν είσοδο τη μεταβλητή status, τα πεδία της οποίας έχουν

πάρει τιμή από την επιστροφή της MPI_Recv. Η μεταβλητή count περιέχει τον αριθμό των

δεδομένων τύπου datatype που ελήφθησαν. Επομένως, ξέροντας το μέγεθος του

συγκεκριμένου datatype σε bytes και τον αριθμό των στοιχείων που ελήφθησαν, μπορούμε

να υπολογίσουμε το μέγεθος του μηνύματος.

Ο τύπος δεδομένων (datatype) πρέπει να είναι ο ίδιος με τον τύπο δεδομένων που

ορίστηκε στην MPI_Recv και αποτελεί ευθύνη του προγραμματιστή να εγγυηθεί ότι αυτό θα

συμβεί. Εάν οι τύποι δεδομένων που προσδιορίζονται στις MPI_Send και MPI_Recv, δεν

είναι συμβατοί, τότε τα αποτελέσματα είναι απροσδιόριστα.

3.4.1 Η έννοια της ολοκλήρωσης των MPI_Send και MPI_Recv

Η έννοια της ολοκλήρωσης μιας κλήσης MPI_Recv είναι απλή και προφανής. Για να

ολοκληρωθεί η διαδικασία λήψης που αντιστοιχεί σε μια κλήση της MPI_Recv, θα πρέπει το

μήνυμα να φθάσει στη διεργασία που καλεί την MPI_Recv, και τα δεδομένα του μηνύματος

να περιέχονται στις μεταβλητές της κλήσης της MPI_Recv.

Στην περίπτωση της κλήσης μιας MPI_Send η έννοια της ολοκλήρωσης είναι απλή

αλλά όχι τόσο προφανής.`Οταν ένα μήνυμα αποστέλλεται με τη χρήση της MPI_Send , τότε

ένα από τα εξής δύο πράγματα μπορούν να συμβούν κατά τη διάρκεια της εκτέλεσης:

31

Page 32: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

1. Το μήνυμα μπορεί να αποθηκευθεί σε μία εσωτερική ενδιάμεση μνήμη του MPI

(MPI internal buffer) και να μεταφερθεί αργότερα στον προορισμό του.

2. Το μήνυμα μπορεί να παραμείνει όπου βρίσκεται, δηλαδή στις μεταβλητές του

προγράμματος, έως ότου η διεργασία-παραλήπτης είναι έτοιμη να το παραλάβει.

Εκείνη τη στιγμή, το μήνυμα μεταφέρεται στον προορισμό του.

Στην πρώτη περίπτωση η διαδικασία αποστολής ολοκληρώνεται και η MPI_Send

επιστρέφει αμέσως μετά την αντιγραφή του μηνύματος στην εσωτερική ενδιάμεση μνήμη

του MPI, ακόμη και αν το μήνυμα δεν έχει φθάσει ακόμη στον προορισμό του ή ακόμη και

αν δεν έχει ξεκινήσει καμμία μη τοπική διαδικασία για την αποστολή του. Το μήνυμα

παραμένει στην ενδιάμεση μνήμη του MPI έως ότου η διεργασία-παραλήπτης αρχίσει να το

παραλαμβάνει. Αν το μέγεθος του μηνύματος είναι μεγαλύτερο από το διαθέσιμο χώρο στην

ενδιάμεση μνήμη του MPI, τότε η εκτέλεση της διεργασίας-αποστολέα αναστέλλεται έως

ότου η διεργασία-παραλήπτης αρχίσει να παραλαμβάνει το μήνυμα, ή εναλλακτικά, έως

ότου περισσότερη ενδιάμεση μνήμη είναι διαθέσιμη.

Στην δεύτερη περίπτωση η διαδικασία αποστολής ολοκληρώνεται και η MPI_Send

επιστρέφει μόνον όταν συγχρονιστούν οι διεργασίες παραλήπτη και αποστολέα και αρχίσει η

αποστολή του μηνύματος. `Εως τότε, αναστέλλεται η εκτέλεση της διεργασίας-αποστολέα.

3.4.2 Επιλογή ενός μηνύματος

Για να ληφθεί ένα μήνυμα, πρέπει ο φάκελος του να περιέχει τις ίδιες τιμές για τις

μεταβλητές source, tag και comm με αυτές που καθορίζονται από τη διεργασία-παραλήπτη

στη συνάρτηση που λαμβάνει το μήνυμα. Στη θέση της source ή/και της tag μπορούμε να

χρησιμοποιήσουμε τις σταθερές MPI_ANY_TAG, MPI_ANY_SOURCE. Με τη χρήση της

ετικέτας MPI_ANY_TAG επιτρέπεται η λήψη μηνυμάτων με οποιοδήποτε tag. Η σταθερά

MPI_ANY_SOURCE επιτρέπει τη λήψη μηνυμάτων από οποιοδήποτε αποστολέα. Όμως,

δεν μπορούμε να χρησιμοποιήσουμε μία σταθερά για την comm. Ο communicator πρέπει

πάντα να ορίζεται με σαφήνεια.

Αν έχει χρησιμοποιηθεί κάποια από τις σταθερές MPI_ANY_SOURCE ή η

MPI_ANY_TAG και ο παραλήπτης θέλει να βρει τον αποστολέα ή την ετικέτα ενός

μηνύματος, διαβάζουμε τα πεδία της δομής status αφού επιστρέψει η MPI_Recv. Στα πεδία

MPI_SOURCE και MPI_TAG περιέχονται οι αντίστοιχες πληροφορίες που ζητάμε. Αυτές οι

σταθερές μπορούν να απλοποιήσουν τον αλγόριθμο του προγράμματος μας, αφού το

αναμενόμενο μήνυμα μπορεί να ληφθεί από οποιαδήποτε διεργασία μπορεί να το στείλει, και

όχι απαραίτητα, από μια συγκεκριμένη. Αυτή η δυνατότητα είναι χρήσιμη στη συλλογική

επικοινωνία. Όμως, στην κόμβου με κόμβο επικοινωνία, καλό θα είναι να ορίζουμε

επακριβώς το tag καθώς και από ποια διεργασία θα λάβουμε το μήνυμα. Διαφορετικά,

μπορεί να λάβουμε μήνυμα από λάθος αποστολέα ή μήνυμα με διαφορετικό tag από το

αναμενόμενο.

Παρατηρούμε ότι υπάρχει μία διαφοροποίηση μεταξύ μιας send και μιας receive

λειτουργίας. Μία receive μπορεί να δεχθεί μηνύματα από έναν τυχαίο αποστολέα, αλλά μία

send στέλνει πάντα σε ένα συγκεκριμένο παραλήπτη. Ακόμα, είναι δυνατόν μια διεργασία να

32

Page 33: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

στείλει ένα μήνυμα στον εαυτό της. Σ’ αυτή την περίπτωση, το MPI αποθηκεύει το μήνυμα

σε μια ενδιάμεση μνήμη αφού ολοκληρωθεί η send και πριν αρχίσει η receive. Όμως, τα

προγράμματα στα οποία η ίδια διεργασία είναι αποστολέας και παραλήπτης δεν είναι

μεταφέρσιμα. Εκτός από αυτό, είναι πολύ πιθανό να προκύψει αδιέξοδο (deadlock).

3.4.3 Σχόλια στο blocking receive

Μια λειτουργία receive πρέπει πάντα να ορίζει με σαφήνεια, τον μέγιστο αριθμό

στοιχείων που πρόκειται να ληφθούν, καθώς και τον τύπο δεδομένων κάθε στοιχείου. Σε

περίπτωση που η ίδια διεργασία αναμένει πολλά μηνύματα διαφορετικού μήκους ή τύπου

δεδομένων το καθένα, γίνεται χρήση των tags για να μπορέσει να τα ξεχωρίσει. Για κάθε

αναμενόμενο μήνυμα, ξεκινάει μια λειτουργία receive.

Πολλές φορές, η διεργασία περιμένει να λάβει μόνον ένα μήνυμα αλλά ο τύπος ή το

μέγεθος του μηνύματος, δεν είναι γνωστά εκ των προτέρων. Αυτό δημιουργεί πρόβλημα στη

διεργασία παραλήπτη διότι, δεν ξέρει πώς να χειριστεί το μήνυμα (για παράδειγμα, δεν ξέρει

τι μέγεθος buffer λήψης να ορίσει). Αν τα είδη των αναμενόμενων μηνυμάτων είναι σχετικά

λίγα, μπορούμε να χρησιμοποιήσουμε διαφορετική ετικέτα (tag) για κάθε είδος μηνύματος.

Αν όμως, έχουμε πολλά είδη μηνυμάτων, στη γενική περίπτωση, δεν είναι πάντα δυνατόν να

χρησιμοποιήσουμε διαφορετικό tag για κάθε είδος. Τότε, ο αποστολέας μπορεί να στείλει

ένα μήνυμα που περιέχει την περιγραφή των δεδομένων που ακολουθούν. Τα δεδομένα

αποστέλλονται με ένα δεύτερο μήνυμα. Το MPI φροντίζει έτσι ώστε τα δυο μηνύματα να

φτάσουν στον παραλήπτη με τη σειρά που εστάλησαν. Έτσι ο παραλήπτης λαμβάνει πρώτα

την περιγραφή των δεδομένων. Μετά, αφού γνωρίζει αυτή την πληροφορία, ξέρει πώς να

λάβει και τα δεδομένα.

Μια άλλη τεχνική για να γνωστοποιηθεί ο τύπος ενός μηνύματος, είναι η χρήση των

συναρτήσεων packing και unpacking. Οι συναρτήσεις αυτές δίνουν τη δυνατότητα στον

αποστολέα να συμπεριλάβει μια περιγραφή των δεδομένων ακολουθούμενη από τα

δεδομένα του ίδιου του μηνύματος. Και τα δύο περιέχονται στο ίδιο το μήνυμα. Ο

παραλήπτης λαμβάνει το συνεπτυγμένο μήνυμα με μια κλήση receive και το αποσυμπιέζει

(unpacking). Οι συναρτήσεις packing και unpacking θα περιγραφούν αργότερα.

Το MPI παρέχει μια ποικιλία μηχανισμών για το ταίριασμα των μηνυμάτων με τις

λειτουργίες receive. Συνήθως, αν ο φάκελος του μηνύματος ταιριάζει με την τάξη του

αποστολέα ή την ετικέτα που έχει περαστεί στη receive, η επικοινωνία ολοκληρώνεται με

επιτυχία. Αν όμως έχουν χρησιμοποιηθεί οι σταθερές MPI_ANY_SOURCE ή

MPI_ANY_TAG, το αποτέλεσμα δεν είναι πάντα το αναμενόμενο. Όπως έχει ήδη

αναφερθεί, καλό είναι να αποφεύγονται οι σταθερές. Στενεύοντας τα περιθώρια επιλογής

ενός μηνύματος προς λήψη, ο κώδικας γίνεται ασφαλέστερος και μειώνονται οι πιθανότητες

για λάθη που οδηγούν την εφαρμογή σε απρόβλεπτη συμπεριφορά. Πολλές φορές, εκτός από

την ασφάλεια επιτυγχάνουμε και καλύτερες επιδόσεις αφού το σύστημα επιλέγει

συγκεκριμένες διεργασίες για την επικοινωνία.

Μία διεργασία δεν είναι υποχρεωμένη να λάβει ένα οποιοδήποτε μήνυμα το οποίο

απευθύνεται σ’ αυτή. Μπορεί πρώτα, να γίνει έλεγχος αν το μήνυμα είναι αυτό που θέλει να

33

Page 34: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

λάβει η διεργασία και μετά να αποφασίσει αν θα το λάβει ή όχι. Αυτή η δυνατότητα

παρέχεται από την MPI_Probe.

3.4.4 Η συνάρτηση MPI_Probe

Η συνάρτηση MPI_Probe εξετάζει αν υπάρχει μήνυμα για λήψη αλλά δεν το

λαμβάνει. Με την MPI_Probe ο παραλήπτης γνωρίζει πως να ενεργήσει για να λάβει το

μήνυμα. Για παράδειγμα, μπορεί να δημιουργήσει buffer με μέγεθος όσο χρειάζεται για να

χωρέσει το μήνυμα. Σε προγράμματα που το μέγεθος του buffer είναι προκαθορισμένο στο

μέγιστο δυνατό μέγεθος ενός μηνύματος, με την MPI_Probe αποφεύγεται και η σπατάλη

μνήμης. Η σύνταξή της ακολουθεί.

int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status)

Όπου:

source Η τάξη του αποστολέα.

tag Η ετικέτα που θα πρέπει να έχει το μήνυμα.

comm Ο communicator που ανήκει ο αποστολέας.

status Εδώ επιστρέφεται το αποτέλεσμα της κλήσης.

Η MPI_Probe αναστέλλει την εκτέλεση της διεργασίας που την καλεί και επιστρέφει

μόνο όταν βρεθεί ένα μήνυμα που έχει την ετικέτα που ορίσαμε. Αφού επιστρέψει η

MPI_Probe, η διεργασία-παραλήπτης μπορεί να ελέγξει την ετικέτα, να διαπιστώσει το

είδος του μηνύματος, και μετά να το λάβει καλώντας την κατάλληλη συνάρτηση. Η

συνάρτηση μοιάζει αρκετά με την MPI_Recv, όπως φαίνεται και από τις παραμέτρους

κλήσης της, αλλά, δεν λαμβάνει μήνυμα. Απλώς, διαπιστώνει την ύπαρξη ή όχι ενός

μηνύματος με τη συγκεκριμένη ετικέτα. Στην παράμετρο status επιστρέφονται το

πραγματικό source και tag του ληφθέντος μηνύματος. Η status είναι μια δομή (struct) τα

πεδία της οποίας περιγράφονται αργότερα.

Δεν είναι απαραίτητο να κληθεί η συνάρτηση λήψης αμέσως μόλις επιστρέψει η

MPI_Probe. Ακόμα, η MPI_Probe μπορεί να κληθεί πολλές φορές πριν τελικά

αποφασίσουμε να λάβουμε το μήνυμα με μια κλήση receive. Στη source και tag μπορούμε

να χρησιμοποιήσουμε τις σταθερές MPI_ANY_SOURCE ή MPI_ANY_TAG όπως και σε

μία receive κλήση.

3.4.5 Οι συναρτήσεις MPI_Pack και MPI_Unpack

Το MPI μας παρέχει τη δυνατότητα αποστολής και λήψης συνεπτυγμένων

(συμπιεσμένων) μηνυμάτων. Με τις συναρτήσεις αυτές μπορούμε να στείλουμε μία

περιγραφή του μηνύματος, δηλαδή, τον τύπο των δεδομένων, ακολουθούμενη από τα

δεδομένα του ίδιου του μηνύματος. Ο τύπος δεδομένων καθώς και το ίδιο το μήνυμα

βρίσκονται στην ίδια ενδιάμεση μνήμη κατά την αποστολή τους και αποθηκεύονται στην

34

Page 35: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

ίδια ενδιάμεση μνήμη μετά τη λήψη τους. Ετσι, μας δίνεται η δυνατότητα να λάβουμε ένα

μήνυμα χωρισμένο σε πολλά κομμάτια. Κάθε λειτουργία receive που εκτελείται αργότερα,

μπορεί να εξαρτάται από το περιεχόμενο ενός μηνύματος που ελήφθη νωρίτερα. Η κλήση

της MPI_Pack είναι η εξής:

int MPI_Pack(void *inbuf, int incount, MPI_Datatype datatype, void *outbuf,

int outsize, int *position, MPI_Comm comm)

Όπου:

inbuf Ενδιάμεση μνήμη που περιέχει το αρχικό μήνυμα.

incount Αριθμός στοιχείων που περιέχονται στην ενδιάμεση μνήμη

(δηλαδή το μέγεθος του αρχικού μηνύματος).

datatype Ο τύπος δεδομένων που περιέχονται στην ενδιάμεση μνήμη.

outbuf Ενδιάμεση μνήμη που θα αποθηκεύσει το τελικό μήνυμα.

outsize Μέγεθος της ενδιάμεσης μνήμης του τελικού μηνύματος, σε bytes.

position Η τρέχουσα θέση στην ενδιάμεση μνήμη, σε bytes.

comm Ο communicator για το συμπιεσμένο μήνυμα.

Η συνάρτηση συμπιέζει το μήνυμα που καθορίζεται από τις τιμές των inbuf, incount

και comm στην ενδιάμεση μνήμη που καθορίζεται από τις outbuf και outsize. Η inbuf μπορεί

να έχει οποιοδήποτε τύπο δεδομένων επιτρέπει η MPI_Send. Η outbuf είναι μια ενδιάμεση

μνήμη που καταλαμβάνει συνεχόμενες διευθύνσεις και έχει μέγεθος outsize bytes,

αρχίζοντας από τη διεύθυνση outbuf. Η position δείχνει στην πρώτη θέση του outbuf από

την οποία θα αρχίσει η αποθήκευση των συμπιεσμένων δεδομένων. Αυτή η παράμετρος

αυξάνει όσο είναι το μέγεθος του συμπιεσμένου μηνύματος έτσι ώστε, να μπορεί να

χρησιμοποιηθεί και σε επόμενες κλήσεις της MPI_Pack. Το συμπιεσμένο μήνυμα

μεταδίδεται με μια συνηθισμένη συνάρτηση αποστολής μηνύματος (π.χ., με την MPI_Send).

Η συνάρτηση MPI_Unpack, όπως δείχνει και το όνομά της, κάνει την αντίστροφη

εργασία από την MPI_Pack. Δηλαδή, αποσυμπιέζει ένα μήνυμα έτσι ώστε η διεργασία-

παραλήπτης να μπορεί να χρησιμοποιήσει τα δεδομένα που περιέχονται σ’ αυτό. Η κλήση

της είναι η παρακάτω:

int MPI_Unpack(void *inbuf, int insize, int *position, void *outbuf,

int outcount, MPI_Datatype datatype, MPI_Comm comm)

Όπου:

inbuf Η διεύθυνση αρχής της ενδιάμεσης μνήμης που περιέχει το

συμπιεσμένο μήνυμα.

insize Το μέγεθος της ενδιάμεσης μνήμης σε bytes.

position Η τρέχουσα θέση στην ενδιάμεση μνήμη, σε bytes.

outbuf Η ενδιάμεση μνήμη που θα αποθηκεύσει το αποσυμπιεσμένο μήνυμα.

outcount Αριθμός στοιχείων που θα αποσυμπιεστούν.

datatype Ο τύπος δεδομένων της outbuf.

comm Ο communicator για το συμπιεσμένο μήνυμα.

35

Page 36: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Η συνάρτηση αποσυμπιέζει το μήνυμα που βρίσκεται στην inbuf, στην ενδιάμεση

μνήμη που καθορίζεται από τις outbuf, outcount και datatype. Η outbuf μπορεί να είναι

οποιουδήποτε τύπου επιτρέπει η MPI_Recv. Η inbuf είναι μια ενδιάμεση μνήμη που

καταλαμβάνει συνεχόμενες διευθύνσεις και έχει μέγεθος insize bytes, αρχίζοντας από τη

διεύθυνση inbuf.

3.5 Παραδείγματα κώδικα

Παρακάτω δίνονται μερικά απλά MPI προγράμματα σε C προκειμένου να δειχθεί η

χρήση ορισμένων από τις βασικές συναρτήσεις που έχουν αναφερθεί ως τώρα.

3.5.1 Blocking Send και Receive

Δίνεται ένα πολύ απλό MPI πρόγραμμα για να δειχθεί η χρήση των συναρτήσεων

MPI_Send και MPI_Recv. Η εκτέλεση γίνεται από δύο διεργασίες. Η διεργασία 0 στέλνει

μία συμβολοσειρά (string) στη διεργασία 1.

#include «mpi.h»

main(int argc, char **argv)

{

char msg[20];

int process_rank, tag = 100;

MPI _Status status;

MPI_Init(&argc, &argv);

MPI_Comm_rank(MPI_COMM_WORLD, &process_rank);

if (process_rank == 0)

{

strcpy(msg, «Hello World»);

MPI_Send(msg, strlen(msg) + 1, MPI_CHAR, 1, tag,

MPI_COMM_WORLD);

}

else if (process_rank == 1)

{

MPI_Recv(msg, 20, MPI_CHAR, 0, tag, MPI_COMM_WORLD,

&status);

MPI_Finalize();

}

}

36

Page 37: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Ο κώδικας εκτελείται και στις δύο διεργασίες. Αρχικά, ορίζονται οι απαραίτητες

μεταβλητές και ενεργοποιείται το MPI με την MPI_Init. Η MPI_Comm_rank επιστρέφει την

τάξη της διεργασίας που την καλεί, στην παράμετρο process_rank. Αν η τάξη είναι 0 (ήτοι, ο

κώδικας εκτελείται από τη διεργασία 0) αποστέλλεται μήνυμα με την MPI_ Send. Το

μήνυμα περιέχεται στον πίνακα τύπου char msg και έχει μήκος όσο είναι ο αριθμός

χαρακτήρων του string «Hello World» συν ο null χαρακτήρας ο οποίος δείχνει το τέλος ενός

string στη C. Η σταθερά MPI_CHAR δείχνει ότι το είδος των δεδομένων που περιέχονται

στον πίνακα msg είναι char. Η τέταρτη παράμετρος είναι η τάξη (rank) της διεργασίας

παραλήπτη. Η tag είναι ο ακέραιος 100. Σ’ αυτό το παράδειγμα φυσικά, η tag δεν χρειάζεται

διότι στέλνουμε μόνο ένα μήνυμα οπότε δεν υπάρχει σύγχυση. Όμως, απαιτείται από την

MPI_Send. Τέλος, η σταθερά MPI_COMM_WORLD είναι όπως έχει ήδη αναφερθεί, ο εξ

ορισμού communicator στον οποίο ανήκουν όλες οι διεργασίες του προγράμματος.

Αν η τάξη είναι 1 (ήτοι, ο κώδικας εκτελείται από τη διεργασία 1), τότε γίνεται λήψη

μηνύματος με κλήση της συνάρτησης MPI_Recv. Οι παράμετροι κλήσης είναι ίδιες, με

αυτές της MPI_Send, εκτός από τη status που περιέχει πληροφορίες σχετικά με το μήνυμα

που ελήφθη. Η μεταβλητή status είναι μία δομή τύπου MPI_Status που περιέχει τα εξής

πεδία:

typedef struct

{

int MPI_SOURCE;

int MPI_TAG;

int MPI_ERROR;

} MPI_Status;

Όπως δείχνει και το όνομα τους, τα πεδία αυτά περιέχουν τον αποστολέα, την tag και

το κωδικό λάθους του ληφθέντος μηνύματος, αντίστοιχα.. Η δομή MPI_Status είναι η μόνη

δομή του MPI η οποία μπορεί να προσπελαστεί άμεσα από τον προγραμματιστή, ο οποίος

μπορεί αν θέλει, να προσθέσει δικά του πεδία στη δομή. Τέλος, απενεργοποιούμε το MPI

καλώντας την MPI_Finalize() η οποία αποδεσμεύει τη μνήμη και καταστρέφει τις δομές

δεδομένων που είχε δημιουργήσει η MPI_Init.

Το πρόγραμμα χρησιμοποιεί blocking send και receive. Η MPI_Send αναστέλλει την

εκτέλεση της διεργασίας που την καλεί μέχρι να μεταδοθούν τα περιεχόμενα του msg στη

διεργασία 1. Μετά την κλήση, η διεργασία 0 μπορεί με ασφάλεια, να μεταβάλλει τα

περιεχόμενα της ενδιάμεσης μνήμης msg αν θέλει. Το ίδιο συμβαίνει όταν η διεργασία 1

καλεί την MPI_Recv. Η εκτέλεσή της αναστέλλεται μέχρι το μήνυμα να αποθηκευθεί στη

δική της ενδιάμεση μνήμη msg. Όταν η μετάδοση ολοκληρωθεί, η ενδιάμεση μνήμη μπορεί

να ξαναχρησιμοποιηθεί, τόσο από τον αποστολέα όσο και από τον παραλήπτη για τη

μετάδοση ή τη λήψη ενός άλλου μηνύματος.

3.5.2 Αποστολή πολλών μηνυμάτων σε μια διεργασία

37

Page 38: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Ακολουθεί ένα λίγο πιο πολύπλοκο παράδειγμα. Αρχικά, το πρόγραμμα δημιουργεί

τον αριθμό διεργασιών που ορίζει ο χρήστης. Όλες αυτές οι διεργασίες στέλνουν ένα string

στη διεργασία με τάξη 0 η οποία απλώς το εμφανίζει. Η έξοδος του προγράμματος εξαρτάται

από τον αριθμό των επεξεργαστών που το εκτελούν.

#include <stdio.h>

#include «mpi.h»

main(int argc, char **argv)

{

int my_rank; /* Τάξη τρέχουσας διεργασίας */

int p; /* Αριθμός επεξεργαστών */

int source; /* Τάξη αποστολέα */

int dest; /* Τάξη παραλήπτη */

int tag = 50; /* Ετικέτα για τα μηνύματα */

char message[100]; /* Ενδιάμεση μνήμη για το μήνυμα */

MPI_Status status; /* Μεταβλητή που περιέχει την κατάσταση της receive */

MPI_Init(&argc, &argv);

MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

MPI_Comm_size(MPI_COMM_WORLD, &p);

if (my_rank != 0) /* Αποστολή στη διεργασία 0 */

{

sprintf(message, «Greetings from process %d!», my_rank);

dest = 0;

/* Χρησιμοποιούμε το strlen(message) +1 για να συμπεριλάβουμε το ‘\0’ */

MPI_Send(message, strlen(message) + 1, MPI_CHAR, dest, tag,

MPI_COMM_WORLD);

}

else /* my_rank == 0 Λήψη από τις υπόλοιπες διεργασίες*/

{

for (source = 1; source < p; source++)

{

MPI_Recv(message, 100, MPI_CHAR, source, tag,

MPI_COMM_WORLD, &status);

printf(«%s \n», message);

}

}

MPI_Finalize();

}

38

Page 39: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Αν το πρόγραμμα εκτελεστεί σε δύο επεξεργαστές εμφανίζει:

Greetings from process 1!

Αν εκτελεστεί σε τέσσερις επεξεργαστές εμφανίζει:

Greetings from process 1!

Greetings from process 2!

Greetings from process 3!

3.5.3 Ανταλλαγή μηνυμάτων μεταξύ δύο διεργασιών (Ping - pong)

Το παρακάτω πρόγραμμα στέλνει ένα μήνυμα σε μια διεργασία και περιμένει

απάντηση από αυτή.

#include "mpi.h"

#include <stdio.h>

int main(argc,argv)

int argc;

char *argv[ ];

{

int numtasks, rank, dest, source, rc, tag = 1;

char inmsg, outmsg = 'Sample message';

MPI_Status Stat;

MPI_Init(&argc,&argv);

MPI_Comm_size(MPI_COMM_WORLD, &numtasks);

MPI_Comm_rank(MPI_COMM_WORLD, &rank);

if (rank == 0)

{

dest = 1;

source = 1;

rc = MPI_Send(&outmsg, 14, MPI_CHAR, dest, tag,

MPI_COMM_WORLD);

rc = MPI_Recv(&inmsg, 14, MPI_CHAR, source, tag,

MPI_COMM_WORLD, &Stat);

}

else if (rank == 1)

{

dest = 0;

source = 0;

rc = MPI_Recv(&inmsg, 14, MPI_CHAR, source, tag,

39

Page 40: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

MPI_COMM_WORLD, &Stat);

rc = MPI_Send(&outmsg, 14, MPI_CHAR, dest, tag,

MPI_COMM_WORLD);

}

MPI_Finalize();

}

3.5.4 Μέγιστο μέγεθος ενδιάμεσης μνήμης

Αρχικά, το πρόγραμμα δεσμεύει μια ενδιάμεση μνήμη με μέγεθος 1ΚΒ και στέλνει το

μέγεθος της ενδιάμεσης μνήμης που δεσμεύτηκε από τη διεργασία 1 στην 0. Αφού γίνει

επιβεβαίωση ότι το μέγεθος πράγματι ελήφθη σωστά από τη 0, η διεργασία 1 διπλασιάζει το

μέγεθος της ενδιάμεσης μνήμης και στέλνει το καινούργιο μέγεθος στην 0. Αυτό συνεχίζεται

μέχρι ο buffer να γίνει 16MB ή να φτάσει το μέγιστο μέγεθος που επιτρέπει το λειτουργικό

σύστημα, οπότε και εμφανίζεται το σχετικό μήνυμα. Σκοπός του προγράμματος είναι να

βρούμε το μέγιστο επιτρεπτό μέγεθος ενδιάμεσης μνήμης που μπορούμε να δεσμεύσουμε σε

ένα πρόγραμμα MPI όταν αυτό εκτελείται σε ένα συγκεκριμένο περιβάλλον. Το μέγεθος της

μνήμης εξαρτάται και από το φόρτο του συστήματος τη στιγμή της εκτέλεσης. Δηλαδή,

ενδέχεται διαφορετικές εκτελέσεις του ίδιου προγράμματος στο ίδιο σύστημα να δώσουν

διαφορετικά αποτελέσματα.

#include "mpi.h"

#include <stdio.h>

int main(argc,argv)

int argc;

char *argv[ ];

{

int myid, numprocs;

int namelen;

char processor_name[MPI_MAX_PROCESSOR_NAME];

char *buf;

int bufsize, other, done, i;

double t1, t2, tbase;

MPI_Status status;

MPI_Init(&argc,&argv);

MPI_Comm_size(MPI_COMM_WORLD, &numprocs);

MPI_Comm_rank(MPI_COMM_WORLD, &myid);

MPI_Get_processor_name(processor_name, &namelen);

printf("Process %d on %s\n", myid, processor_name);

40

Page 41: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

bufsize = 1024;

other = (myid + 1) % 2;

done = 0;

while (!done && bufsize < 1024*1024*16) {

if ((buf = (char *) malloc (bufsize)) == NULL) {

printf("%d could not malloc %d bytes\n", myid, bufsize );

MPI_Finalize();

exit(-1);

}

printf("%d sending %d to %d\n", myid, bufsize, other );

if ((myid % 2) == 0) {

MPI_Send(MPI_BOTTOM 0, MPI_INT, other, 1, MPI_COMM_WORLD);

MPI_Recv(MPI_BOTTOM, 0, MPI_INT, other, 2, MPI_COMM_WORLD,

&status );

/* Compute a time to send when the receive is waiting */

t1 = MPI_Wtime();

MPI_Send(buf, bufsize, MPI_CHAR, other, 100, MPI_COMM_WORLD);

t2 = MPI_Wtime();

tbase = t2 - t1;

MPI_Recv(MPI_BOTTOM, 0, MPI_INT, other, 2, MPI_COMM_WORLD,

&status );

/* Compute a time when the receive is NOT waiting */

t1 = MPI_Wtime();

MPI_Send(buf, bufsize, MPI_CHAR, other, 100, MPI_COMM_WORLD);

t2 = MPI_Wtime();

if (t2 - t1 > 1.5 && t2 - t1 > 2.0 * tbase) {

printf( "MPI_Send blocks with buffers of size %d\n", bufsize );

done = 1;

}

}

else

{

MPI_Recv(MPI_BOTTOM, 0, MPI_INT, other,1,MPI_COMM_WORLD,

&status);

t1 = MPI_Wtime();

MPI_Send(MPI_BOTTOM, 0, MPI_INT, other, 2, MPI_COMM_WORLD);

MPI_Recv(buf,bufsize, MPI_CHAR, other, 100, MPI_COMM_WORLD,

&status);

MPI_Send(MPI_BOTTOM, 0, MPI_INT, other, 2, MPI_COMM_WORLD);

while (MPI_Wtime() - t1 < 2.0) ;

MPI_Recv(buf, bufsize, MPI_CHAR, other, 100, MPI_COMM_WORLD,

41

Page 42: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

&status );

}

printf("%d received %d from %d\n", myid, bufsize, other );

free( buf );

i = done;

MPI_Allreduce(&i, &done, 1, MPI_INT, MPI_SUM,

MPI_COMM_WORLD);

bufsize *= 2;

}

MPI_Finalize();

return 0;

}

Η έξοδος με 2 διεργασίες είναι:

Process 1 on Manolis.Athens.Greece

1 sending 1024 to 0

1 received 1024 from 0

1 sending 2048 to 0

1 received 2048 from 0

1 sending 4096 to 0

1 received 4096 from 0

1 sending 8192 to 0

1 received 8192 from 0

1 sending 16384 to 0

1 received 16384 from 0

1 sending 32768 to 0

1 received 32768 from 0

1 sending 65536 to 0

1 received 65536 from 0

1 sending 131072 to 0

1 received 131072 from 0

1 sending 262144 to 0

1 received 262144 from 0

1 sending 524288 to 0

1 received 524288 from 0

1 sending 1048576 to 0

1 received 1048576 from 0

1 sending 2097152 to 0

1 received 2097152 from 0

1 sending 4194304 to 0

1 received 4194304 from 0

1 sending 8388608 to 0

42

Page 43: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

1 received 8388608 from 0

Process 0 on Manolis.Athens.Greece

0 sending 1024 to 1

0 received 1024 from 1

0 sending 2048 to 1

0 received 2048 from 1

0 sending 4096 to 1

0 received 4096 from 1

0 sending 8192 to 1

0 received 8192 from 1

0 sending 16384 to 1

0 received 16384 from 1

0 sending 32768 to 1

0 received 32768 from 1

0 sending 65536 to 1

0 received 65536 from 1

0 sending 131072 to 1

0 received 131072 from 1

0 sending 262144 to 1

0 received 262144 from 1

0 sending 524288 to 1

0 received 524288 from 1

0 sending 1048576 to 1

0 received 1048576 from 1

0 sending 2097152 to 1

0 received 2097152 from 1

0 sending 4194304 to 1

0 received 4194304 from 1

0 sending 8388608 to 1

0 received 8388608 from 1

3.5.5 Υπολογισμός του π

Το παρακάτω πρόγραμμα υπολογίζει μια προσεγγιστική τιμή για το π

χρησιμοποιώντας αριθμητική ολοκλήρωση.

#include <stdlib.h>

#include <stdio.h>

#include "mpi.h"

#define sqr(x) ((x)*(x))

#define DARTS 5000 /* number of throws at dartboard */

#define ROUNDS 10 /* number of times "darts" is iterated */

43

Page 44: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

#define MASTER 0 /* task ID of master task */

long random(void);

double dboard(int darts)

{

double x_coord, /* x coordinate, between -1 and 1 */

y_coord, /* y coordinate, between -1 and 1 */

pi, /* pi */

r; /* random number between 0 and 1 */

int score, n; /* number of darts that hit circle */

unsigned long cconst;

/* used to convert integer random number */

/* between 0 and 2^31 to double random number */

/* between 0 and 1 */

cconst = 2 << (31 - 1);

score = 0;

/* "throw darts at board" */

for (n = 1; n <= darts; n++) {

/* generate random numbers for x and y coordinates */

r = (double)random()/cconst;

x_coord = (2.0 * r) - 1.0;

r = (double)random()/cconst;

y_coord = (2.0 * r) - 1.0;

/* if dart lands in circle, increment score */

if ((sqr(x_coord) + sqr(y_coord)) <= 1.0)

score++;

}

/* calculate pi */

pi = 4.0 * (double)score/(double)darts;

return(pi);

}

MPI_Status status;

MPI_Request request;

main(int argc, char **argv)

{

44

Page 45: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

double homepi, /* value of pi calculated by current task */

pi, /* average of pi after "darts" is thrown */

avepi, /* average pi value for all iterations */

pirecv, /* pi received from worker */

pisum; /* sum of workers pi values */

int mytid, /* task ID - also used as seed number */

nproc, /* number of tasks */

source, /* source of incoming message */

mtype, /* message type */

msgid, /* message identifier */

nbytes, /* size of message */

rcode, /* return code */

i, n;

/* Obtain number of tasks and task ID */

MPI_Init(&argc, &argv);

MPI_Comm_rank(MPI_COMM_WORLD, &mytid);

MPI_Comm_size(MPI_COMM_WORLD, &nproc);

printf ("MPI task ID = %d\n", mytid);

/* Set seed for random number generator equal to task ID */

srandom (mytid);

avepi = 0;

for (i = 0; i < ROUNDS; i++) {

/* All tasks calculate pi using dartboard algorithm */

homepi = dboard(DARTS);

/* Workers send homepi to master */

/* - Message type will be set to the iteration count */

/* - A non-blocking send is followed by mpi_wait */

/* this is safe programming practice */

if (mytid != MASTER) {

mtype = i;

MPI_Isend(&homepi, 1, MPI_DOUBLE, MASTER, mtype,

MPI_COMM_WORLD, &request);

MPI_Wait(&request, &status);

}

/* Master receives messages from all workers */

/* Message type will be set to the iteration count */

/* a message can be received from any task, as long as the */

/* message types match */

45

Page 46: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

/* The return code will be checked, and a message displayed */

/* if a problem occurred */

else {

mtype = i;

pisum = 0;

for (n = 1; n < nproc; n++) {

MPI_Recv(&pirecv, 1, MPI_DOUBLE, MPI_ANY_SOURCE,

mtype, MPI_COMM_WORLD, &status);

/* keep running total of pi */

pisum = pisum + pirecv;

}

/* Master calculates the average value of pi for this iteration */

pi = (pisum + homepi)/nproc;

/* Master calculates the average value of pi over all iterations */

avepi = ((avepi * i) + pi)/(i + 1);

printf(" After %3d throws, average value of pi = %10.8f\n",

(DARTS * (i + 1)),avepi);

}

}

MPI_Finalize();

}

Ένα στιγμιότυπο εκτέλεσης του προγράμματος με 10 διεργασίες είναι: 

MPI task ID = 0

After 5000 throws, average value of pi = 3.14424000

After 10000 throws, average value of pi = 3.14088000

After 15000 throws, average value of pi = 3.14584000

After 20000 throws, average value of pi = 3.14762000

After 25000 throws, average value of pi = 3.14385600

After 30000 throws, average value of pi = 3.14318667

After 35000 throws, average value of pi = 3.14357714

MPI task ID = 1

MPI task ID = 4

MPI task ID = 8

MPI task ID = 7

MPI task ID = 9

MPI task ID = 6

MPI task ID = 2

MPI task ID = 3

MPI task ID = 5

After 40000 throws, average value of pi = 3.14346000

After 45000 throws, average value of pi = 3.14483556

46

Page 47: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

After 50000 throws, average value of pi = 3.14424000

Παρατηρούμε ότι οι περισσότερες διεργασίες δεν μας δίνουν απαραίτητα, καλύτερη

προσέγγιση.

3.6 Εκτέλεση ενός προγράμματος MPI

Αν και οι λεπτομέρειες της εκτέλεσης του προγράμματος διαφέρουν από σύστημα σε

σύστημα, τα βήματα είναι τα ίδια με την προϋπόθεση ότι κάθε διεργασία εκτελείται σε ένα

μόνο επεξεργαστή. Τα βήματα αυτά είναι τα εξής:

1. Ο πηγαίος κώδικας μεταγλωττίζεται και παράγεται το εκτελέσιμο αρχείο.

2. Ο χρήστης εκτελεί το πρόγραμμα σε ένα επεξεργαστή και ο εκτελέσιμος κώδικας

αντιγράφεται σε κάθε επεξεργαστή από το MPI.

3. Κάθε επεξεργαστής αρχίζει να εκτελεί το δικό του αντίγραφο του κώδικα.

4. Διαφορετικές διεργασίες μπορούν να εκτελέσουν διαφορετικές εντολές

κάνοντας διακλάδωση μέσα στο πρόγραμμα. Η διακλάδωση συνήθως,

βασίζεται στην τάξη κάποιας διεργασίας.

Επομένως, τα MPI προγράμματα βασίζονται στο Single Program Multiple Data

(SPMD) πρότυπο προγραμματισμού. Δηλαδή, έχουμε το αποτέλεσμα της εκτέλεσης του

ιδίου προγράμματος σε διαφορετικούς επεξεργαστές κάνοντας διακλάδωση μέσα στον ίδιο

πηγαίο κώδικα ανάλογα με την τάξη κάποιας διεργασίας. Άρα, οι εντολές που εκτελούνται

από τη διεργασία 0, μπορεί να είναι διαφορετικές από αυτές που εκτελούνται από τις

υπόλοιπες διεργασίες, παρόλο που όλες οι διεργασίες εκτελούν τον ίδιο πηγαίο κώδικα.

3.7 Συμπεράσματα από την αναστέλλουσα επικοινωνία

Το blocking send και receive είναι η πιο απλή μορφή κόμβου με κόμβο επικοινωνίας

που παρέχει το MPI. Σε απλά προγράμματα η αναστέλλουσα επικοινωνία είναι αρκετή. Η

αναστολή της εκτέλεσης των διεργασιών που επικοινωνούν, εγγυάται την ασφαλή

επικοινωνία μεταξύ τους. Η αναστέλλουσα επικοινωνία βασίζεται σε μη-τοπικές κλήσεις

συναρτήσεων. Ενδέχεται δηλαδή, η επιστροφή μιας blocking συνάρτησης να εξαρτάται από

την κατάσταση και άλλων διεργασιών.

Κάθε μήνυμα συνοδεύεται από την ετικέτα που πληροφορεί τη διεργασία-παραλήπτη

για το είδος του μηνύματος. Η επιλογή ενός μηνύματος με τη χρήση της ετικέτας είναι πολύ

σημαντική διότι, μας δίνει τη δυνατότητα να κατηγοριοποιήσουμε τα μηνύματα που

λαμβάνονται. Έτσι, επιτυγχάνεται ομοιόμορφος χειρισμός των μηνυμάτων μιας κατηγορίας

(δηλαδή μηνυμάτων με την ίδια ετικέτα).

Επειδή έχουμε τη δυνατότητα να επιλέξουμε ποιο μήνυμα θα λάβουμε, δεν έχει νόημα

να μιλάμε για αντιστοίχηση των send και των receive κλήσεων. Εννοείται ότι οι διεργασίες

δεν απαιτούν να τους σταλεί ένα συγκεκριμένο μήνυμα. Διαφορετικά, θα μπορούσαν να

ενεργήσουν μόνες τους, χωρίς να χρειάζεται αποστολή μηνύματος. Μπορούν μόνο να

λάβουν ένα μήνυμα που έχει σταλεί. Τότε το MPI αναλαμβάνει το ρόλο ενός πρακτορείου

47

Page 48: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

όπου οι διεργασίες κάνουν κλήσεις αποστολής και λήψης και το MPI απλώς αναλαμβάνει το

ταίριασμά τους.

48

Page 49: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

4. Μη Αναστέλλουσα Επικοινωνία

4.1 Γενικά

Εκτός από τις συναρτήσεις αποστολής και λήψης μηνυμάτων οι οποίες υποστηρίζουν

αναστέλλουσα επικοινωνία μεταξύ διεργασιών, το MPI παρέχει και συναρτήσεις που

υποστηρίζουν μη αναστέλλουσα (non-blocking) επικοινωνία. Στη μη αναστέλλουσα

επικοινωνία η συνάρτηση αποστολής και η συνάρτηση παραλαβής μηνυμάτων επιστρέφουν

αμέσως, χωρίς να αναστέλλουν την εκτέλεση της διεργασίας που τις καλεί, άσχετα με το αν

έχει ολοκληρωθεί η διαδικασία αποστολής ή παραλαβής, αντίστοιχα. Η μη αναστέλλουσα

επικοινωνία επιτυγχάνεται ως εξής: Το MPI δίνει τη δυνατότητα διαχωρισμού της έναρξης

μιας διαδικασίας αποστολής ή παραλαβής μηνυμάτων από την ολοκλήρωσή της, παρέχοντας

τη δυνατότητα κλήσης δύο διαφορετικών συναρτήσεων αποστολής ή παραλαβής. Η κλήση

της πρώτης συνάρτησης αρχίζει τη διαδικασία αποστολής ή παραλαβής, ενώ η κλήση της

δεύτερης ελέγχει εάν η διαδικασία αυτή έχει ολοκληρωθεί. Μεταξύ των δύο κλήσεων δεν

αναστέλλεται η εκτέλεση της διεργασίας-αποστολέα ή της διεργασίας παραλήπτη. Η έννοια

της ολοκλήρωσης της διαδικασίας αποστολής ή παραλαβής στην μη αναστέλλουσα

επικοινωνία παραμένει η ίδια με αυτήν της αναστέλλουσας επικοινωνίας και προσδιορίζεται

ανάλογα με την κατάσταση επικοινωνίας (communication mode). Εκείνο που αλλάζει στην

μη αναστέλλουσα επικοινωνία σε σχέση με την αναστέλλουσα, είναι το πότε επιστρέφει μία

συνάρτηση αποστολής ή παραλαβής μηνυμάτων.

4.2 Κίνητρο για μη αναστέλλουσα επικοινωνία

Εξ’ ορισμού, η αναστέλλουσα επικοινωνία είναι πιο αξιόπιστη από την μη

αναστέλλουσα. Πολλές φορές όμως, μπορεί να οδηγήσει την εφαρμογή σε αδιέξοδο. Για να

γίνει αυτό καλύτερα κατανοητό, ας θεωρήσουμε έναν communicator στον οποίον ανήκουν

έξι διεργασίες οι οποίες είναι συνδεδεμένες σύμφωνα με τη δομή ενός δακτυλίου και είναι

αριθμημένες διαδοχικά απο το 0 έως το 5. Έστω ότι κάθε μια στέλνει ένα μήνυμα στη

γειτονική της διεργασία καλώντας την MPI_Send και μετά εκτελεί μια κλήση receive.

Επειδή ενδέχεται η διαδικασία αποστολής να μην ολοκληρώνεται και η MPI_Send να μην

49

Page 50: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

επιστρέφει έως ότου συγχρονιστούν οι διεργασίες παραλήπτη και αποστολέα και αρχίσει η

αποστολή του μηνύματος, και επειδή όλες οι διεργασίες στέλνουν μηνύματα αλλά καμμία

δεν μπορεί να αρχίσει να λαμβάνει, είναι πιθανόν να προκύψει αδιέξοδο και καμμία

επικοινωνία να μην ολοκληρώνεται. Μια λύση είναι να αλλάξουμε τον αλγόριθμο της

εφαρμογής, έτσι ώστε οι μισές διεργασίες να στέλνουν και οι υπόλοιπες μισές να

λαμβάνουν. Για παράδειγμα, οι διεργασίες με τάξη 0, 1, 2 να στέλνουν στις 4, 3 και 5,

αντίστοιχα. Μετά τη λήψη, οι αποστολείς θα γίνουν παραλήπτες και αντίστροφα. Δηλαδή,

θα στείλουν οι 5, 3 και 4 στις 1, 0 και 2, αντίστοιχα. Με αυτό τον τρόπο, έχουμε μόνον τρεις

επικοινωνίες στον communicator οι οποίες είναι ανεξάρτητες μεταξύ τους.

Μια άλλη λύση είναι να χρησιμοποιηθεί μη αναστέλλουσα επικοινωνία. Έτσι, όλες οι

διαδικασίες αποστολής μπορούν να ξεκινήσουν ταυτόχρονα, άσχετα με το αν έχουν

ξεκινήσει οι διαδικασίες λήψης. Επειδή δε οι συναρτήσεις που ξεκινούν τις διαδικασίες

αποστολής, επιστρέφουν αμέσως, μπορούν να ξεκινήσουν και οι διαδικασλιες λήψης και να

αποφευχθεί το αδιέξοδο.

4.3 Κλήσεις μη αναστέλλουσας επικοινωνίας και αντικείμενο request

Προκειμένου να χρησιμοποιήσουμε μη αναστέλλουσα επικοινωνία το MPI απαιτεί

δύο κλήσεις για κάθε διαδικασία επικοινωνίας (αποστολή ή λήψη): Μία κλήση για να

ξεκινήσει η διαδικασία επικοινωνίας και μία δεύτερη για να ελεχθεί εάν η διαδικασία αυτή

ολοκληρώθηκε.

Η έναρξη μιας διαδικασίας αποστολής μηνύματος γίνεται με την κλήση μιας

συνάρτησης έναρξης αποστολής (posting send,) η οποία επιστρέφει αμέσως. Η

έναρξη μιας διαδικασίας παραλαβής μηνύματος γίνεται με την κλήση μιας

συνάρτησης έναρξης παραλαβής (posting receive) η οποία επίσης, επιστρέφει

αμέσως.

Από τη στιγμή που έχει ξεκινήσει μία διαδικασία αποστολής ή παραλαβής

μηνύματος, μία διεργασία μπορεί να ελέγξει εάν η διαδικασία αυτή έχει

ολοκληρωθεί καλώντας μία συνάρτηση ολοκλήρωσης αποστολής (complete send) ή

μία συνάρτηση ολοκλήρωσης παραλαβής (complete receive) αντίστοιχα, χωρίς να

αναστέλλεται η διαδικασία της ολοκλήρωσης της διαδικασίας. Εναλλακτικά, μία

διεργασία μπορεί να περιμένει να ολοκληρωθεί μία διαδικασία χωρίς να καλέσει

την αντίστοιχη συνάρτηση ολοκλήρωσης. Ωστόσο, αν δεν κληθεί η συνάρτηση

ολοκλήρωσης, η διεργασία δεν μπορεί να είναι σίγουρη ότι η επικοινωνία

ολοκληρώθηκε.

Ένα παράδειγμα non-blocking λήψης μηνύματος είναι ένα fax που τίθεται σε

λειτουργία και παραμένει ανοιχτό περιμένοντας να λάβει ένα έγγραφο. Ο χρήστης του fax

μπορεί να ελέγχει περιοδικά, αν ήρθε το αναμενόμενο έγγραφο.

Από τη στιγμή που μία διεργασία ξεκινάει μία διαδικασία αποστολής ή παραλαβής

καλώντας μία μή αναστέλλουσα συνάρτηση, χρείάζεται κάποιον τρόπο να αναφέρεται σε

αυτήν τη διαδικασία. Γι’ αυτόν το σκοπό το MPI χρησιμοποιεί χειριστές σε αντικείμενα

request (request handles). Το αντικείμενο request (request object) είναι μια δομή δεδομένων

50

Page 51: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

που δεσμεύει το MPI όταν γίνεται μια μη αναστέλλουσα επικοινωνία. Αυτά τα αντικείμενα

βρίσκονται στη μνήμη συστήματος του MPI και δεν μπορούν να προσπελαστούν άμεσα από

το πρόγραμμα του χρήστη. Γι’ αυτό, λέγονται αδιαφανή αντικείμενα (opaque objects). Η

προσπέλασή τους γίνεται με τη βοήθεια των χειριστών. Η κλήση μιας συνάρτησης έναρξης

μιας διαδικασίας αποστολής ή παραλαβής δημιουργεί ένα αντικείμενο request και

επιστρέφει ένα χειριστή (handle) προς αυτό μέσω της παραμέτρου request. Στο αντικείμενο

request περιέχονται πληροφορίες που αφορούν την επικοινωνία, όπως, το είδος της

ενδιάμεσης μνήμης που περιέχει το προς αποστολή μήνυμα και την κατάσταση των

επικοινωνιών που έχουν ήδη αρχίσει και περιμένουν να ολοκληρωθούν. Υπάρχουν

συναρτήσεις που επιστρέφουν την κατάσταση αυτών των επικοινωνιών. Σ’ αυτές, περνάμε

ως παράμετρο, το χειριστή που επιστρέφεται από τη συνάρτηση που δημιούργησε το

αντικείμενο request.

Η σταθερά MPI_REQUEST_NULL δείχνει έναν άκυρο χειριστή σε ένα αντικείμενο

request. Οι συναρτήσεις που καταστρέφουν ένα αντικείμενο request, θέτουν το χειριστή του

αντικειμένου σ’ αυτήν την τιμή. Προφανώς, είναι λάθος να περαστεί ως παράμετρος, σε

συνάρτηση ένας χειριστής με τιμή MPI_REQUEST_NULL.

4.4 Συναρτήσεις μη αναστέλλουσας επικοινωνίας

Όπως έχει ήδη αναφερθεί, στην μη αναστέλλουσα επικοινωνία έχουμε δύο ειδών

κλήσεις. Τις κλήσεις οι οποίες ξεκινούν τις διαδικασίες επικοινωνίας και τις κλήσεις οι

οποίες ελέγχουν για την ολοκλήρωση των διδικασιών αυτών.

4.4.1 Συναρτήσεις έναρξης επικοινωνίας (posting send και receive)

Οι συναρτήσεις που ξεκινούν μια μη αναστέλλουσα επικοινωνία έχουν τα ίδια

ονόματα με τις αντίστοιχες που ξεκινούν μιαν αναστέλλουσα επικοινωνία, αλλά έχουν ένα

πρόσθετο γράμμα I (Immediate), που δείχνει ότι η κλήση είναι non-blocking. `Ετσι οι

βασικές συναρτήσεις έναρξης μη αναστέλλουσας επικοινωνίας είναι οι MPI_Isend και

MPI_Irecv.

Η συνάρτηση MPI_Isend

Η MPI_Isend ξεκινά τη διαδικασία αποστολής μηνύματος:

int MPI_Isend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm, MPI_Request *request)

Όπου:

buf Η διεύθυνση αρχής της ενδιάμεσης μνήμης όπου βρίσκεται

το προς αποστολή μήνυμα.

51

Page 52: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

count Αριθμός στοιχείων προς αποστολή (μέγεθος του buffer).

datatype Τι είδους δεδομένα αποστέλλονται (δηλαδή MPI_INT,

MPI_FLOAT, MPI_CHAR, κτλ.

dest Η τάξη της διεργασίας παραλήπτη.

tag Η ετικέτα του μηνύματος.

comm Ο communicator του παραλήπτη.

request Χειριστής προς ένα αντικείμενο request.

Όπως φαίνεται, οι παράμετροι κλήσης της είναι ίδιες με την MPI_Send εκτός από την

παράμετρο request η οποία είναι ένας χειριστής σε ένα αντικείμενο request. Σ’ αυτή την

παράμετρο, επιστρέφεται ο χειριστής του αντικειμένου request που δημιουργεί το MPI για

την επικοινωνία.

Η κλήση της MPI_Isend λέει στο MPI ότι μπορεί να αρχίσει την διαδικασία

αποστολής δεδομένων. Ακόμη και όταν επιστρέψει η MPI_Isend, η διεργασία-αποστολέας

δεν πρέπει να προσπελάσει την ενδιάμεση μνήμη (ούτε καν για ανάγνωση) και τις άλλες

παραμέτρους της MPI_Isend, μέχρι να επιστρέψει η συνάρτηση ολοκλήρωσης της

διαδικασίας αποστολής.

Η συνάρτηση MPI_Irecv

Η MPI_Irecv ξεκινάει τη διαδικασία παραλαβής μηνύματος. Οι παράμετροί της είναι

ίδιες με αυτές της MPI_Recv μόνο που στη θέση του status υπάρχει το request.

int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, int tag,

MPI_Comm comm, MPI_Request *request)

Όπου:

buf Η διεύθυνση αρχής της ενδιάμεσης μνήμης λήψης στην οποία θα

αποθηκευτεί το μήνυμα.

count Ο μέγιστος αριθμός στοιχείων προς λήψη.

datatype Τι είδους δεδομένα θα ληφθούν σε κάθε στοιχείο.

source Η τάξη της διεργασίας αποστολέα.

tag Ο τύπος του μηνύματος.

comm Ο communicator στον οποίο ανήκει η διεργασία

αποστολέας.

request Χειριστής προς ένα αντικείμενο request.

Ακόμη και όταν επιστρέψει η MPI_Irecv, η διεργασία-παραλήπτης δεν πρέπει να

προσπελάσει την ενδιάμεση μνήμη λήψης και τις άλλες παραμέτρους της MPI_Irecv, μέχρι

να επιστρέψει η συνάρτηση ολοκλήρωσης της διαδικασίας παραλαβής..

52

Page 53: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

4.4.2 Συναρτήσεις ολοκλήρωσης επικοινωνίας

Υπάρχουν δυο είδη ελέγχου για την ολοκλήρωση μιας διαδικασίας αποστολής ή

παραλαβής μηνυμάτων που έχει ξεκινήσει με την κλήση μιας συνάρτησης αποστολής ή

παραλαβής (π.χ., την MPI_Isend ή την MPI_Irecv).

Ο έλεγχος Wait ο οποίος γίνεται από την συνάρτηση MPI_Wait και κάποιες παραλλαγές

της. Οι συναρτήσεις που υποστηρίζουν αυτό το είδος ελέγχου, αναστέλλουν την

εκτέλεση της διεργασίας που τις καλεί. Είναι χρήσιμες όταν για παράδειγμα, τα

δεδομένα που ελήφθησαν από την κλήση μιας μη αναστέλλουσας διαδικασίας

παραλαβής, χρειάζονται για κάποιον υπολογισμό, ή όταν η ενδιάμεση μνήμη

επικοινωνιών πρόκειται να ξαναχρησιμοποιηθεί. Είναι εύκολο να παρατηρήσουμε ότι

μια μη αναστέλλουσα επικοινωνία που ακολουθείται αμέσως από έναν έλεγχο Wait,

είναι ισοδύναμη με μια αναστέλλουσα επικοινωνία.

Ο έλεγχος Test ο οποίος γίνεται από την συνάρτηση MPI_Test και κάποιες παραλλαγές

της. Οι συναρτήσεις που υποστηρίζουν αυτό το είδος ελέγχου, επιστρέφουν true ή false

ανάλογα με το αν έχει ολοκληρωθεί η επικοινωνία. Δεν αναστέλλουν την εκτέλεση της

διεργασίας που τις καλεί, και είναι χρήσιμες όταν θέλουμε να ελέγξουμε αν

ολοκληρώθηκε η επικοινωνία, αλλά δεν μας ενδιαφέρει προς στιγμή, το αποτέλεσμά

της, ούτε θέλουμε να ξαναχρησιμοποιήσουμε αμέσως την ενδιάμεση μνήμη. Για

παράδειγμα, όταν η διεργασία-παραλήπτης θέλει να κάνει κάτι άλλο πριν ασχοληθεί με

τα δεδομένα που έλαβε από την κλήση μιας διαδικασίας παραλαβής..

Οι συναρτήσεις MPI_Wait και MPI_Test ολοκληρώνουν τις μη αναστέλλουσες

διαδικασίες αποστολής και λήψης μηνυμάτων. Η ολοκλήρωση μιας αποστολής, δείχνει ότι ο

αποστολέας είναι τώρα ελεύθερος να προσπελάσει την ενδιάμεση μνήμη αποστολής. Με την

ολοκλήρωση της λήψης, ο παραλήπτης γνωρίζει ότι η ενδιάμεση μνήμη λήψης περιέχει το

μήνυμα, ότι είναι ελεύθερος να το προσπελάσει, και ότι τα πεδία της δομής status έχουν

έγκυρες τιμές.

Η συνάρτηση MPI_Wait

Μια διεργασία μπορεί να μπει σε κατάσταση αναμονής μέχρι να τελειώσει μια

επικοινωνία, καλώντας την MPI_Wait. Η δήλωσή της είναι:

int MPI_Wait(MPI_Request *request, MPI_Status *status)

Όπου:

request Χειριστής προς ένα αντικείμενο request (ο οποίος επεστράφει κατά την

έναρξη της διαδικασίας αποστολής ή λήψης)

status Στην περίπτωση μιας διαδικασίας παραλαβής, περιέχει πληροφορίες

σχετικά με το μήνυμα που παρελήφθη. Στην περίπτωση μιας διαδικασίας

αποστολής, μπορεί να περιέχει έναν κωδικό λάθους.

53

Page 54: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Η συνάρτηση επιστρέφει έναν κωδικό λάθους. Η παράμετρος request προσδιορίζει μία μη αναστέλλουσα διαδικασία αποστολής (posting send) ή παραλαβής (posting receive) που έχει κληθεί προηγουμένως. Η MPI_Wait επιστρέφει όταν ολοκληρωθεί η επικοινωνία που χαρακτηρίζεται από την παράμετρο request. Το αντικείμενο request που είχε δημιουργηθεί από μία μη αναστέλλουσα διαδικασία αποστολής (posting send) ή παραλαβής (posting receive), καταστρέφεται και η request παίρνει την τιμή MPI_REQUEST_NULL. Στην περίπτωση μιας μη αναστέλλουσας διαδικασίας παραλαβής, οι τιμές των παραμέτρων source, tag και count των δεδομένων που ελήφθησαν, είναι διαθέσιμες μέσω της παραμέτρου status. Στην περίπτωση μιας μη αναστέλλουσας διαδικασίας αποστολής, η παράμετρος status μπορεί να περιέχει έναν κωδικό λάθους που επιστρέφει η διαδικασία αποστολής. Ο κωδικός αυτός είναι διαφορετικός από τον κωδικό λάθους που επιστρέφει η κλήση της MPI_Wait.

Η συνάρτηση MPI_Test

Για να ελέγξει μια διεργασία αν ολοκληρώθηκε η αποστολή ή η παραλαβή ενός

μηνύματος, μπορεί να καλέσει τη συνάρτηση MPI_TEST. Η δήλωση είναι:

int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status)

Όπου:

request Χειριστής προς το αντικείμενο request.

flag Γίνεται αληθής αν έχει ολοκληρωθεί η μετάδοση.

status Επιστρέφει ένα αντικείμενο status.

Η MPI_Test επιστρέφει αμέσως. Η παράμετρος request προσδιορίζει μία μη

αναστέλλουσα διαδικασία αποστολής (posting send) ή παραλαβής (posting receive) που έχει

κληθεί προηγουμένως. Αν η παράμετρος flag είναι αληθής, τότε η μη αναστέλλουσα

διαδικασία επικοινωνίας που χαρακτηρίζεται από την παράμετρο request, έχει ολοκληρωθεί.

Όπως και με την κλήση της MPI_Wait, το αντικείμενο request στο οποίο δείχνει ο χειριστής

καταστρέφεται και του αποδίδεται η τιμή της σταθεράς MPI_REQUEST_NULL. Αν η

επικοινωνία που χαρακτηρίζεται από την παράμετρο request, δεν έχει ολοκληρωθεί, η

παράμετρος flag έχει την τιμή ψευδής. Στην περίπτωση μιας διαδικασίας παραλαβής, αν η

flag είναι αληθής, οι τιμές των παραμέτρων source, tag και count των δεδομένων που

ελήφθησαν, είναι διαθέσιμες μέσω της παραμέτρου status. Στην περίπτωση μιας μη

αναστέλλουσας διαδικασίας αποστολής, αν η flag είναι αληθής, η παράμετρος status μπορεί

να περιέχει έναν κωδικό λάθους που επιστρέφει η διαδικασία αποστολής (όχι η MPI_Test).

4.5 Ακύρωση επικοινωνίας

Η συνάρτηση MPI_Cancel χρησιμοποιείται για την ακύρωση μιας μη αναστέλλουσας

διαδικασίας επικοινωνίας η αποία έχει ξεκινήσει από μια συνάρτηση έναρξης επικοινωνίας

(posting send ή posting receive) αλλά δεν έχει ακόμη ολοκληρωθεί.

54

Page 55: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

int MPI_Cancel(MPI_Request *request)

request Χειριστής προς το αντικείμενο request.

Η συνάρτηση MPI_Cancel μαρκάρει μια επικοινωνία (η οποία έχει ξεκινήσει από μια

συνάρτηση έναρξης επικοινωνίας) για ακύρωση και επιστρέφει αμέσως. Αν η επικοινωνία

δεν μπορεί να ακυρωθεί (δηλαδή, αν έχει ήδη κληθεί η συνάρτηση complete send ή receive),

τότε η επικοινωνία ολοκληρώνεται κανονικά. Αν η επικοινωνία ακυρωθεί επιτυχώς, η κλήση

της συνάρτησης ολοκλήρωσης θα αποδεσμεύσει το αντικείμενο request και θα επιστρέψει

στην παράμετρο status την πληροφορία ότι η επικοινωνία ακυρώθηκε. Αν μια επικοινωνία

είναι σημειωμένη για ακύρωση, η κλήση της συνάρτησης ολοκλήρωσης για αυτήν την

επικοινωνία, θα επιστρέψει, άσχετα από τις δραστηριότητες των άλλων διεργασιών.

Η εφαρμογή πρέπει να καλέσει τη συνάρτηση MPI_Test_cancelled περνώντας της ως

παράμετρο την παράμετρο status που επέστρεψε η κλήση της συνάρτησης ολοκλήρωσης,

για να ελέγξει αν η επικοινωνία πράγματι ακυρώθηκε. Η σύνταξη της MPI_Test_cancelled

είναι:

int MPI_Test_cancelled(MPI_Status *status, int *flag)

Όπου:

status Είναι ένα αντικείμενο status.

flag Έχει την τιμή αληθής αν η επικοινωνία έχει ακυρωθεί.

Αν η τιμή της παραμέτρου flag είναι αληθής, τα πεδία της παραμέτρου status δεν

είναι καθορισμένα. Αν η επικοινωνία δεν έχει ακυρωθεί, η τιμή της flag είναι ψευδής.

Παρακάτω δίνεται ένα τμήμα κώδικα που χρησιμοποιεί την MPI_Cancel.

MPI_Comm_rank(comm, &rank);

if (rank == 0)

MPI_Send(a, 1, MPI_CHAR, tag, comm);

else if (rank == 1)

{

MPI_Irecv(a, 1, MPI_CHAR, 0, tag, comm, &req);

MPI_Cancel(&req);

MPI_Wait(&req, &status);

MPI_Test_cancelled(&status, &flag);

if (flag) /* true άρα η cancel ήταν επιτυχής. Ξεκινάμε καινούργια receive */

MPI_Recv(a, 1, MPI_CHAR, 0, tag, comm, &req);

}

4.6 Έλεγχος για ολοκλήρωση πολλών επικοινωνιών

Δεν είναι ασυνήθιστο σε ένα MPI πρόγραμμα να έχουμε πολλές μη αναστέλλουσες

διαδικασίες επικοινωνίας που περιμένουν να ολοκληρωθούν. Γι’ αυτό υπάρχουν

55

Page 56: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

συναρτήσεις που ελέγχουν την ολοκλήρωση πολλών επικοινωνιών. Υπάρχουν τρία είδη

συναρτήσεων. Αυτές που ελέγχουν την ολοκλήρωση όλων των διαδικασιών επικοινωνίας,

αυτές που ελέγχουν αν ολοκληρώθηκαν κάποιες διαδικασίες, και τέλος, αυτές που ελέγχουν

αν ολοκληρώθηκέ τουλάχιστον μία διαδικασία. Κάθε είδος συνάρτησης παρέχεται σε Wait

και Test μορφή. Ο παρακάτω πίνακας δείχνει αυτές τις συναρτήσεις

Συναρτήσεις ελέγχου ολοκλήρωσης

Συνθήκη Ελέγχου

Έλεγχος Wait

(blocking)

Έλεγχος Test

(non-blocking, μόνο query)

Αν έχει ολοκληρωθεί

τουλάχιστον μία. Επιστρέφει

ακριβώς μία.

MPI_ Waitany MPI_Testany

Αν έχουν ολοκληρωθεί όλες. MPI_Waitall MPI_Testall

Αν έχει ολοκληρωθεί

τουλάχιστον μία. Επιστρέφει

όλες όσες έχουν ολοκληρωθεί.

MPI_Waitsome MPI_Testsome

Αν και τα ονόματα των συναρτήσεων δείχνουν τι κάνει η κάθε μια, ακολουθεί μια

σύντομη περιγραφή τους.

4.6.1 Η συνάρτηση MPI_Waitany

Η MPI_Waitany αναστέλλει την εκτέλεση της διεργασίας που την καλεί, μέχρι να

ολοκληρωθεί μία τουλάχιστον από τις διαδικασίες επικοινωνίας σε εκκρεμότητα. Αν

περισσότερες από μία μπορούν να ολοκληρωθούν, επιλέγεται μία αυθαίρετα. Η κλήση της

είναι:

int MPI_Waitany(int count, MPI_Request *array_of_requests, int *index,

MPI_Status *status)

Όπου:

count Το μήκος της λίστας.

array_of_requests Πίνακας που περιέχει χειριστές request.

index Θέση του πίνακα που αντιστοιχεί σε χειριστή

request που ολοκληρώθηκε.

56

Page 57: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

status Δείκτης προς μια δομή status.

Η συνάρτηση επιστρέφει στη μεταβλητή index τη θέση του πίνακα του αντίστοιχου

αντικειμένου request που ολοκληρώθηκε, και στη status την κατάσταση της επικοινωνίας.

Το αντικείμενο request παύει να υπάρχει και ο χειριστής request παίρνει την τιμή της

σταθεράς MPI_REQUEST_NULL. Πρόκειται για μια μη τοπική συνάρτηση.

4.6.2 Η συνάρτηση MPI_Testany

Η συνάρτηση MPI_Testany ελέγχει αν έχει ολοκληρωθεί τουλάχιστον μια μη

αναστέλλουσα διδικασία επικοινωνίας σε εκκρεμότητα. Η κλήση της είναι:

int MPI_Testany(int count, MPI_Request *array_of_requests, int *index,

int *flag, MPI_Status *status)

Όπου:

count Το μήκος της λίστας.

array_of_requests Πίνακας που περιέχει χειριστές request.

index Θέση του πίνακα που αντιστοιχεί σε χειριστή

request που ολοκληρώθηκε.

flag true αν έχει ολοκληρωθεί.

status Δείκτης προς μια δομή status.

Αν η επικοινωνία έχει ολοκληρωθεί, η τιμή της flag γίνεται true, στη μεταβλητή index

επιστρέφεται η θέση του πίνακα του αντικειμένου request που ολοκληρώθηκε, και στη status

η κατάσταση της επικοινωνίας. Το αντικείμενο request παύει να υπάρχει και ο χειριστής

request handle παίρνει την τιμή της σταθεράς MPI_REQUEST_NULL. Η συνάρτηση είναι

μη τοπική. Αν καμμία επικοινωνία δεν έχει ολοκληρωθεί, η τιμή της flag γίνεται false και η

δομή status είναι ακαθόριστη. Τότε, επιστρέφεται η τιμή της MPI_UNDEFINED στην

index.

Η κλήση της MPI_Testany(count, array_of_requests, &index, &flag, &status) έχει το

ίδιο αποτέλεσμα με την κλήση MPI_Test(&array_of_requests[i], & flag, &status), για i = 0,

1, … , count - 1, μέχρι κάποια κλήση να επιστρέψει flag = true ή μέχρι να αποτύχουν όλες

οι κλήσεις της MPI_Testany. Στην πρώτη περίπτωση, η μεταβλητή index παίρνει την

τελευταία τιμή του i και στη δεύτερη, παίρνει την τιμή MPI_UNDEFINED.

4.6.3 Η συνάρτηση MPI_Waitall

Η MPI_Waitall αναστέλλει την εκτέλεση της διεργασίας που την καλεί, μέχρι να

ολοκληρωθούν όλες οι διαδικασίες επικοινωνίας σε εκκρεμότητα.. Η κλήση της είναι:

int MPI_Waitall(int count, MPI_Request *array_of_requests,

57

Page 58: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

MPI_Status *array_of_statuses)

Όπου:

count Το μήκος της λίστας.

array_of_requests Πίνακας που περιέχει χειριστές request.

array_of_statuses Πίνακας που περιέχει δομές τύπου status.

Το στοιχείο i του πίνακα array_of_requests παίρνει ως τιμή το αποτέλεσμα της i-οστής

διαδικασίας επικοινωνίας. Όλα τα αντικείμενα request αποδεσμεύονται από τη μνήμη και οι

αντίστοιχοι χειριστές παίρνουν την τιμή MPI_REQUEST_NULL. Πρόκειται για μια μη

τοπική συνάρτηση.

Η κλήση της MPI_Waitall(count, &array_of_requests, &array_of_statuses) έχει το ίδιο

αποτέλεσμα με την κλήση MPI_Wait(&array_of_requests[i], &array_of_statuses[I]), για i =

0, 1, … , count - 1. Αν μία η περισσότερες επικοινωνίες δεν έχουν ολοκληρωθεί όταν κληθεί

η MPI_Waitall, επιστρέφεται ο κωδικός λάθους MPI_ERR_IN_STATUS.

4.6.4 Η συνάρτηση MPI_Testall

Η MPI_Testall επιστρέφει true αν έχουν ολοκληρωθεί όλες οι διαδικασίες

επικοινωνίας σε εκκρεμότητα. Είναι μια τοπική συνάρτηση. Η κλήση της είναι

int MPI_Testall(int count, MPI_Request *array_of_requests, int *flag,

MPI_Status *array_of_statuses)

Όπου:

count Το μήκος της λίστας.

array_of_requests Πίνακας που περιέχει χειριστές request.

flag Αληθής αν έχουν ολοκληρωθεί όλες οι επικοινωνίες.

array_of_statuses Πίνακας που περιέχει δομές τύπου status.

Αν έχουν ολοκληρωθεί όλες οι επικοινωνίες, η τιμή της μεταβλητής flag γίνεται true,

οι αντίστοιχες θέσεις του πίνακα status ενημερώνονται με τις αλλαγές, και αποδεσμεύεται η

μνήμη που είχε δεσμευτεί για τον πίνακα array_of_requests. Τέλος, όλοι οι χειριστές request

παίρνουν την τιμή της σταθεράς MPI_REQUEST_NULL.

4.6.5 Η συνάρτηση MPI_Waitsome

Η MPI_Waitsome αναστέλλει την εκτέλεση της διεργασίας που την καλεί μέχρι να

ολοκληρωθεί τουλάχιστον μια διαδικασία επικοινωνίας σε εκκρεμμότητα. Επιστρέφει όλες

τις διαδικασίες που ολοκληρώθηκαν. Η κλήση της είναι:

int MPI_Waitsome(int incount, MPI_Request *array_of_requests,

int *outcount, int *array_of_indices, MPI_Status *array_of_statuses)

58

Page 59: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Όπου:

incount Το μέγεθος του πίνακα array_of_requests.

array_of_requests Πίνακας με τους χειριστές request.

outcount Αριθμός ολοκληρωμένων επικοινωνιών.

array_of_indices Πίνακας με τις θέσεις των ολοκληρωμένων επικοινωνιών.

array_of_statuses Πίνακας με δομές status για τις ολοκληρωμένες επικοινωνίες.

4.6.6 Η συνάρτηση MPI_Testsome

Η MPI_Testsome συμπεριφέρεται όπως και η MPI_Waitsome με τη διαφορά ότι δεν

αναστέλλει την εκτέλεση της καλούσας διεργασίας και επιστρέφει αμέσως. Η κλήση της έχει

τις ίδιες παραμέτρους με την MPI_Waitsome, δηλαδή είναι:

int MPI_Testsome(int incount, MPI_Request *array_of_requests,

int *outcount, int *array_of_indices)

Όπου:

incount Το μέγεθος του πίνακα array_of_requests.

array_of_requests Πίνακας με τους χειριστές request.

outcount Αριθμός ολοκληρωμένων επικοινωνιών.

array_of_indices Πίνακας με τις θέσεις των ολοκληρωμένων επικοινωνιών.

array_of_statuses Πίνακας με δομές status για τις ολοκληρωμένες επικοινωνίες.

4.7 Σύγκριση αναστέλλουσας και μη αναστέλλουσας επικοινωνίας

Η μη αναστέλλουσα επικοινωνία είναι πιο γρήγορη σε σχέση με την αναστέλλουσα

διότι η συνάρτηση MPI_ΙSend επιστρέφει αμέσως χωρίς να περιμένει να ολοκληρωθεί η

μετάδοση. Πολλές φορές, όμως, ο χρόνος που κερδίζουμε από την άμεση επιστροφή της

MPI_Send αντισταθμίζεται από τον έλεγχο που κάνουμε για να δούμε αν η μετάδοση

ολοκληρώθηκε. Ακόμα, η μη αναστέλλουσα επικοινωνία, δεν είναι τόσο αξιόπιστη όσο η

αναστέλλουσα. Υπάρχει η πιθανότητα μια διεργασία-παραλήπτης να προσπαθήσει να

προσπελάσει δεδομένα που δεν έχει ακόμα λάβει, επειδή, η προσπάθεια προσπέλασης έγινε

πριν ολοκληρωθεί η επικοινωνία

Αν κύριο μέλημα μας είναι η ταχύτητα, και γνωρίζουμε από πριν ότι η αξιοπιστία στη

μετάδοση είναι εξασφαλισμένη (ελέγχοντας αν έχουν φτάσει τα δεδομένα πριν

προσπαθήσουμε να τα χρησιμοποιήσουμε) μπορούμε να χρησιμοποιήσουμε μη

αναστέλλουσα επικοινωνία.

59

Page 60: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

4.8 Καταστάσεις επικοινωνίας

Η έννοια της ολοκλήρωσης μιας διαδικασίας αποστολής ή παραλαβής προσδιορίζεται

ανάλογα με την κατάσταση επικοινωνίας (communication mode). Το MPI υποστηρίζει

τέσσερις καταστάσεις επικοινωνίας οι οποίες ορίζουν κριτήρια προκειμένου να

προσδιορισθεί πότε μία διαδικασία αποστολής έχει ολοκηρωθεί. Υπάρχει μόνο μία

κατάσταση επικοινωνίας receive (receive mode) η οποία αντιστοιχεί σε όλες τις

καταστάσεις επικοινωνίας send (send modes). Οι καταστάσεις επικοινωνίας send είναι η

standard, η synchronous, η buffered και η ready.

Όλες οι καταστάσεις επικοινωνίας send υπάρχουν σε blocking και non-blocking

μορφή. Η έννοια της ολοκλήρωσης μιας διαδικασίας αποστολής ή παραλαβής μηνυμάτων

στην αναστέλλουσα και μη αναστέλλουσα επικοινωνία παραμένει η ίδια και προσδιορίζεται

ανάλογα με την κατάσταση επικοινωνίας (communication mode). Εκείνο που αλλάζει στην

μη αναστέλλουσα επικοινωνία σε σχέση με την αναστέλλουσα είναι το πότε επιστρέφει μία

συνάρτηση αποστολής ή παραλαβής μηνυμάτων. Στην αναστέλλουσα επικοινωνία, η

επιστροφή μιας συνάρτησης αποστολής ή παραλαβής ταυτίζεται με την ολοκλήρωση της

αντίστοιχης διαδικασίας αποστολής ή παραλαβής. Στην μη αναστέλλουσα επικοινωνία η

επιστροφή μιας συνάρτησης έναρξης της αποστολής ή της παραλαβής γίνεται άμεσα ενώ ο

έλεγχος για την ολοκλήρωση της αντίστοιχης διαδικασίας αποστολής ή παραλαβής γίνεται

από τις συναρτήσεις MPI_Test και MPI_Wait (και από κάποιες παρόμοιες συναρτήσεις).

Οι συναρτήσεις MPI_Send και MPI_Isend που περιγράφηκαν σε προηγούμενα κεφάλαια,

χρησιμοποιούν τη standard κατάσταση επικοινωνίας.

Για κάθε κατάσταση επικοινωνίας παρέχεται διαφορετική συνάρτηση send. Ένα

πρόσθετο γράμμα στο όνομα της συνάρτησης δείχνει την κατάσταση επικοινωνίας που αυτή

χρησιμοποιεί. `Ετσι, χρησιμοποιούνται τα γράμματα B, S και R για να δηλώσουν την

buffered, synchronous και ready κατάσταση επικοινωνίας, αντίστοιχα. Οι παρακάτω πίνακες

περιέχουν τις συναρτήσεις επικοινωνίας του MPI για κάθε κατάσταση επικοινωνίας στην

περίπτωση της αναστέλλουσας και μη αναστέλλουσας επικοινωνίας.

Αναστέλλουσα μορφή

Standard send MPI_Send

Synchronous send MPI_Ssend

Buffered send MPI_Bsend

Ready send MPI_Rsend

Receive MPI_Recv

Μη αναστέλλουσα μορφή

Standard send MPI_Isend

Synchronous send MPI_Issend

Buffered send MPI_Ibsend

Ready send MPI_Irsend

Receive MPI_Irecv

60

Page 61: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Οι συναρτήσεις αποστολής (send) μηνυμάτων σε όλες τις καταστάσεις επικοινωνίας,

χρησιμοποιούν τα ορίσματα buf, count, datatype, source, dest, tag, comm, status και request

με τον ίδιο τρόπο που τα χρησιμοποιούν στην κατάσταση επικοινωνίας standard.

4.8.1 Standard send

Η κατάσταση επικοινωνίας standard για την αποστολή μηνυμάτων (standard send)

είναι η κατάσταση που συχνότερα χρησιμοποιείται. Οι άλλες τρεις καταστάσεις είναι

χρήσιμες σε ειδικές περιπτώσεις, αλλά δεν έχουν τη γενική χρήση της κατάστασης standard.

`Οταν το MPI εκτελεί ένα standard send, τότε ένα από τα εξής δύο πράγματα

μπορούν να συμβούν :

1. Το μήνυμα να αποθηκευθεί σε μία εσωτερική ενδιάμεση μνήμη του MPI (MPI internal

buffer) και να μεταφερθεί αργότερα στον προορισμό του.

2. Το μήνυμα να παραμείνει όπου βρίσκεται (δηλαδή, στις μεταβλητές του προγράμματος)

έως ότου η διεργασία-παραλήπτης είναι έτοιμη να το παραλάβει ή με άλλα λόγια, όταν

οι διεργασίες αποστολέα και παραλήπτη συγχρονιστούν. Εκείνη τη στιγμή, το μήνυμα

μεταφέρεται στον προορισμό του.

Στην πρώτη περίπτωση, η διαδικασία αποστολής ολοκληρώνεται αμέσως μετά την

αντιγραφή του μηνύματος στην εσωτερική ενδιάμεση μνήμη του MPI, ακόμη και αν το

μήνυμα δεν έχει φθάσει ακόμη στον προορισμό του ή ακόμη και αν δεν έχει ξεκινήσει

καμμία μη τοπική διαδικασία για την αποστολή του. Το μήνυμα παραμένει στην ενδιάμεση

μνήμη του MPI έως ότου η διεργασία-παραλήπτης αρχίσει να το παραλαμβάνει. Στην

δεύτερη περίπτωση, η διαδικασία αποστολής ολοκληρώνεται και η MPI_Send επιστρέφει

μόνον όταν συγχρονιστούν οι διεργασίες παραλήπτη και αποστολέα και αρχίσει η αποστολή

του μηνύματος.

Η παραπάνω έννοια της ολοκλήρωσης της διαδικασίας αποστολής παραμένει η ίδια

στην αναστέλλουσα και στη μη αναστέλλουσα επικοινωνία, ήτοι στις συναρτήσεις

MPI_Send και MPI_Isend. Η διαφορά βρίσκεται στο πότε επιστρέφουν οι δύο συναρτήσεις:

Η MPI_Send δεν επιστρέφει έως ότου ολοκληρωθεί η διαδικασία αποστολής, ενώ η

MPI_Isend ξεκινά τη διαδικασία αποστολής και επιστρέφει αμέσως. Η έννοια της

ολοκλήρωσης είναι πάντα η ίδια: το μήνυμα αντιγράφηκε στην εσωτερική ενδιάμεση μνήμη

του MPI, ή οι διεργασίες αποστολέα και παραλήπτη συγχρονίστηκαν για την αποστολή και

παραλαβή του μηνύματος. `Ενα από τα πλεονεκτήματα του standard send είναι ότι η επιλογή

μεταξύ της αντιγραφής του μηνύματος στην ενδιάμεση μνήμη του MPI (buffering) και του

συγχρονισμού των διεργασιών αφήνεται στο MPI, το οποίο αποφασίζει ανάλογα με την

περίπτωση.

4.8.2 Synchronous send

Η κατάσταση επικοινωνίας synchronous για την αποστολή μηνυμάτων (synchronous

send) μας δίνει τη δυνατότητα σύγχρονης επικοινωνίας, καθόσον απαιτεί οι διεργασίες

αποστολέα και παραλήπτη να συγχρονιστούν για τη μετάδοση του μηνύματος. Η διαδικασία

61

Page 62: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

αποστολής ολοκληρώνεται όταν η διεργασία-παραλήπτης αρχίσει να παραλαμβάνει το

μήνυμα, χωρίς να είναι απαραίτητο να έχει ολοκληρωθεί η παραλαβή του μηνύματος.

Προκειμένου να ολοκληρωθεί η διαδικασία αποστολής, η διεργασία-παραλήπτης στέλνει

μιαν απάντηση στην διαδικασία-αποστολέα (μια διαδικασία γνωστή ως χειραψία

(handshake). Η κλήση μιας synchronous send είναι μια μη τοπική κλήση.

Οι συναρτήσεις MPI_Ssend και MPI_Issend

Η αναστέλλουσα συνάρτηση σύγχρονης αποστολής μηνύματος MPI_Ssend έχει τις

ίδιες παραμέτρους με την αντίστοιχη συνάρτηση standard send:

int MPI_Ssend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm)

Η συνάρτηση MPI_Issend η οποία ξεκινάει μια μη αναστέλλουσα διαδικασία

αποστολής μηνύματος σε κατάσταση επικοινωνίας synchronous, έχει επίσης, τις ίδιες

παραμέτρους με την αντίστοιχη συνάρτηση standard send:

int MPI_Issend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm, MPI_Request *request)

Αν μία αναστέλλουσα synchronous send κλήση έχει ξεκινήσει πριν από την

αντίστοιχη receive, η send θα είναι ανενεργή μέχρι να συγχρονιστεί με τη receive.

Παρομοίως, αν έχει ξεκινήσει μια μη αναστέλλουσα synchronous send, ο έλεγχος για

ολοκλήρωση (completion test) δε θα επιστρέψει επιτυχώς, μέχρι να συγχρονιστεί το

αντίστοιχο receive. Όπως είναι φανερό, η ολοκλήρωση της synchronous send είναι πιθανόν

να καθυστερήσει περισσότερο από την standard send. Όμως, η συμπεριφορά της

synchronous send, σε σχέση με την standard send, είναι περισσότερο καθορισμένη. Μία

synchronous send πάντα συγχρονίζει τον αποστολέα και τον παραλήπτη, ενώ μια standard

send άλλοτε συγχρονίζει και άλλοτε όχι. Αυτό κάνει τη συμπεριφορά ενός προγράμματος

περισσότερο προβλέψιμη. Ακόμα, διευκολύνεται η διόρθωση των λαθών στο

πρόγραμμα,καθώς δεν υπάρχουν μηνύματα που δεν έχουν ληφθεί, στο δίκτυο. Τα

προβλήματα αδιεξόδων μπορούν να αποφευχθούν με τη χρήση μη αναστέλλουσας

σύγχρονης επικοινωνίας.

4.8.3 Buffered send

Η κατάσταση επικοινωνίας buffered για την αποστολή μηνυμάτων (buffered send)

απαιτεί προκειμένου να πραγματοποιηθεί μία διαδικασία αποστολής μηνύματος, το προς

αποστολή μήνυμα να αποθηκευθεί σε μια ενδιάμεση μνήμη η οποία παρέχεται από την

εφαρμογή. Εννοείται ότι η ενδιάμεση μνήμη θα πρέπει να έχει μέγεθος αρκετό για να

χωρέσει το μήνυμα. Η ενδιάμεση μνήμη απελευθερώνεται μόλις ολοκληρωθεί η επικοινωνία

ή όταν ακυρωθεί η μετάδοση πριν τελειώσει. Η buffered send ολοκληρώνεται αμέσως, αφού

62

Page 63: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

αντιγράψει το μήνυμα σε κάποια ενδιάμεση μνήμη του συστήματος για να μεταδοθεί

αργότερα, αν είναι απαραίτητο. Έτσι, ο αποστολέας και ο παραλήπτης δε χρειάζεται να είναι

συγχρονισμένοι. Το μειονέκτημα είναι ότι, ο προγραμματιστής δεν μπορεί να ξέρει αν

υπάρχει διαθέσιμη μια ενδιάμεση μνήμη και πρέπει να δεσμεύσει ο ίδιος χώρο γι’ αυτήν,

καλώντας τη συνάρτηση MPI_Buffer_attach. Η συνάρτηση αυτή θα περιγραφεί αργότερα.

Η συνάρτηση MPI_Ibsend

Η συνάρτηση MPI_Ibsend ξεκινάει μια μη αναστέλλουσα διαδικασία αποστολής

μηνύματος σε κατάσταση επικοινωνίας buffered. Η κλήση της είναι:

int MPI_Ibsend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm, MPI_Request *request)

Οι παράμετροι της και η σημασία τους είναι ίδιες με την MPI_Issend.

Οι συναρτήσεις MPI_Buffer_attach και MPI_Buffer_detach

Η συνάρτηση MPI_Buffer_attach δημιουργεί μια ενδιάμεση μνήμη για αποστολή

μηνυμάτων. Αυτή η ενδιάμεση μνήμη χρησιμοποιείται μόνο για αποστολή μηνυμάτων σε

buffered mode. Η ενδιάμεση αποθήκευση (buffering) του μηνύματος γίνεται από τον

αποστολέα. Η σύνταξη της είναι:

int MPI_Buffer_attach(void *buffer, int size)

Όπου:

buffer Η αρχική διεύθυνση της ενδιάμεσης μνήμης.

size Το μέγεθός της, σε bytes.

H buffer είναι ένας πίνακας τον οποίο ορίζει ο προγραμματιστής. Αυτός ο πίνακας

μπορεί να περιέχει δεδομένα οποιουδήποτε τύπου (π.χ., μια τυχαία ακολουθία από bytes) γι’

αυτό δηλώνεται ως void *. Έτσι, η διεργασία μπορεί να τον χρησιμοποιήσει όπως θέλει.

Κάθε διεργασία μπορεί να χρησιμοποιήσει μόνο μια ενδιάμεση μνήμη κάθε φορά που

εκτελείται. Δηλαδή, αν πρέπει να μεταβάλλουμε το μέγεθος της ενδιάμεσης μνήμης, θα

πρέπει πρώτα να αποδεσμεύσουμε την παλιά και μετά να δημιουργήσουμε την καινούργια.

H συνάρτηση MPI_Buffer_detach αποδεσμεύει την ενδιάμεση μνήμη η οποία είχε

δεσμευτεί από μία κλήση της MPI_Buffer_attach. Η μόνη διαφορά στην κλήση είναι ότι

τώρα η παράμετρος size είναι δείκτης. Η σύνταξή της είναι:

int MPI_Buffer_detach(void *buffer, int *size)

Η συνάρτηση επιστρέφει τη διεύθυνση καθώς και το μέγεθος της ενδιάμεσης μνήμης

που αποδεσμεύθηκε στις παραμέτρους buffer και size αντίστοιχα. Οι επικοινωνίες που ήδη

χρησιμοποιούν την ενδιάμεση μνήμη, ολοκληρώνονται πριν αυτή αποδεσμευτεί. Δηλαδή, η

63

Page 64: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

διεργασία που καλεί την MPI_Buffer_detach τίθεται σε κατάσταση αναστολής (blocking)

μέχρι να μεταδοθούν όλα τα μηνύματα που βρίσκονται στην ενδιάμεση μνήμη. Στη C, η

κλήση αυτή δεν αποδεσμεύει τη μνήμη που καταλαμβάνει ο πίνακας. Απλά, μεταβάλλει τη

δομή του έτσι ώστε να μην μπορεί πλέον να αποθηκεύσει μηνύματα. Όπως φαίνεται, ο

πίνακας μπορεί να είναι οποιουδήποτε τύπου (void *). Ακολουθεί ένα παράδειγμα κώδικα

που χρησιμοποιεί τις attach και detach:

#define BUFFSIZE 10000

int size;

char *buff;

buff = (char*)malloc(BUFFSIZE);

MPI_Buffer_attach(buff, BUFFSIZE);

/* Τώρα η διεργασία έχει μια ενδιάμεση μνήμη με μέγεθος 10000 bytes για να

χρησιμοποιηθεί από την MPI_Bsend */

MPI_Buffer_detach(&buff, &size);

/* Τώρα η ενδιάμεση μνήμη έχει μέγεθος 0 */

MPI_Buffer_attach(buff, size);

/* Η ενδιάμεση μνήμη είναι ξανά διαθέσιμη*/

Παρόλο που και οι δυο συναρτήσεις έχουν σαν τύπο του πρώτου ορίσματος void *,

αυτά τα ορίσματα χρησιμοποιούνται διαφορετικά. Στην MPI_Buffer_attach περνιέται ένας

δείκτης προς την ενδιάμεση μνήμη. Στην MPI_Buffer_detach περνιέται η διεύθυνση του

δείκτη. Έτσι, η MPI_Buffer_detach μπορεί να επιστρέψει τη διεύθυνση του δείκτη. Παρά τη

διαφορετική χρήση τους, και στις δυο συναρτήσεις το πρώτο όρισμα είναι void * (αντί για

void * και void ** αντίστοιχα) για να αποφευχθούν οι πολύπλοκες μετατροπές τύπου (type

cast) από την πλευρά του προγραμματιστή Στο προηγούμενο παράδειγμα, η &buff, η οποία

είναι τύπου char ** μπορεί να περαστεί σαν όρισμα στην MPI_Buffer_detach χωρίς

μετατροπή τύπου. Αν η τυπική παράμετρος (formal parameter) ήταν τύπου void ** τότε θα

χρειαζόταν μετατροπή τύπου πριν και μετά την κλήση.

4.8.4 Ready send

Η κατάσταση επικοινωνίας ready για την αποστολή μηνυμάτων (ready send) απαιτεί

πριν κληθεί μία συνάρτηση ready send από την διεργασία-αποστολέα, η διεργασία-

παραλήπτης να έχει καλέσει την αντίστοιχη συνάρτηση παραλαβής. Διαφορετικά, η

επικοινωνία είναι λανθασμένη και το αποτέλεσμά της ακαθόριστο. Σε μερικά συστήματα

αυτό μας δίνει τη δυνατότητα να μην χρησιμοποιήσουμε λειτουργίες χειραψίας (hand-shake)

και έτσι οδηγούμαστε σε καλύτερες επιδόσεις. Σε κάποια προγράμματα, μία ready send

μπορεί να αντικατασταθεί από μία standard send, χωρίς άλλες επιπτώσεις στη συμπεριφορά

του προγράμματος εκτός από τις επιδόσεις. Ο αποστολέας απλά, στέλνει το μήνυμα

ελπίζοντας ότι ο παραλήπτης περιμένει να το λάβει. Αν ο παραλήπτης είναι έτοιμος, θα το

λάβει. Διαφορετικά, το μήνυμα θα χαθεί ή θα προκύψει λάθος ανάλογα με τη συγκεκριμένη

υλοποίηση του MPI. Η κατάσταση επικοινωνίας ready χρησιμοποιείται μόνο σε εφαρμογές

64

Page 65: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

όπου πραγματικά υπάρχει βελτίωση των επιδόσεων λόγω της αποφυγής της χειραψίας. Τα

προγράμματα που χρησιμοποιούν την κατάσταση επικοινωνίας ready πρέπει να

σχεδιάζονται προσεκτικά γιατί είναι δύσκολο να διορθωθούν.

Η συνάρτηση MPI_Rsend

Η συνάρτηση MPI_Rsend ξεκινάει μια αναστέλλουσα διαδικασία αποστολής

μηνύματος σε κατάσταση επικοινωνίας ready. Η κλήση της είναι:

int MPI_Rsend (void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm)

65

Page 66: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

5. Συλλογική Επικοινωνία

5.1 Γενικά

`Οπως έχει αναφερθεί, εκτός από την από κόμβο σε κόμβο επικοινωνία στην οποία

εμπλέκονται δύο διεργασίες, το MPΙ υποστηρίζει επίσης, την συλλογική (collective)

επικοινωνία στην οποία μπορούν να εμπλέκονται και περισσότερες των δύο διεργασιών.

Θεωρητικά, για την πραγματοποίηση της επικοινωνίας πολλών διεργασιών, μπορούν να

χρησιμοποιηθούν οι συναρτήσεις send και receive που χρησιμοποιούνται στην από κόμβο σε

κόμβο επικοινωνία. Με άλλα λόγια, μπορούν να χρησιμοποιηθούν «πολλές από κόμβο σε

κόμβο επικοινωνίες». Όμως, έτσι αυξάνεται η πολυπλοκότητα του κώδικα. Αν για

παράδειγμα, πρέπει μία διεργασία να στείλει το ίδιο μήνυμα σε 100 άλλες διεργασίες, θα

χρειαστούν 100 κλήσεις από κόμβο σε κόμβο επικοινωνίας. Θα ήταν πολύ πιο εύκολο αν

ήταν δυνατόν η αποστολή του μηνύματος στις 100 διεργασίες να γίνει με την κλήση μιας

μόνο συνάρτησης.

Σ’ αυτές τις περιπτώσεις, χρησιμοποιείται η συλλογική επικοινωνία. Έτσι,

επιτυγχάνουμε την αποστολή ενός μηνύματος σε πολλούς παραλήπτες ή την παραλαβή ενός

μηνύματος από πολλούς αποστολείς, με μία μόνον κλήση συνάρτησης συλλογικής

επικοινωνίας. Οι συναρτήσεις συλλογικής επικοινωνίας κατασκευάζονται με τη χρήση

συναρτήσεων της από κόμβο σε κόμβο επικοινωνίας. Αν και κάποιος θα μπορούσε να

κατασκευάσει τις δικές του συναρτήσεις συλλογικής επικοινωνίας, οι συναρτήσεις που

παρέχει το MPI, κρύβουν πολλές και περίπλοκες λεπτομέρειες κατασκευής τους, και

επιπλέον, υλοποιούν τους πιο αποδοτικούς αλγορίθμους που είναι γνωστοί για τις

λειτουργίες που υποστηρίζουν.

5.2 Είδη συναρτήσεων συλλογικής επικοινωνίας

Με τη χρήση των συναρτήσεων συλλογικής επικοινωνίας πετυχαίνουμε τη μετάδοση

δεδομένων σε όλες τις διεργασίες μιας ομάδας (group) η οποία σχετίζεται με κάποιον

communicator. Μια συνάρτηση, η MPI_Barrier, χρησιμοποιείται για να συγχρονίζει τις

διεργασίες μιας ομάδας χωρίς στην πραγματικότητα, να μεταδίδει μηνύματα.

Το MPI παρέχει τις παρακάτω συναρτήσεις για συλλογική επικοινωνία.

66

Page 67: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Συνάρτηση μετάδοσης δεδομένων από μία διεργασία μιας ομάδας σε όλες τις υπόλοιπες

διεργασίες της ομάδας (Broadcast).

Συνάρτηση συγκέντρωσης δεδομένων από όλες τις διεργασίες μιας ομάδας σε μία

διεργασία της ομάδας (Gather).

Συνάρτηση μετάδοσης (διασκορπισμού) δεδομένων από μία διεργασία μιας ομάδας σε

όλες τις διεργασίες της ομάδας (Scatter).

Συνάρτηση συγκέντρωσης δεδομένων από όλες τις διεργασίες μιας ομάδας σε μία

διεργασία της ομάδας όπου και αποθηκεύεται το αποτέλεσμα μιας πράξης πάνω σε αυτά

τα δεδομένα (Reduce). Οι πράξεις που υποστηρίζονται συμπεριλαμβάνουν: το

άθροισμα, το γινόμενο, το λογικό ή, το λογικό και, το μέγιστο, και το ελάχιστο.

Παραλλαγές των παραπάνω συναρτήσεων όπου όλες οι διεργασίες συγκεντρώνουν

δεδομένα από όλες τις διεργασίες.

Οι περισσότερες συναρτήσεις συλλογικής επικοινωνίας υπάρχουν σε δυο παραλλαγές.

Η «απλή», στην οποία όλα τα μηνύματα που στέλνονται ή λαμβάνονται από μία διεργασία ,

έχουν το ίδιο μέγεθος, και η «διανυσματική» στην οποία κάθε μήνυμα μπορεί να έχει

διαφορετικό μέγεθος. Στην απλή έκδοση της συνάρτησης, αν έχουμε πολλά μηνύματα που

στέλνονται ή λαμβάνονται από μία διεργασία, αυτά πρέπει να βρίσκονται σε συνεχόμενες

διευθύνσεις στην μνήμη. Η διανυσματική παραλλαγή επιτρέπει τα μηνύματα να βρίσκονται

και σε μη διαδοχικές διευθύνσεις.

Μερικές από τις συναρτήσεις συλλογικής επικοινωνίας, όπως για παράδειγμα, η

broadcast και η gather, έχουν μόνο μια διεργασία που στέλνει ή μόνο μια διεργασία που

λαμβάνει. Αυτή η διεργασία ονομάζεται ρίζα (root).

Οι συναρτήσεις συλλογικής επικοινωνίας χωρίζονται σε τρεις κατηγορίες:

Η διεργασία-ρίζα στέλνει δεδομένα σε όλες τις άλλες (συμπεριλαμβάνοντας και την ίδια

τη διεργασία-ρίζα). Παράδειγμα τέτοιας συνάρτησης είναι η scatter.

Η διεργασία-ρίζα λαμβάνει δεδομένα από όλες τις άλλες διεργασίες

(συμπεριλαμβάνοντας και την ίδια τη διεργασία-ρίζα). Παράδειγμα τέτοιας συνάρτησης

είναι η gather.

Όλες οι διεργασίες επικοινωνούν με όλες τις άλλες. Παραδείγματα τέτοιας συνάρτησης

είναι οι allgather και alltoall.

5.3 Σύγκριση point-to-point και συλλογικής επικοινωνίας

Οι συναρτήσεις συλλογικής επικοινωνίας του MPI έχουν περισσότερους περιορισμούς

στη χρήση τους από τις αντίστοιχες συναρτήσεις από κόμβο σε κόμβο επικοινωνίας. Για

παράδειγμα, η ποσότητα των δεδομένων που αποστέλλονται πρέπει να είναι ακριβώς η ίδια

με αυτή που καθορίζεται στη διεργασία-παραλήπτη. Στη συλλογική επικοινωνία υπάρχουν

μόνον αναστέλλουσες (blocking) κλήσεις συναρτήσεων. Ακόμα, δεν υπάρχει όρισμα tag στις

συναρτήσεις. Επομένως, μέσα σε κάθε ομάδα οι send και receive κλήσεις αντιστοιχίζονται

σύμφωνα με τη σειρά της εκτέλεσής τους.

Στις περισσότερες υλοποιήσεις του MPI υπάρχει μόνο μία κατάσταση επικοινωνίας

(communication mode) που μπορεί να θεωρηθεί αντίστοιχο με τη standard από κόμβο σε

67

Page 68: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

κόμβο επικοινωνία. Επομένως, μια συλλογική επικοινωνία δεν συγχρονίζει απαραίτητα, τις

εμπλεκόμενες διεργασίες. Ωστόσο, σε αρκετές υλοποιήσεις του MPI, οι κλήσεις σε

συναρτήσεις συλλογικής επικοινωνίας συγχρονίζουν τις εμπλεκόμενες διεργασίαες. Εν

τούτοις, επειδή ο συγχρονισμός δεν είναι δεδομένος σε κάθε υλοποίηση, ο προγραμματιστής

δεν πρέπει να βασίζεται σε αυτόν, ακόμη και αν μια συγκεκριμένη υλοποίηση του MPI

παρέχει συγχρονισμό. Διαφορετικά, η εφαρμογή του δεν θα είναι μεταφέρσιμη σε

υλοποιήσεις του MPI οι οποίες δεν παρέχουν συγχρονισμό. Το MPI παρέχει τη συνάρτηση

MPI_barrier η οποία δίνει τη δυνατότητα συγχρονισμού των διεργασιών που θα

προσδιορίσει ο προγραμματιστής.

Σε μια συλλογική επικοινωνία συμμετέχουν απαραίτητα, όλες οι διεργασίες του

communicator (δηλαδή της ομάδας που αντιστοιχεί στο συγκεκριμένο communicator). Αν

θέλουμε να χρησιμοποιήσουμε μερικές μόνο από τις διεργασίες για μια συλλογική

επικοινωνία, θα πρέπει να δημιουργήσουμε έναν καινούργιο communicator ο οποίος θα

περιλαμβάνει μόνον αυτές τις διεργασίες. Συμμετοχή όλων των διεργασιών σημαίνει ότι όλες

πρέπει να καλέσουν την ίδια συνάρτηση συλλογικής επικοινωνίας. Παρ’ όλα αυτά, μερικές

παράμετροι κλήσης δεν χρησιμοποιούνται από όλες τις διεργασίες. Συνήθως, η διεργασία-

ρίζα χρησιμοποιεί όλες τις παραμέτρους, ενώ οι υπόλοιπες διεργασίες αγνοούν κάποιες

παραμέτρους.

Οι συλλογικές και οι από κόμβο σε κόμβο επικοινωνίες δεν εμπλέκονται μεταξύ τους.

Μπορούμε στο ίδιο MPI πρόγραμμα να χρησιμοποιήσουμε και τα δύο είδη επικοινωνίας,

5.4 Συναρτήσεις συλλογικής επικοινωνίας

Η συνάρτηση MPI_Barrier

Χρησιμοποιείται για να συγχρονίσει όλες τις διεργασίες οι οποίες συμμετέχουν σε μια

συλλογική επικοινωνία. Προκαλεί αναστολή της εκτέλεσης της διεργασίας που την καλεί,

καθώς επίσης, και όλων των υπολοίπων διεργασιών του communicator. Επιστρέφει μόνον

όταν την καλέσουν όλες οι υπόλοιπες διεργασίες. Η σύνταξή της είναι:

int MPI_Barrier(MPI_Comm comm)

comm είναι ο communicator της συλλογικής επικοινωνίας. Η συνάρτηση δεν μεταδίδει

μήνυμα. Όταν όμως επιστρέψει, ξέρουμε ότι όλες οι διεργασίες έχουν φτάσει στο ίδιο

σημείο στην εκτέλεση του κώδικα.

Η συνάρτηση MPI_Bcast

Αποστέλλει το ίδιο μήνυμα από τη διεργασία-ρίζα σε όλες τις υπόλοιπες διεργασίες. Η

κλήση της είναι:

68

Page 69: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root,

MPI_Comm comm)

Όπου:

buffer Η διεύθυνση της ενδιάμεσης μνήμης που περιέχει το προς αποστολή

μήνυμα.

count Ο αριθμός των στοιχείων που υπάρχουν στην ενδιάμεση μνήμη.

datatype Ο τύπος δεδομένων της ενδιάμεσης μνήμης.

root Η τάξη της διεργασίας-ρίζας.

comm Ο communicator της συλλογικής επικοινωνίας.

Η κλήση της κάνει δημόσια εκπομπή (broadcast) των περιεχομένων της ενδιάμεσης

μνήμης της διεργασίας-ρίζα στην ενδιάμεση μνήμη που έχει οριστεί σε κάθε μια διεργασία

του communicator. Όταν επιστρέψει, όλες οι ενδιάμεσες μνήμες των διεργασιών έχουν τα

ίδια περιεχόμενα. Οι παράμετροι root και comm πρέπει να έχουν την ίδια τιμή σε όλες τις

διεργασίες. Το ίδιο ισχύει για την παράμετρο datatype. Η ποσότητα των δεδομένων που

αποστέλλονται πρέπει να είναι η ίδια για κάθε ζευγάρι αποστολέα-παραλήπτη που

σχηματίζει η ρίζα και κάποια άλλη διεργασίας.

Ακολουθεί ένα πρόγραμμα στο οποίο φαίνεται η χρήση της MPI_Bcast.

#include "mpi.h"

#include <stdio.h>

int main(argc,argv)

int argc;

char *argv[ ];

{

double var;

MPI_Init(&argc,&argv);

MPI_Comm_rank(MPI_COMM_WORLD, &rank);

if (rank == 1) var = 10.5;

MPI_Bcast(&constant, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

Printf(“ Στη Διεργασία %d η τιμή της μεταβλητής είναι %f \n”, rank, var);

MPI_Finalize();

}

Μία πιθανή έξοδος του προγράμματος για 6 διεργασίες είναι:

Στη Διεργασία 0 η τιμή της μεταβλητής είναι 10.5

Στη Διεργασία 1 η τιμή της μεταβλητής είναι 10.5

Στη Διεργασία 3 η τιμή της μεταβλητής είναι 10.5

Στη Διεργασία 5 η τιμή της μεταβλητής είναι 10.5

Στη Διεργασία 2 η τιμή της μεταβλητής είναι 10.5

Στη Διεργασία 4 η τιμή της μεταβλητής είναι 10.5

69

Page 70: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Η συνάρτηση MPI_Gather

`Οταν καλείται η MPI_Gather, κάθε διεργασία (συμπεριλαμβανομένης και της

διεργασίας-ρίζας) στέλνει τα περιεχόμενα της ενδιάμεσης μνήμης αποστολής (send buffer)

στη διεργασία-ρίζα. Η διεργασία-ρίζα λαμβάνει τα μηνύματα και τα αποθηκεύει στην

ενδιάμεση μνήμη της κατά αύξουσα σειρά της τάξης της διεργασίας-αποστολέα.. Η κλήση

της είναι:

int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf,

int recvcount, MPI_Datatype recvtype, int root MPI_Comm comm)

Όπου:

sendbuf Η διεύθυνση αρχής της ενδιάμεσης μνήμης αποστολής.

sendcount Ο αριθμός των στοιχείων στην ενδιάμεση μνήμη αποστολής.

sendtype Ο τύπος των δεδομένων που περιέχονται στην ενδιάμεση μνήμη

αποστολής sendbuf.

recvbuf Η διεύθυνση αρχής της ενδιάμεσης μνήμης λήψης.

recvcount Ο αριθμός των στοιχείων που λαμβάνονται από κάθε διεργασία.

recvtype Ο τύπος των δεδομένων που περιέχονται στην ενδιάμεση μνήμη

λήψης recvbuf.

root Η τάξη της διεργασίας-παραλήπτη (δηλαδή της ρίζας).

comm Ο communicator στον οποίο ανήκουν οι διεργασίες.

Η κλήση της MPI_Gather είναι λειτουργικά ισοδύναμη με την κλήση της

MPI_Send(sendbuf, sendcount, sendtype, root, …) από κάθε μια από τις n διεργασίες-

αποστολέα, ακολουθούμενη από n κλήσεις της MPI_Recv(recvbuf + i * recvcount,

recvcount, recvtype, i, …) από τη διεργασία-ρίζα. Τελικά, ο παραλήπτης λαμβάνει ένα

μεγάλο σε μέγεθος μήνυμα το οποίο περιέχει όλα τα επιμέρους μηνύματα των υπολοίπων

διεργασιών. Η παράμετρος ενδιάμεση μνήμη λήψης (δηλαδή, το recvbuf) χρησιμοποιείται

μόνον από τη διεργασία-ρίζα και αγνοείται από τις υπόλοιπες διεργασίες. Ισχύει και εδώ ο

ίδιος περιορισμός με την MPI_Bcast. Δηλαδή, η ποσότητα των δεδομένων που

αποστέλλονται, πρέπει να είναι η ίδια για κάθε ζευγάρι αποστολέα-παραλήπτη (της ρίζας και

κάποιας άλλης διεργασίας).

Η διεργασία-ρίζα χρησιμοποιεί όλες τις παραμέτρους, ενώ οι υπόλοιπες διεργασίες

χρησιμοποιούν μόνο τις sendbuf, sendcount, sendtype, root και comm. Οι παράμετροι root

και comm πρέπει να έχουν την ίδια τιμή σε όλες τις διεργασίες που επικοινωνούν.

Ακολουθεί ένα παράδειγμα χρήσης της MPI_Gather. Η διεργασία-ρίζα λαμβάνει 100

ακέραιους από τις υπόλοιπες διεργασίες της ομάδας και τους αποθηκεύει στην ενδιάμεση

μνήμη λήψης της ρίζας.

MPI_Comm comm;

int gsize, sendarraty[100];

int root, *rbuf;

70

Page 71: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

/* Εκχώρηση τιμών στον πίνακα*/

. . .

MPI_Comm_size(comm, &gsize);

rbuf = (int *) malloc(100*gsize*sizeof(int));

MPI_Gather(sendarray, 100, MPI_INT, rbuf, 100, MPI_INT, root, comm);

Η συνάρτηση MPI_Scatter

Η MPI_Scatter κάνει την αντίστροφη δουλειά από την MPI_Gather. Δηλαδή, η

διεργασία-ρίζα διαμοιράζει ένα μήνυμα σε πολλές διεργασίες κάθε μια από τις οποίες,

λαμβάνει ένα μέρος του μηνύματος. Το μήνυμα χωρίζεται σε ίσα τμήματα πριν αποσταλεί

στους παραλήπτες του. Η κλήση της συνάρτησης είναι:

int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf,

int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm)

Όπου:

sendbuf Η διεύθυνση αρχής της ενδιάμεσης μνήμης αποστολής.

sendcount Ο αριθμός των στοιχείων που αποστέλλονται σε κάθε διεργασία.

sendtype Ο τύπος των δεδομένων που περιέχονται στην ενδιάμεση μνήμη

αποστολής sendbuf.

recvbuf Η διεύθυνση αρχής της ενδιάμεσης μνήμης λήψης.

recvcount Ο αριθμός των στοιχείων που λαμβάνονται από κάθε διεργασία.

recvtype Ο τύπος των δεδομένων που περιέχονται στην ενδιάμεση μνήμη

λήψης recvbuf.

root Η τάξη της διεργασίας-αποστολέα (δηλαδή της ρίζας).

comm Ο communicator στον οποίο ανήκουν οι διεργασίες.

Η κλήση της MPI_Scatter είναι λειτουργικά ισοδύναμη με n κλήσεις της

MPI_Send(sendbuf + i*sendcount, sendcount, sendtype, i, …), για i = 0 έως n – 1 από τη

διεργασία-ρίζα, ακολουθούμενη από μια κλήση της MPI_Recv(recvbuf, recvcount, recvtype,

root, … ) από κάθε μία από τις υπόλοιπες διεργασίες-παραλήπτες. Τελικά, η ενδιάμεση

μνήμη κάθε διεργασίας περιέχει ένα μέρος του μηνύματος που εστάλη από τη διεργασία-

ρίζα. Οι τιμές των παραμέτρων sendcount και sendtype που έχουν καθοριστεί στη διεργασία-

ρίζα πρέπει να είναι ίδιες με τις αντίστοιχες παραμέτρους recvcount και recvtype που έχουν

καθοριστεί στις διεργασίες-παραλήπτες. Το ίδιο ισχύει και για τις παραμέτρους root και

comm.

Η διεργασία-ρίζα χρησιμοποιεί όλες τις παραμέτρους που περνιούνται στη συνάρτηση,

ενώ οι υπόλοιπες διεργασίες χρησιμοποιούν μόνον τις recvbuf, recvcount, recvtype, root και

comm. Η ενδιάμεση μνήμη αποστολής αγνοείται. Ακολουθεί ένα παράδειγμα χρήσης της

MPI_Scatter. Εδώ, έχουμε αποστολή 100 ακεραίων από τη διεργασία-ρίζα στις υπόλοιπες

διεργασίες της ομάδας.

MPI_Comm comm;

int gsize, *sendbuf;

71

Page 72: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

int root, rbuf[100];

/* Εκχώρηση τιμών στον sendbuf */

. . .

MPI_Comm_size(comm, &gsize);

sendrbuf = (int *) malloc(100*gsize*sizeof(int));

MPI_Scatter(sendarray, 100, MPI_INT, rbuf, 100, MPI_INT, root, comm);

Η συνάρτηση MPI_Allgather

Η συνάρτηση είναι παρόμοια με την MPI_Gather με τη διαφορά ότι το μήνυμα που

στέλνει κάθε διεργασία, λαμβάνεται από όλες τις διεργασίες που συμμετέχουν, όχι μόνο από

τη ρίζα. Κάθε διεργασία λαμβάνει μηνύματα από όλες τις διεργασίες (συπεριλαμβανομένου

του εαυτού της) και τα αποθηκεύει στην ενδιάμεση μνήμη της κατά αύξουσα σειρά της

τάξης της διεργασίας-αποστολέα.. Η κλήση της ακολουθεί:

int MPI_Allgather(void *sendbuf, int sendcount, MPI_Datatype sendtype,

void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm)

Όπου:

sendbuf Η διεύθυνση αρχής της ενδιάμεσης μνήμης αποστολής.

sendcount Ο αριθμός των στοιχείων που περιέχονται στο sendbuf.

sendtype Τύπος δεδομένων που περιέχει το sendbuf.

recvbuf Η διεύθυνση αρχής της ενδιάμεσης μνήμης λήψης.

recvcount Αριθμός στοιχείων που λαμβάνονται από κάθε διεργασία.

recvtype Τύπος δεδομένων που περιέχει το recvbuf.

comm Ο communicator των διεργασιών που επικοινωνούν.

Οι παράμετροι sendcount και sendtype σε μια διεργασία πρέπει να έχουν την ίδια τιμή

με τις recvcount και recvtype που καθορίζονται σε μια οποιαδήποτε άλλη διεργασία. Το

αποτέλεσμα της κλήσης της MPI_Allgather(. . .) είναι το ίδιο με το αποτέλεσμα n κλήσεων

της MPI_Gather(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, comm)

από όλες τις διεργασίες, για root = 0, . . ., n – 1. Οι κανόνες για τη σωστή χρήση της

MPI_Allgather είναι παρόμοιοι με τους αντίστοιχους κανόνες για τη σωστή χρήση της

MPI_Gather. Παρατηρούμε ότι δεν υπάρχει παράμετρος root επειδή το αποτέλεσμα

λαμβάνεται από όλες τις διεργασίες. Ακολουθεί ένα παράδειγμα κώδικα που χρησιμοποιεί

την MPI_Allgather. Εδώ, έχουμε λήψη 100 ακεραίων από κάθε διεργασία στην ομάδα σε

οποιαδήποτε άλλη.

MPI_Comm comm;

int gsize, sendarraty[100];

int *rbuf;

/* Εκχώρηση τιμών στον πίνακα*/

. . .

72

Page 73: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

MPI_Comm_size(comm, &gsize);

rbuf = (int *) malloc(100*gsize*sizeof(int));

MPI_Allgather(sendarray, 100, MPI_INT, rbuf, 100, MPI_INT, comm);

Μετά την κλήση, κάθε διεργασία περιέχει στη δική της ενδιάμεση μνήμη το μήνυμα

που προκύπτει από τη συνένωση των επιμέρους μηνυμάτων από όλες τις διεργασίες.

Η συνάρτηση MPI_Alltoall

Πρόκειται για μια βελτιωμένη έκδοση της προηγούμενης συνάρτησης, με την έννοια

ότι κάθε διεργασία στέλνει διαφορετικά δεδομένα στους παραλήπτες. Η ποσότητα των

δεδομένων που αποστέλλονται πρέπει να είναι η ίδια με την ποσότητα των δεδομένων που

λαμβάνονται για κάθε ζευγάρι διεργασιών. Η κλήση της έχει τις ίδιες παραμέτρους με την

MPI_Allgather.

int MPI_Alltoall(void *sendbuf, int sendcount, MPI_Datatype sendtype,

void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm)

Όπου:

sendbuf Η διεύθυνση αρχής της ενδιάμεσης μνήμης αποστολής.

sendcount Ο αριθμός των στοιχείων που αποστέλλονται σε κάθε διεργασία.

sendtype Τύπος δεδομένων που περιέχει το sendbuf.

recvbuf Η διεύθυνση αρχής της ενδιάμεσης μνήμης λήψης.

recvcount Ο αριθμός των που λαμβάνονται από κάθε διεργασία.

recvtype Τύπος δεδομένων που περιέχει το recvbuf.

comm Ο communicator των διεργασιών που επικοινωνούν.

Το αποτέλεσμα της κλήσης είναι λειτουργικά ισοδύναμο με την κλήση της

MPI_Send(sendbuf + i * sendcount, sendcount, sendtype, i , . . .) από κάθε διεργασία σε

κάθε άλλη, ακολουθούμενη από μια κλήση της MPI_Recv(recvbuf + i * recvcount,

recvcount, i, . . .) από όλες τις διεργασίες, όπου i = 0, . . . , n. Όλες οι παράμετροι

χρησιμοποιούνται από όλες τις διεργασίες. Παρατηρούμε ότι δεν υπάρχει παράμετρος root.

5.5 Παράδειγμα συλλογικής επικοινωνίας

Το παρακάτω πρόγραμμα αποστέλλει τα στοιχεία ενός πίνακα ακεραίων 1010 σε 10

διεργασίες χρησιμοποιώντας την MPI_Scatter. Κάθε διεργασία λαμβάνει μια γραμμή του

αρχικού πίνακα. Έπειτα, γίνεται έλεγχος αν τα δεδομένα ελήφθησαν σωστά.

#include "mpi.h"

#include <stdio.h>

#define MAX_PROCESSES 10

73

Page 74: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

void Test_Waitforall( )

{

int m, one, myrank, n;

MPI_Comm_rank( MPI_COMM_WORLD, &myrank );

MPI_Comm_size( MPI_COMM_WORLD, &n );

one = 1;

MPI_Allreduce(&one,&m, 1, MPI_INT, MPI_SUM,

MPI_COMM_WORLD );

if (m != n) {

printf( "[%d] Expected %d processes to wait at end, got %d\n", myrank,

n, m );

}

if (myrank == 0)

printf( "All processes completed test\n" );

}

int main( int argc, char **argv )

{

int rank, size, i,j;

int table[MAX_PROCESSES][MAX_PROCESSES];

int row[MAX_PROCESSES];

int errors=0;

int participants;

MPI_Init( &argc, &argv );

MPI_Comm_rank( MPI_COMM_WORLD, &rank );

MPI_Comm_size( MPI_COMM_WORLD, &size );

/* A maximum of MAX_PROCESSES processes can participate */

if ( size > MAX_PROCESSES ) participants = MAX_PROCESSES;

else participants = size;

if ( (rank < participants) ) {

int send_count = MAX_PROCESSES;

int recv_count = MAX_PROCESSES;

/* If I'm the root (process 0), then fill out the big table */

if (rank == 0)

for ( i=0; i<participants; i++)

for ( j=0; j<MAX_PROCESSES; j++ )

table[i][j] = i+j;

/* Scatter the big table to everybody's little table */

MPI_Scatter(&table[0][0], send_count, MPI_INT,

74

Page 75: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

&row[0], recv_count, MPI_INT, 0, MPI_COMM_WORLD);

/* Now see if our row looks right */

for (i=0; i<MAX_PROCESSES; i++)

if ( row[i] != i+rank ) errors++;

}

Test_Waitforall( );

MPI_Finalize();

if (errors)

printf( "[%d] done with ERRORS(%d)!\n", rank, errors );

return errors;

}

Όπως είναι φανερό, η εκτέλεση του προγράμματος γίνεται με 10 διεργασίες το πολύ.

Φυσικά, είναι εύκολη η μετατροπή του έτσι ώστε να εκτελείται και με περισσότερες.

75

Page 76: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

6. Εικονικές Τοπολογίες

6.1 Γενικά

Μία εικονική (ή ιδεατή) τοπολογία (virtual topology) είναι ένας μηχανισμός

αρίθμησης των διεργασιών ενός communicator με έναν τέτοιο τρόπο, ώστε η αρίθμηση αυτή

να αντιστοιχεί σε μία διάταξη των διεργασιών η οποία να είναι περισσότερο συμβατή με την

διάταξη επικοινωνίας (communication pattern) των διεργασιών. Αν για παράδειγμα, κάθε

διεργασία ενός communicator επικοινωνεί με δύο ή τρεις διεργασίες σύμφωνα με μια

διάταξη επικοινωνίας που αντιστοιχεί στη δομή ενός δυαδικού δένδρου, τότε θα

μπορούσαμε να δημιουργήσουμε μία εικονική δενδρική τοπολογία για να απεικονήσουμε τη

διάταξη επικοινωνίας των διεργασιών.

Ο κύριος σκοπός δημιουργίας εικονικών τοπολογιών είναι η απλούστευση του

κώδικα. Η δημιουργία εικονικών τοπολογιών δίνει στον προγραμματιστή τη δυνατότητα

χρήσης κατάλληλων συναρτήσεων, όπως για παράδειγμα, μία συνάρτηση υπολογισμού των

τάξεων των κοντινότερων γειτόνων-διεργασιών μιας διεργασίας σε μία εικονική τοπολογία

πλέγματος. Οι τάξεις αυτές μπορούν στη συνέχεια, να χρησιμοποιηθούν ως παράμετροι, σε

κλήσεις συναρτήσεων αποστολής ή/και παραλαβής δεδομένων.

Θα πρέπει να σημειωθεί ότι μία εικονική τοπολογία απεικονίζει τη βασική διάταξη

επικοινωνίας (communication pattern) μεταξύ των διεργασιών ενός communicator. Η

δημιουργία μιας εικονικής τοπολογίας δεν εμποδίζει όλες τις διεργασίες του communicator

να μπορούν να επικοινωνούν μεταξύ τους.

Το MPI παρέχει συναρτήσεις για τη δημιουργία και τη διαχείρηση δύο ειδών

εικονικών τοπολογιών: Τις καρτεσιανές εικονικές τοπολογίες (cartesian virtual topologies)

και τις εικονικές τοπολογίες γραφημάτων (graph virtual topologies). Στις καρτεσιανές

εικονικές τοπολογίες κάθε διεργασία είναι «συνδεδεμένη» με τους γείτονές της σε μία

διάταξη εικονικού πλέγματος (με ή χωρίς εξωτερικές συνδέσεις). Η τάξη κάθε διεργασίας

εξαρτάται από τη θέση της στο πλέγμα και υπάρχει η έννοια των συντεταγμένων μιας

διεργασίας. Στις εικονικές τοπολογίες γραφημάτων κάθε διεργασία μπορεί να είναι

«συνδεδεδένη» με οποιεσδήποτε άλλες διεργασίες. Η αρίθμηση των διεργασιών είναι

αυθαίρετη, και φυσικά, δεν υπάρχει η έννοια των συντεταγμένων μιας διεργασίας. Στη

συνέχεια, θα συζητήσουμε μόνο για τις καρτεσιανές ιδεατές τοπολογίες και κάποιες

συναρτήσεις των τοπολογιών αυτών.

76

Page 77: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

6.2 Βασικές συναρτήσεις διαχείρησης καρτεσιανών τοπολογιών

Μερικές από τις βασικές συναρτήσεις καρτεσιανής εικονικής τοπολογίας που παρέχει

το MPI είναι οι εξής:

MPI_Cart_create

MPI_Cart_rank

MPI_Cart_coords

MPI_Cart_sub

MPI_Cartdim_get

MPI_Cart_get

Η συνάρτηση MPI_Cart_create

int MPI_Cart_create (MPI_Comm old_comm, int ndims, int *dims, int *periods,

int reorder, MPI_Comm *cart_comm)

Η συνάρτηση παίρνει ως παράμετρο έναν υπάρχοντα communicator old_comm και

επιστρέφει ένα νέο communicator cart_comm. Η βασική διάταξη επικοινωνίας μεταξύ των

διεργασιών του cart_comm αντιστοιχεί στη δομή μιας καρτεσιανής τοπολογίας. Η

παράμετρος ndims περιέχει τον αριθμό των διαστάσεων της εικονικής τοπολογίας. O

αριθμός των διεργασιών σε κάθε διάσταση περιέχεται στον πίνακα dims, ενώ ο πίνακας

periods περιέχει επίσης, ένα στοιχείο για κάθε διάσταση της τοπολογίας. `Ενα στοιχείο του

πίνακα periods είναι TRUE αν η αντίστοιχη διάσταση έχει εξωτερική ακμή, δηλαδή, η

αντίστοιχη στήλη του πλέγματος σχηματίζει δακτύλιο (ring). Διαφορετικά, το στοιχείο αυτό

είναι FALSE. Η παράμετρος reorder μπορεί να πάρει την τιμή TRUE ή FALSE.

Συγκεκριμένα, η reorder παίρνει την τιμή

FALSE αν τα δεδομένα έχουν ήδη κατανεμηθεί στις διεργασίες. Στην περίπτωση αυτή, η

τάξη κάθε διεργασίας στον νέο communicator (cart_comm) παραμένει η ίδια με αυτήν

του παλιού communicator (old_comm). Αυτό που κερδίζουμε είναι ότι, μπορούμε να

έχουμε πρόσβαση στις υπόλοιπες συναρτήσεις χειρισμού της καρτεσιανής εικονικής

τοπολογίας που δημιουργήθηκε.

TRUE αν τα δεδομένα δεν έχουν ακόμη κατανεμηθεί στις διεργασίες. Στην περίπτωση

αυτή, η τάξη κάθε διεργασίας στον νέο communicator (cart_comm) μπορεί να είναι

διαφορετική από αυτήν του παλιού communicator (old_comm). Ο νέος communicator

αναλαμβάνει να διαμοιράσει τα δεδομένα στις διεργασίες.

Η MPI_Cart_create είναι μία συνάρτηση συλλογικής επικοινωνίας. Πρέπει να καλείται

από όλες τις διεργασίες της ομάδας που αντιστοιχεί στον communicator. `Οπως όλες οι

συναρτήσεις συλλογικής επικοινωνίας, χρησιμοποιεί αναστέλλουσα επικοινωνία. Ωστόσο,

μπορεί να υπάρξει ή μπορεί να μην υπάρξει συγχρονισμός μεταξύ των διεργασιών. Η

77

Page 78: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

συνάρτηση MPI_Topo_test μπορεί να χρησιμοποιηθεί για να ελεγχθεί εάν μία εικονική

τοπολογία έχει συσχετισθεί μέ έναν communicator.

Αν το μέγεθος του πλέγματος είναι μικρότερο από τον αριθμό των διαθέσιμων

διεργασιών, οι διεργασίες που δεν περιλαμβάνονται στο νέο communicator, επιστρέφουν

MPI_COMM_NULL. Αν το μέγεθος του πλέγματος είναι μεγαλύτερο από τον αριθμό των

διαθέσιμων διεργασιών, τότε η κλήση της συνάρτησης επιστρέφει λάθος.

Οι συναρτήσεις MPI_CART_RANK και MPI_CART_COORDS

Η συνάρτηση MPI_Cart_rank επιστρέφει την τάξη μιας διεργασίας της οποίας οι

συντεταγμένες στην ιδεατή καρτεσιανή τοπολογία είναι γνωστές, προκειμένου για

παράδειγμα, να χρησιμοποιηθεί ως παράμετρος σε μία συνάρτηση αποστολής ή παραλαβής

μηνύματος.

int MPI_Cart_rank(MPI_Comm cart_comm, int *coords, int *rank)

Η αντίστροφη συνάρτηση MPI_Cart_coords επιστρέφει τις καρτεσιανές

συντεταγμένες μιας διεργασίας της οποίας η τάξη είναι γνωστή. Η συνάρτηση μπορεί για

παράδειγμα, να χρησιμοποιηθεί στον προσδιορισμό των καρτεσιανών συντεταγμένων μιας

συγκεκριμένης διεργασίας από την οποία μόλις παρελήφθη ένα μήνυμα.

int MPI_Cart_coords(MPI_Comm cart_comm, int rank, int maxdims, int *coords)

Η παράμετρος maxdims κρατάει τον αριθμό των διαστάσεων της καρτεσιανής εικονικής

τοπολογίας.

Η συνάρτηση MPI_Cart_sub

Μία κλήση της MPI_Cart_sub δημιουργεί communicators με δομή υποπλέγματος Ν-

1 διαστάσεων το πολύ, από ένα πλέγμα Ν διαστάσεων. Πολλές φορές, αφού έχουμε

δημιουργήσει μία καρτεσιανή εικονική τοπολογία, χρειαζόμαστε να εφαρμόσουμε κάποιες

συναρτήσεις συλλογικής επικοινωνίας σε υποπλέγματα του αρχικού πλέγματος. Σε αυτές τις

περιπτώσεις είναι χρήσιμη η συνάρτηση MPI_Cart_sub.

int MPI_Cart_sub (MPI_Comm cart_comm, int *belongs, MPI_Comm new_cart_comm)

Η παράμετρος belongs είναι ένας πίνακας ndims διαστάσεων που προσδιορίζει αν κάθε μία

διάσταση του cart_comm ανήκει στον νέο communicator new_cart_comm. Για παράδειγμα,

αν ο cart_comm ορίζει ένα 425 πλέγμα, και belongs = (TRUE, FALSE, TRUE) τότε, η

συνάρτηση MPI_Cart_sub θα δημιουργήσει δύο communicators καθένας με 20 διεργασίες

συνδεδεμένες σύμφωνα με τη δομή ενός 45 πλέγματος.

78

Page 79: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Παράρτημα

1. Εγκατάσταση του MPI

Το MPI παρέχεται σε ένα συμπιεσμένο αρχείο για να είναι πιο γρήγορη η μεταφορά

του μέσω ftp. Το MPI δίνεται σε μορφή πηγαίου κώδικα για να είναι δυνατή οποιαδήποτε

προσαρμογή του στο σύστημα που θα εκτελεστεί. Υπάρχουν αρκετά ftp sites τα οποία

δίνουν κάποια υλοποίηση του MPI. Ορισμένα links είναι:

ftp://info.mcs.anl.gov/pub/mpi . Εδώ βρίσκεται η υλοποίηση του MPICH από το

Argonne National Laboratory.

ftp.epcc.ed.ac.uk/pub/chimp/release/chimp.tar.Z . Εδώ υπάρχει η υλοποίηση του

CHIMP από το Edinburgh Parallel Computing Center.

ftp://tbag.osc.edu/pub/lam . Η υλοποίηση LAM από το Ohio Supercomputing Center.

ftp://ftp.erc.msstate.edu/unify . Ένα υποσύνολο του MPI για χρήση μέσα από το PVM

από το Mississippi State University.

Για το εργαστήριο του μαθήματος «Παράλληλος Υπολογισμός» χρησιμοποιείται το mpich v

1.1.2 του Argonne National Laboratory. Τα βήματα της εγκατάστασης είναι τα παρακάτω:

1. Μεταφέρουμε το αρχείο mpich.tar.gz ή το αρχείο mpich.tar.Z στο σύστημά μας με

ανώνυμο ftp. Το ποιο αρχείο θα μεταφέρουμε εξαρτάται από το ποιο πρόγραμμα

αποσυμπίεσης έχουμε διαθέσιμο. Αν υπάρχει το gunzip, χρειαζόμαστε το .gz,

διαφορετικά το .Z

2. Αν έχουμε το gunzip, η αποσυμπίεση γίνεται με gunzip -c mpich.tar.gz | tar xovf – Αν

έχουμε το zcat η αποσυμπίεση γίνεται με zcat mpich.tar.Z | tar xovf – Η παύλα στο

τέλος της εντολής απαιτείται. Συνιστάται η εγκατάσταση να γίνει στον κατάλογο /usr

και όχι στον / ο οποίος συνήθως, περιέχεται σε μικρό partition του δίσκου και

ενδεχομένως, να μην έχει αρκετό ελεύθερο χώρο

3. Και στις δυο περιπτώσεις στον τρέχοντα κατάλογο δημιουργείται ένας κατάλογος με το

όνομα mpich. Αφού μεταβούμε σ’ αυτό τον κατάλογο, εκτελούμε το script configure.

79

Page 80: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

Το configure θα προσπαθήσει να εντοπίσει την αρχιτεκτονική του συστήματός μας και

θα διαμορφώσει κατάλληλα το MPI. Το configure κάνει αρκετούς ελέγχους και

εμφανίζει τα αποτελέσματα στην οθόνη οπότε καλό θα ήταν να στείλουμε την έξοδό

του σε κάποιο αρχείο με τον χαρακτήρα ανακατεύθυνσης >. `Ετσι, αν υπάρξει

πρόβλημα, θα μπορέσουμε να διαβάσουμε τα μηνύματα αργότερα. Το configure δέχεται

πολλές παραμέτρους σε περίπτωση που οι εξ’ ορισμού ρυθμίσεις δεν επαρκούν για

σωστή διαμόρφωση. Για μία δοκιμαστική εγκατάσταση, συνιστάται να εκτελεστεί το

configure χωρίς παραμέτρους. Εκτός από την αρχιτεκτονική, το configure ελέγχει και

άλλες παραμέτρους του συστήματός μας όπως:

Ποιός compiler υπάρχει και την έκδοση του.

Τη συμπεριφορά του compiler δηλαδή, αν είναι ANSI και αν δέχεται κάποιες

συγκεκριμένες παραμέτρους που είναι σημαντικές για το MPI, για παράδειγμα αν

δέχεται τη δεσμευμένη-λέξη const.

Αν υπάρχουν κάποια απαραίτητα αρχεία επικεφαλίδας (header) καθώς, και κάποιες

βιβλιοθήκες.

Το μέγεθος των βασικών τύπων δεδομένων της γλώσσας, για παράδειγμα int, char, long

καθώς και το μέγεθος των δεικτών προς αυτά.

4. Αν όλα πάνε καλά από τη γραμμή εντολών δίνουμε make. Το make είναι ένα utility του

UNIX το οποίο αυτοματοποιεί και διευκολύνει τη μεταγλώττιση μεγάλων

προγραμμάτων που αποτελούνται από πολλά αρχεία πηγαίου κώδικα. Το make θα κάνει

όλες τις απαραίτητες μεταγλωττίσεις και θα δημιουργήσει τα απαραίτητα αρχεία και τις

βιβλιοθήκες. Αυτή η διαδικασία είναι χρονοβόρα. Συνιστάται να ανακατευθύνουμε την

έξοδο του make σε αρχείο όπως έγινε και με την έξοδο του configure. Το make διαβάζει

ένα αρχείο με το όνομα Makefile το οποίο βρίσκεται στον τρέχοντα κατάλογο. Εκεί

βρίσκει όλες τις πληροφορίες που χρειάζεται για να κάνει τη μεταγλώττιση του MPI. Το

Makefile δημιουργείται αυτόματα από το configure.

5. Αν έχουμε ένα δίκτυο από σταθμούς εργασίας και όχι ένα πραγματικό παράλληλο

σύστημα, θα πρέπει να αλλάξουμε το αρχείο mpich/util/machines/machines.xxx όπου

xxx είναι το όνομα της αρχιτεκτονικής που χρησιμοποιούμε. Εκεί ορίζουμε τα ονόματα

των μηχανών τα οποία θέλουμε να χρησιμοποιηθούν από το MPI. Η εγκατάσταση

δημιουργεί αυτό το αρχείο και γράφει το όνομα της μηχανής στην οποία έγινε η

εγκατάσταση 5 φορές. Αν όλες οι γραμμές του αρχείου είναι ίδιες το MPI θα

χρησιμοποιήσει μόνο μια μηχανή. Ετσι, πετυχαίνουμε μια προσομοίωση παράλληλης

εκτέλεσης. Στον ίδιο κατάλογο θα βρείτε ένα README που περιγράφει τη δομή που

πρέπει να έχει το αρχείο machines καθώς και ένα έτοιμο παράδειγμα για τροποποίηση.

6. Σε σύστημα PC που τρέχει LINUX θα χρειαστεί να δημιουργήσουμε (ή να αλλάξουμε

αν ήδη υπάρχει έτοιμο) το αρχείο .rhosts. Αυτό το αρχείο θα πρέπει να βρίσκεται στον

HOME κατάλογο του χρήστη που θα χρησιμοποιήσει το MPI. Εκεί θα πρέπει να

80

Page 81: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

ορίσουμε μια γραμμή με το όνομα του μηχανήματος συνοδευόμενο από το όνομα του

χρήστη. Η δομή του αρχείου είναι ίδια με τη δομή του machines.xxx.

1.1 Το πρόγραμμα tstmachines

Με αυτό το script ελέγχουμε ποιες μηχανές είναι διαθέσιμες σε ένα δίκτυο σταθμών

εργασίας για την εκτέλεση MPI προγραμμάτων. Βρίσκεται στον κατάλογο mpich/util. Αν

όλες οι μηχανές που έχουμε ορίσει στο αρχείο machines.xxx είναι διαθέσιμες, δεν θα έχουμε

καμία έξοδο στην οθόνη. Αν υπάρχει πρόβλημα θα εμφανιστεί το σχετικό μήνυμα. Με την

παράμετρο –v βλέπουμε τι ακριβώς κάνει το tstmachines. Μια έξοδος του tstmachines σε

σύστημα LINUX με ένα επεξεργαστή είναι η παρακάτω:

Trying true on Manolis.Athens.Greece...

Trying true on Manolis.Athens.Greece...

Trying true on Manolis.Athens.Greece...

Trying true on Manolis.Athens.Greece ...

Trying true on Manolis.Athens.Greece...

Trying ls on Manolis.Athens.Greece...

Trying ls on Manolis.Athens.Greece...

Trying ls on Manolis.Athens.Greece...

Trying ls on Manolis.Athens.Greece...

Trying ls on Manolis.Athens.Greece...

Trying user program on Manolis.Athens.Greece...

Trying user program on Manolis.Athens.Greece...

Trying user program on Manolis.Athens.Greece...

Trying user program on Manolis.Athens.Greece...

Trying user program on Manolis.Athens.Greece...

Στη συγκεκριμένη εκτέλεση του tstmachines στο αρχείο machines.LINUX υπήρχαν 5

ίδιες γραμμές γι’ αυτό και ο κάθε έλεγχος έγινε 5 φορές. Το script ελέγχει αν είναι δυνατή η

εκτέλεση ενός remote shell στα μηχανήματα του δικτύου το όνομα των οποίων υπάρχει στο

αρχείο machines.xxx. Αν το rsh μπορεί να εκτελεστεί το tstmachines δημιουργεί ένα μικρό

πρόγραμμα σε C σε κάθε υπολογιστή και προσπαθεί να το εκτελέσει. Για να γίνει αυτό θα

πρέπει να υπάρχει ένας κοινός κατάλογος σε όλους τους υπολογιστές ο οποίος θα έχει

προσαρτηθεί μέσω του NFS.

2. Μεταγλώττιση

Για μεταγλωτίσουμε ένα πρόγραμμα MPI μπορούμε να χρησιμοποιήσουμε τον

compiler cc που υπάρχει σε κάθε σύστημα UNIX. Όμως είναι πιο εύκολο η μεταγλώττιση

να γίνει με το script mpicc γιατί το script γνωρίζει που βρίσκονται τα αρχεία βιβλιοθήκης του

81

Page 82: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

MPI ενώ το cc όχι και πρέπει να δηλώσουμε τη διαδρομή στη γραμμή εντολής. Δηλαδή , το

mpicc είναι ένα script το οποίο περνάει στον compiler του συστήματος μερικές επιπλέον

παραμέτρους. Το πρόγραμμα mpicc χρησιμοποιείται ως εξής:

mpicc [args] filename

Για παράδειγμα, έστω ότι έχουμε το πηγαίο αρχείο σε C example.c. Για να το

μεταγλωττίσουμε δίνουμε:

mpicc –c example.c

Αν η μεταγλώττιση είναι επιτυχής, θα δημιουργηθεί το object αρχείο example.o. Για

να δημιουργήσουμε το εκτελέσιμο αρχείο example δίνουμε:

mpicc -o example example.o

Αν το πρόγραμμα μας αποτελείται από περισσότερα πηγαία αρχεία, μεταγλωττίζουμε

το καθένα από αυτά με mpicc –c και στο mpicc –o δίνουμε και τα υπόλοιπα object αρχεία

δηλαδή mpicc –o ExeFile file1.o file2.o file3.o κτλ. Αν το πρόγραμμα μας είναι γραμμένο

σε C++ η μεταγλώττιση και σύνδεση γίνονται με το script mpiCC το οποίο δέχεται τις ίδιες

παραμέτρους με το mpicc.

3. Εκτέλεση

Για να εκτελέσουμε ένα MPI πρόγραμμα, χρησιμοποιούμε το script mpirun. Με το

mpirun εκτελούμε ένα MPI πρόγραμμα με τον αριθμό διεργασιών που ορίζουμε. Η χρήση

του είναι:

mpirun -np <Αριθμός διεργασιών> <Όνομα προγράμματος>

Για να εκτελέσουμε το πρόγραμμα example του προηγούμενου παραδείγματος με 10

διεργασίες δίνουμε:

mpirun –np 10 example

Το mpirun διαβάζει το αρχείο mpich/util/machines/machines.xxx για να βρει τα

ονόματα των μηχανών που θα χρησιμοποιήσει. Οι παράμετροι του mpirun περνούν στη

συνάρτηση MPI_Init μέσω των παραμέτρων της main. Τα mpicc, mpiCC και mpirun

βρίσκονται στον κατάλογο mpich/bin ο οποίος πρέπει να βρίσκεται στο path. Το mpirun

μπορεί να εκτελέσει οποιοδήποτε πρόγραμμα MPI άσχετα με το αν ο πηγαίος κώδικας του

είναι σε C, C++ ή Fortran. Αυτό είναι δυνατό γιατί το mpirun απλά δημιουργεί διεργασίες

και αναθέτει σ’ αυτές ένα αντίγραφο του εκτελέσιμου κώδικα. Δεν ασχολείται με τη γλώσσα

στην οποία έγινε η συγγραφή του προγράμματος.

Αν δοκιμάσουμε να εκτελέσουμε ένα MPI πρόγραμμα χωρίς το mpirun, θα

δημιουργηθεί μονο μια διεργασία. Σ’ αυτή την περίπτωση, αν το πρόγραμμα μπορεί να

εκτελεστεί και με μια μόνο διεργασία, θα έχουμε τα σωστά αποτελέσματα. Διαφορετικά, θα

εμφανιστεί ένα μήνυμα λάθους που εξαρτάται από τον αλγόριθμο που χρησιμοποιούμε. Γι’

82

Page 83: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

αυτό μετά τη κλήση της MPI_Init, καλό είναι να υπολογίσουμε το μέγεθος του

MPI_COMM_WORLD και να το αποθηκεύσουμε σε μια μεταβλητή. Αν το μέγεθος του

MPI_COMM_WORLD δεν είναι αυτό που χρειάζεται από τον αλγόριθμό μας, θα πρέπει να

κλείσουμε τη βιβλιοθήκη του MPI αφού πρώτα εμφανίσουμε ένα μήνυμα λάθους.

4. Οι συναρτήσεις του MPI σε κατηγορίες

Παρακάτω δίνονται τα prototypes των συναρτήσεων του MPI για κλήση από τη C,

χωρισμένες σε κατηγορίες.

4.1 Συναρτήσεις διαχείρισης του περιβάλλοντος

int MPI_Abort(MPI_Comm comm, int errorcode)

int MPI_Errhandler_create(MPI_Handler_function *function,

MPI_Errhandler *errhandler)

int MPI_Errhandler_free(MPI_Errhandler *errhandler)

int MPI_Errhandler_get(MPI_Comm comm, MPI_Errhandler *errhandler)

int MPI_Errhandler_set(MPI_Comm comm, MPI_Errhandler errhandler)

int MPI_Error_class(int errorcode, int *errorclass)

int MPI_Error_string(int errorcode, char* string, int* resultlen)

int MPI_Finalize(void)

int MPI_Get_processor_name(char *name, int *resultlen)

int MPI_Init(int *argc, char **argv)

int MPI_Initialized(int *flag)

double MPI_Wtick(void)

double MPI_Wtime(void)

4.2 Συναρτήσεις επικοινωνίας Point-to-Point

int MPI_Bsend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm)

int MPI_Bsend_init(void *buf, int count, MPI_Datatype datatype, int dest,

int tag, MPI_Comm comm, MPI_Request *request)

int MPI_Buffer_attach(void *buffer, int size)

int MPI_Buffer_detach(void *buffer, int* size)

int MPI_Cancel(MPI_Request *request)

int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count)

int MPI_Get_elements(MPI_Status *status, MPI_Datatype datatype, int *count)

int MPI_Ibsend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm, MPI_Request *request)

int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag, MPI_Status *status)

int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source,

83

Page 84: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

int tag, MPI_Comm comm, MPI_Request *request)

int MPI_Isend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm, MPI_Request *request)

int MPI_Issend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm, MPI_Request *request)

int MPI_Irsend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm, MPI_Request *request)

int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status)

int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source,

int tag, MPI_Comm comm, MPI_Status *status)

int MPI_Recv_init(void *buf, int count, MPI_Datatype datatype, int source,

int tag, MPI_Comm comm, MPI_Request *request)

int MPI_Request_free(MPI_Request *request)

int MPI_Rsend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm

comm)

int MPI_Rsend_init(void *buf, int count, MPI_Datatype datatype, int dest,

int tag, MPI_Comm comm, MPI_Request *request)

int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm)

int MPI_Send_init(void *buf, int count, MPI_Datatype datatype, int dest,

int tag, MPI_Comm comm, MPI_Request *request)

int MPI_Sendrecv(void *sendbuf, int sendcount, MPI_Datatype datatype,

int dest, int sendtag, void *recvbuf, int recvcount,

MPI_Datatype recvtype, int source, MPI_Datatype recvtag,

MPI_Comm comm, MPI_Status *status)

int MPI_Sendrecv_replace(void *buf, int count, MPI_Datatype datatype,

int dest, int sendtag, int source, int recvtag,

MPI_Comm comm, MPI_Status* status)

int MPI_Ssend(void *buf, int count, MPI_Datatype datatype, int dest, int tag,

MPI_Comm comm)

int MPI_Ssend_init(void *buf, int count, MPI_Datatype datatype, int dest,

int tag, MPI_Comm comm, MPI_Request *request)

int MPI_Start(MPI_Request *request)

int MPI_Startall(int count, MPI_Request *array_of_requests)

int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status)

int MPI_Test_cancelled(MPI_Status *status, int *flag)

int MPI_Testall(int count, MPI_Request *array_of_requests, int *flag,

MPI_Status *array_of_statuses)

int MPI_Testany(int count, MPI_Request *array_of_requests, int *index,

int *flag, MPI_Status *status)

int MPI_Testsome(int incount, MPI_Request *array_of_requests,

int *outcount, int *array_of_indices)

int MPI_Wait(MPI_Request *request, MPI_Status *status)

84

Page 85: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

int MPI_Waitall(int count, MPI_Request *array_of_requests,

MPI_Status *array_of_statuses)

int MPI_Waitany(int count, MPI_Request *array_of_requests,

int *index, MPI_Status *status)

int MPI_Waitsome(int incount, MPI_Request *array_of_requests,

int *outcount, int *array_of_indices,

MPI_Status *array_of_statuses)

4.3 Συναρτήσεις συλλογικής επικοινωνίας

int MPI_Allgather(void *sendbuf, int sendcount, MPI_Datatype sendtype,

void *recvbuf, int recvcount, MPI_Datatype recvtype,

MPI_Comm comm)

int MPI_Allgatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype,

void *recvbuf, int *recvcounts, int *displs,

MPI_Datatype recvtype, MPI_Comm comm)

int MPI_Allreduce(void *sendbuf, void *recvbuf, int count,

MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)

int MPI_Alltoall(void *sendbuf, int sendcount, MPI_Datatype sendtype,

void *recvbuf, int recvcount, MPI_Datatype recvtype,

MPI_Comm comm)

int MPI_Alltoallv(void *sendbuf, int *sendcounts, int *sdispls,

MPI_Datatype sendtype, void* recvbuf, int *recvcounts,

int *rdispls, MPI_Datatype recvtype, MPI_Comm comm)

int MPI_Barrier(MPI_Comm comm)

int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root,

MPI_Comm comm)

int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype,

void *recvbuf, int recvcount, MPI_Datatype recvtype,

int root, MPI_Comm comm)

int MPI_Gatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype,

void *recvbuf, int *recvcounts, int *displs,

MPI_Datatype recvtype, int root, MPI_Comm comm)

int MPI_Op_create(MPI_User_function*function, int commute, MPI_Op *op)

int MPI_Op_free(MPI_Op *op)

int MPI_Reduce(void *sendbuf, void *recvbuf, int count,

MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)

int MPI_Reduce_scatter(void *sendbuf, void *recvbuf, int *recvcounts,

MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)

int MPI_Scan(void *sendbuf, void *recvbuf, int count,

MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)

int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype sendtype,

void *recvbuf, int recvcount, MPI_Datatype recvtype,

85

Page 86: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

int root, MPI_Comm comm)

int MPI_Scatterv(void *sendbuf, int *sendcounts, int *displs,

MPI_Datatype, sendtype, void *recvbuf, int recvcount,

MPI_Datatype recvtype, int root, MPI_Comm comm)

4.4 Συναρτήσεις διαχείρισης ομάδων

int MPI_Group_compare(MPI_Group group1, MPI_Group group2, int *result)

int MPI_Group_difference(MPI_Group group1, MPI_Group group2,

MPI_Group *newgroup)

int MPI_Group_excl(MPI_Group group, int n , int *ranks,

MPI_Group *newgroup)

int MPI_Group_free(MPI_Group *group)

int MPI_Group_incl(MPI_Group group, int n, int *ranks,

MPI_Group *newgroup)

int MPI_Group_intersection(MPI_Group group1, MPI_Group group2,

MPI_Group *newgroup)

int MPI_Group_range_excl(MPI_Group group, int n, int ranges[ ][3],

MPI_Group *newgroup)

int MPI_Group_range_incl(MPI_Group group, int n, int ranges[ ][3],

MPI_Group *newgroup)

int MPI_Group_rank(MPI_Group group, int* rank)

int MPI_Group_size(MPI_Group group, int *size)

int MPI_Group_translate_ranks(MPI_Group group1, int n, int *ranks1,

MPI_Group group2, int *ranks2)

int MPI_Group_union(MPI_Group group1, MPI_Group group2,

MPI_Group* newgroup)

4.5 Συναρτήσεις διαχείρισης Communicators

int MPI_Comm_compare(MPI_Comm comm1, MPI_Comm comm2, int *result)

int MPI_Comm_create(MPI_Comm comm, MPI_Group group,

MPI_Comm *newcomm)

int MPI_Comm_dup(MPI_Comm comm, MPI_Comm *newcomm)

int MPI_Comm_free(MPI_Comm *comm)

int MPI_Comm_group(MPI_Comm comm, MPI_Group *group)

int MPI_Comm_rank(MPI_Comm comm, int *rank)

int MPI_Comm_remote_group(MPI_Comm comm, MPI_Group *group)

int MPI_Comm_remote_size(MPI_Comm comm, int *size)

int MPI_Comm_size(MPI_Comm comm, int *size)

int MPI_Comm_split(MPI_Comm comm, int color, int key,

MPI_Comm *newcomm)

int MPI_Comm_test_inter(MPI_Comm comm, int *flag)

86

Page 87: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

int MPI_Intercomm_create(MPI_Comm local_comm, int local_leader,

MPI_Comm bridge_comm, int remote_leader,

int tag, MPI_Comm *newintercomm)

int MPI_Intercomm_merge(MPI_Comm intercomm, int high,

MPI_Comm *newintracomm)

4.6 Συναρτήσεις για τους παραγόμενους τύπους δεδομένων

int MPI_Type_commit(MPI_Datatype *datatype)

int MPI_Type_contiguous(int count, MPI_Datatype oldtype,

MPI_Datatype *newtype)

int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint *extent)

int MPI_Type_free(MPI_Datatype *datatype)

int MPI_Type_hindexed(int count, int *array_of_blocklengths,

MPI_Aint *array_of_displacements,

MPI_Datatype oldtype, MPI_Datatype *newtype)

int MPI_Type_hvector(int count, int blocklength, MPI_Aint stride,

MPI_Datatype oldtype, MPI_Datatype *newtype)

int MPI_Type_indexed(int count, int *array_of_blocklengths,

int *array_of_displacements, MPI_Datatype oldtype,

MPI_Datatype* newtype)

int MPI_Type_lb(MPI_Datatype datatype, MPI_Aint *displacement)

int MPI_Type_size(MPI_Datatype datatype, int *size)

int MPI_Type_struct(int count, int *array_of_blocklengths,

MPI_Aint *array_of_displacements,

MPI_Datatype *array_of_types, MPI_Datatype *newtype)

int MPI_Type_ub(MPI_Datatype datatype, MPI_Aint *displacement)

int MPI_Type_vector(int count, int blocklength, int stride)

4.7 Συναρτήσεις διαχείρισης εικονικών τοπολογιών

int MPI_Cart_coords(MPI_Comm comm, int rank, int maxdims, int *coords)

int MPI_Cart_create(MPI_Comm comm_old, int ndims, int *dims,

int *periods, int reorder, MPI_Comm *comm_cart)

int MPI_Cart_get(MPI_Comm comm, int maxdims, int *dims, int *periods,

int *coords)

int MPI_Cart_map(MPI_Comm comm, int ndims, int *dims, int *periods,

int *newrank)

int MPI_Cart_rank(MPI_Comm comm, int *coords, int *rank)

int MPI_Cart_shift(MPI_Comm comm, int direction, int disp,

int *rank_source, int *rank_dest)

int MPI_Cart_sub(MPI_Comm comm, int *remain_dims, MPI_Comm *newcomm)

87

Page 88: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

int MPI_Cartdim_get(MPI_Comm comm, int *ndims)

int MPI_Dims_create(int nnodes, int ndims, int *dims)

int MPI_Graph_create(MPI_Comm comm_old, int nnodes, int *index,

int *edges, int reorder, MPI_Comm *comm_graph)

int MPI_Graph_get(MPI_Comm comm, int maxindex, int maxedges,

int *index, int *edges)

int MPI_Graph_map(MPI_Comm comm, int nnodes, int *index, int *edges,

int *newrank)

int MPI_Graph_neighbors(MPI_Comm comm, int rank, int maxneighbors,

int *neighbors)

int MPI_Graph_neighbors_count(MPI_Comm comm int rank, int *nneighbors)

int MPI_Graphdims_get(MPI_Comm comm, int nnodes, int nedges)

int MPI_Topo_test(MPI_Comm comm, int *status)

4.8 Διάφορες συναρτήσεις

int MPI_Address(void *location, MPI_Aint *address)

int MPI_Attr_delete(MPI_Comm comm, int keyval)

int MPI_Attr_get(MPI_Comm comm, int keyval, void *attribute_val, int *flag)

int MPI_Attr_put(MPI_Comm comm, int keyval, void *attribute_val)

int MPI_Keyval_create(MPI_Copy_function *copy_fn,

MPI_Delete_function *delete_fn, int *keyval, void *extra_state)

int MPI_Keyval_free(int *keyval)

int MPI_Pack(void *inbuf, int incount, MPI_Datatype datatype, void *outbuf,

int outsize, int *position, MPI_Comm comm)

int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size)

int MPI_Pcontrol(const int level, …)

int MPI_Unpack(void *inbuf, int insize, int *position, void *outbuf,

int outcount, MPI_Datatype datatype, MPI_Comm comm)

5. MPI σταθερές

Ακολουθούν οι σταθερές που έχουν οριστεί στη βιβλιοθήκη του MPI για χρήση από C

και Fortran. Αρκετές από αυτές τις σταθερές, επιστρέφονται από τις συναρτήσεις ως

αποτέλεσμα ή περιέχουν ένα μήνυμα λάθους. Κάποιες άλλες μπορούν να χρησιμοποιηθούν

σαν ορίσματα κλήσης. Ορισμένες άλλες παριστάνουν τύπους δεδομένων. Τέλος, υπάρχουν

και οι σταθερές που συμβολίζουν τιμές που χρησιμοποιούνται συχνά στο MPI.

5.1 Αποτελέσματα συναρτήσεων

MPI_CART

MPI_COMM_NULL

88

Page 89: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

MPI_CONGRUENT

MPI_DATATYPE_NULL

MPI_ERRHANDLER_NULL

MPI_ERROR

MPI_GRAPH

MPI_IDENT

MPI_OP_NULL

MPI_PENDING

MPI_PROC_NULL

MPI_REQUEST_NULL

MPI_SIMILAR

MPI_SUCCESS

MPI_UNDEFINED

MPI_UNEQUAL

5.2 Μηνύματα λάθους

MPI_ERR_ARG

MPI_ERR_BUFFER

MPI_ERR_COMM

MPI_ERR_COUNT

MPI_ERR_DIMS

MPI_ERR_GROUP

MPI_ERR_IN_STATUS

MPI_ERR_INTERN

MPI_ERR_LASTCODE

MPI_ERR_OP

MPI_ERR_OTHER

MPI_ERR_PENDING

MPI_ERR_RANK

MPI_ERR_REQUEST

MPI_ERR_ROOT

MPI_ERR_TAG

MPI_ERR_TOPOLOGY

MPI_ERR_TRUNCATE

MPI_ERR_TYPE

MPI_ERR_UNKNOWN

5.3 Ορίσματα κλήσης

MPI_ANY_SOURCE

MPI_ANY_TAG

89

Page 90: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

5.4 Τύποι δεδομένων

MPI_BYTE

MPI_CHAR

MPI_CHARACTER

MPI_COMPLEX

MPI_DOUBLE

MPI_DOUBLE_COMPLEX

MPI_DOUBLE_INT

MPI_DOUBLE_PRECISION

MPI_FLOAT

MPI_FLOAT_INT

MPI_INT

MPI_INTEGER

MPI_INTEGER1

MPI_INTEGER2

MPI_INTEGER4

MPI_LOGICAL

MPI_LONG

MPI_LONG_DOUBLE

MPI_LONG_DOUBLE_INT

MPI_LONG_INT

MPI_LONG_LONG

MPI_PACKED

MPI_REAL

MPI_REAL2

MPI_REAL4

MPI_REAL8

MPI_SHORT

MPI_SHORT_INT

MPI_UNSIGNED

MPI_UNSIGNED_CHAR

MPI_UNSIGNED_LONG

MPI_UNSIGNED_SHORT

5.5 Συμβολικές σταθερές

MPI_2DOUBLE_PRECISION

MPI_2INT

MPI_2INTEGER

MPI_2REAL

MPI_BAND

MPI_BOR

90

Page 91: MPI-notes ΠΑΡΑΛΛΗΛΑ ΣΥΣΤΗΜΑΤΑ

MPI_BOTTOM

MPI_BSEND_OVERHEAD

MPI_BXOR

MPI_COMM_SELF

MPI_COMM_WORLD

MPI_GROUP_EMPTY

MPI_ERRORS_ARE_FATAL

MPI_ERRORS_RETURN

MPI_GROUP_NULL

MPI_HOST

MPI_IO

MPI_KEYVAL_INVALID

MPI_LAND

MPI_LB

MPI_LOR

MPI_LXOR

MPI_MAX

MPI_MAX_ERROR_STRING

MPI_MAX_PROCESSOR_NAME

MPI_MAXLOC

MPI_MIN

MPI_MINLOC

MPI_PROD

MPI_SOURCE

MPI_STATUS_SIZE

MPI_SUM

MPI_TAG

MPI_TAG_UB

MPI_UB

MPI_WTIME_IS_GLOBAL

91