HY-486 Αρχές Κατανεμημένου Υπολογισμού Εαρινό Εξάμηνο 2017-2018 Πρώτη Προγραμματιστική Εργασία Προθεσμία παράδοσης: Δευτέρα 30/4 στις 23:59. 1. Γενική Περιγραφή Στην πρώτη προγραμματιστική εργασία καλείστε να υλοποιήσετε ένα διαμοιραζόμενο σύστημα παραγωγής και κατανάλωσης πληροφοριών. Η προγραμματιστική εργασία θα πρέπει να υλοποιηθεί στην γλώσσα C με τη χρήση της βιβλιοθήκης pthreads. Προσοχή: Η εργασία είναι, εν μέρει, διαφορετική για τους προπτυχιακούς και τους μεταπτυχιακούς φοιτητές. Κάθε μέρος αναφέρεται αναλυτικά παρακάτω. 2. Υλοποίηση Στην εργασία αυτή θα πρέπει να υλοποιήσετε ένα διαμοιραζόμενο σύστημα παραγωγής και κατανάλωσης πληροφοριών (producer-consumer system), όπου η κάθε δημοσιευμένη πληροφορία θα χαρακτηρίζεται από ένα hashtag, δηλαδή θα αφορά μία συγκεκριμένη θεματική ενότητα. Οι λειτουργίες που θα πραγματοποιούνται στο σύστημα θα είναι η δημοσίευση και η λήψη πληροφοριών. Για τη δημοσίευση των πληροφοριών, μία οικογένεια από νήματα που θα ονομάζονται νήματα παραγωγής (ή παραγωγοί ή νήματα δημοσίευσης) θα παράγουν πληροφορίες και θα τις εισάγουν σε μία διαμοιραζόμενη ουρά (shared queue). Πρέπει να σημειωθεί, ότι κάθε παραγωγός παράγει πληροφορίες με ένα συγκεκριμένο, μοναδικό hashtag. Στη συνέχεια, οι παραγωγοί θα μεταφέρουν τις πληροφορίες σε μία δεύτερη διαμοιραζόμενη δομή δεδομένων, διαγράφοντάς τες από τη διαμοιραζόμενη ουρά. Έπειτα, οι πληροφορίες θα διαβάζονται και θα διαγράφονται από τη δεύτερη διαμοιραζόμενη δομή δεδομένων από μια δεύτερη οικογένεια νημάτων, τα νήματα κατανάλωσης (ή καταναλωτές). Πιο συγκεκριμένα, στην πρώτη φάση, που είναι η φάση δημιουργίας πληροφοριών, κάθε νήμα παραγωγής θα παράγει πληροφορίες που περιγράφονται από το struct: struct info { int producerid; int timestamp; Στο παραπάνω struct, το producerid είναι το μοναδικό αναγνωριστικό του κάθε νήματος παραγωγής και το timestamp είναι η χρονική στιγμή που δημιουργήθηκε η πληροφορία. Για λόγους
απλότητας, το producerid υποδηλώνει και το hashtag της πληροφορίας που παράγεται από αυτόν τον παραγωγό. Το νήμα με αναγνωριστικό producerid παράγει πληροφορίες με τα εξής timestamps: i*n + producerid όπου Ν είναι το πλήθος των παραγωγών, ενώ το i παίρνει τις τιμές 0 i N-1. Επομένως: Ο παραγωγός με producerid=0 παράγει πληροφορίες (struct info) με timestamps: 0, N, 2*N, 3*N,, (Ν-1)*Ν = N^2 N. Ο παραγωγός με producerid=1 παράγει πληροφορίες με timestamps: 1, N+1, 2*N+1, 3*N+1,..., N^2 N + 1.... Ο παραγωγός με producerid=n-1 παράγει πληροφορίες με timestamps: N-1, 2*N-1, 3*N- 1,, N^2 N (N-1) = N^2-1. Την κάθε πληροφορία που παράγει ο κάθε παραγωγός, την εισάγει (καλώντας την enqueue) στην διαμοιραζόμενη ουρά (concurrent queue) η οποία αναπαρίσταται από το παρακάτω σχήμα: Η διαμοιραζόμενη ουρά θα πρέπει να υλοποιηθεί με τους εξής τρόπους: Unbounded Total Queue με χρήση locks, όπως έχει διδαχθεί στο μάθημα (κεφάλαιο 5, σελίδες 2 και 3). Η δομή θα αναπαρίσταται από το struct: struct queue { struct queue_node *Head; struct queue_node *Tail; pthread_mutex_t head_lock; pthread_mutex_t tail_lock; Το struct που θα περιγράφει τον κάθε κόμβο της ουράς θα είναι της μορφής: struct queue_node { struct info inf; struct queue_node *next;
Μεταπτυχιακοί φοιτητές (Bonus 25% για τους προπτυχιακούς): Unbounded Lock-Free Queue χωρίς τη χρήση locks (non-blocking algorithm of Michael and Scott), όπως έχει διδαχθεί στο μάθημα (κεφάλαιο 5, σελίδες 4 και 5). Η δομή θα αναπαρίσταται από ένα struct που θα περιέχει δύο pointers που θα δείχνουν στην αρχή και στο τέλος της ουράς, αντίστοιχα. Ο κάθε κόμβος της ουράς θα είναι ένα struct που θα περιέχει δύο πεδία, το πεδίο info και το πεδίο next που είναι δείκτης στο επόμενο στοιχείο της ουράς. Πριν προχωρήσουν στην επόμενη φάση, όλοι οι παραγωγοί θα πρέπει να έχουν τελειώσει τη φάση της δημοσίευσης, δηλαδή την εισαγωγή των πληροφοριών που ο καθένας παράγει στη διαμοιραζόμενη ουρά. Για να επιτευχθεί αυτό, θα χρησιμοποιήσετε ένα barrier (από τη βιβλιοθήκη των pthreads) ώστε τα νήματα να συγχρονιστούν με το πέρας της φάσης της δημοσίευσης και να ξεκινήσουν συγχρονισμένα την επόμενη φάση. Τα νήματα θα φτάνουν στο barrier αφότου τελειώσουν με την παραγωγή όλων των πληροφοριών τους και την εισαγωγή των πληροφοριών αυτών στην διαμοιραζόμενη ουρά. Αφού ολοκληρωθεί η φάση της δημοσίευσης, οι πληροφορίες θα πρέπει να μεταφερθούν από τη διαμοιραζόμενη ουρά σε μια άλλη διαμοιραζόμενη δομή, προκειμένου να μπορέσουν να καταναλωθούν από τους καταναλωτές. Συγκεκριμένα, οι παραγωγοί ξεκινούν να διαγράφουν πληροφορίες από την ουρά (dequeue) και κάθε πληροφορία που διαγράφουν την εισάγουν στην ακόλουθη δομή: Διαμοιραζόμενο Δένδρο Δυαδικής Αναζήτησης (binary search tree) στο οποίο ο συγχρονισμός επιτυγχάνεται με fine-grained synchronization (hand-over-hand locking). Το δένδρο είναι ταξινομημένο βάσει του πεδίου timestamp (έτσι ώστε αν πραγματοποιηθεί ενδοδιατεταγμένη διάταξη πάνω στο δένδρο, τα timestamps να εμφανίζονται σε αύξουσα διάταξη). Concurrent Binary Search Tree Μεταπτυχιακοί φοιτητές (Bonus 20% για τους προπτυχιακούς): Ουρά Προτεραιότητας (Priority Queue), υλοποιημένη ως ένα δυναμικό πλήρες δένδρο (dynamic complete tree) (όπως αυτή που σας ζητήθηκε να υλοποιήσετε στην 1η σειρά θεωρητικών ασκήσεων, Άσκηση C).
Concurrent Priority Queue Το struct που θα περιγράφει τον κάθε κόμβο του δένδρου θα είναι της μορφής: struct tree_node { struct info inf; pthread_mutex_t lock; struct tree_node *lc; struct tree_node *rc; Ένας global pointer, struct tree_node *Root, δεικτοδοτεί την ρίζα του δένδρου. Ταυτόχρονα, οι καταναλωτές θα ξεκινούν να διαγράφουν κόμβους (πληροφορίες) από το δένδρο. Επομένως, θα πρέπει κι αυτοί να συμμετέχουν στο barrier που ακολουθεί τη φάση δημοσίευσης. Συγκεκριμένα, οι καταναλωτές ξεκινούν την εκτέλεσή τους με τη συμμετοχή τους σ αυτό το barrier. Επομένως, οι παραγωγοί πραγματοποιούν εισαγωγές στη δεύτερη διαμοιραζόμενη δομή παράλληλα με τις διαγραφές που εκτελούν τα νήματα κατανάλωσης. Συγκεκριμένα, για τη φάση της διαγραφής θα πρέπει να λάβετε υπ όψιν σας τα εξής: Κάθε νήμα κατανάλωσης ενδιαφέρεται να καταναλώσει όλες τις πληροφορίες μιας μόνο κατηγορίας, δηλαδή τις πληροφορίες που έχουν δημοσιευθεί από έναν μόνο παραγωγό. Για λόγους απλότητας, θεωρήστε ότι το νήμα κατανάλωσης με αναγνωριστικό id, ενδιαφέρεται να καταναλώσει τις πληροφορίες που παράγονται μόνο από το νήμα παραγωγής με αναγνωριστικό id-1. Επομένως, ο καταναλωτής 3 θα καταναλώσει τις πληροφορίες που παρήγαγε ο παραγωγός 2, ο καταναλωτής 2 θα καταναλώσει τις πληροφορίες που παρήγαγε ο παραγωγός 1, ο καταναλωτής 1 θα καταναλώσει τις πληροφορίες που παρήγαγε ο παραγωγός Ν, κ.ο.κ. Είναι αξιοσημείωτο ότι για να γίνουν παράλληλα οι εισαγωγές και οι διαγραφές στη δεύτερη δομή, ο καταναλωτής με αναγνωριστικό id θα πρέπει να διατηρεί έναν πίνακα από bits (μπορείτε για ευκολία να αποθηκεύσετε integers), N στοιχείων, στον οποίο καταγράφει ποιά από τα N στοιχεία που έχει παράξει ο παραγωγός με αναγνωριστικό id-1, έχει ήδη καταναλώσει.
Μεταπτυχιακοί φοιτητές: Θα υπάρχει μόνο ένα νήμα κατανάλωσης, το οποίο θα εκτελεί όλες τις διαγραφές χρησιμοποιώντας τη DeleteMin(), όπως αυτή περιγράφηκε στην 1η σειρά θεωρητικών ασκήσεων. Για τη διευκόλυνσή σας η υλοποίηση της συνάρτησης CalculatePath() σας δίνεται (βλ. ιστοσελίδα μαθήματος). Όταν κάθε καταναλωτής έχει καταναλώσει όλες τις πληροφορίες που του αντιστοιχούν, οι δύο διαμοιραζόμενες δομές θα πρέπει να είναι άδειες και η διαδικασία θα τερματίζει. 4. Αρχικοποίηση κι Εκτέλεση Προγράμματος Το πρόγραμμα θα πρέπει να δέχεται ως είσοδο έναν φυσικό αριθμό Ν, ο οποίος αναπαριστά το πλήθος της κάθε οικογένειας threads (Ν παραγωγοί και N καταναλωτές) για τους προπτυχιακούς φοιτητές, ενώ για τους μεταπτυχιακούς φοιτητές μόνο το πλήθος των παραγωγών. 5. Παράδοση Εργασίας Για την παράδοση της εργασίας θα πρέπει να χρησιμοποιήσετε το πρόγραμμα turnin, που υπάρχει εγκατεστημένο στα μηχανήματα του τμήματος. Συγκεκριμένα, η εντολή παράδοσης είναι: turnin project1@hy486 Για να επιβεβαιώσετε ότι η υποβολή της εργασίας σας ήταν επιτυχής μπορείτε να χρησιμοποιήσετε την εντολή: verify-turnin project1@hy486 Προσοχή: Τα παραδοτέα σας θα πρέπει να περιέχουν ό,τι χρειάζεται (τα αρχεία πηγαίου κώδικα και ένα README όπου θα εξηγείτε πώς κάνατε compile και πως τρέξατε τον κώδικά σας) και να είναι σωστά δομημένα ώστε να κάνουν compile και να εκτελούνται στα μηχανήματα της σχολής, όπου και θα γίνει η εξέταση της εργασίας. O κώδικάς σας θα πρέπει να περιέχει σχόλια.