Επισκόπηση Κατανεµηµένα Συστήµατα Ι Μάθηµα Βασικής Επιλογής, Χειµερινού Εξαµήνου Τοµέας Εφαρµογών και Θεµελιώσεων Χρήστος Κονίνης Ορέστης Ακριβόπουλος Τρίτη, 9 εκεµβρίου, 2010 Υπολογιστικό Τα µηνύµατα στο Shawn Χειρισµός διαφορετικών τύπων µηνυµάτων Rerun exactly the same Simulation Reading Tags from topology file Example 4η Ασκηση FooldSet Χρησιµοποιώντας διαφορετικούς τύπους µηνυµάτων ήλωση και αποστολή διαφορετικών τύπων µηνυµάτων Σχεδόν όλοι οι αλγόριθµοι χρησιµοποιούν περισσότερους του ενός τύπους µηνυµάτων Είναι ϐολικό και επιθυµητό να χρησιµοποιούµε διαφορετικούς τύπους µηνυµάτων για κάθε διακριτή λειτουργία: Μπορεί να ϑέλουµε πολυπλοκότητα επικοινωνίας ανά τύπο µηνύµατος Ευκολότερο στην κατανόηση/αποσφαλµάτωση εν υπερφορτώνουµε το µήνυµα µε πληροφορίες που δεν χρειάζονται ανά περίπτωση Στο shawn η υλοποίηση ενός νέου µηνύµατος γίνεται µε την δήλωση µιας κλάσης-µήνυµα στο αρχείο message.h, οπότε για να δηµιουργήσουµε ένα νέο µήνυµα: Βασιζόµενη σε µια ήδη υπάρχουσα κλάση στο message.h π.χ. floodingmessage, την αντιγράφουµε αλλάζοντας το όνοµά της (π.χ. hellomessage) και τις µεταβλητές που ϑα χρειαστούµε Οµοίως και για της συναρτήσεις (constructor/desctructor) στο message.cpp Τώρα µπορούµε να στέλνουµε µηνύµατα hellomessage: send(new hellomessage() )
Λήψη και επεξεργασία µηνύµατα διαφορετικών τύπων Λήψη και επεξεργασία µηνυµάτα διαφορετικών τύπων Οταν σε µία διεργασία παραλαµβάνουµε ένα νέο µήνυµα προκειµένου να εξακριβώσουµε τον τύπου του µηνύµατος, κάνουµε dynamic_cast στον τύπο της κλάσης-µήνυµα, αν µας επιστρέψει NULL το µήνυµα έχει άλλο τύπο. Αν µας επιστρέψει ένα δείκτη στην κλάση τότε καλούµε µια µέθοδο για την περαιτέρω επεξεργασία του συγκεκριµένου µηνύµατος. Παράδειγµα της message_process για 1 τύπο µηνύµατος const FloodingMessage floodmsg = dynamic_cast < const FloodingMessage > ( mh. get ( ) ) ; i f ( floodmsg! = NULL && owner_w ( )! = floodmsg >source ( ) ) { handle_flooding_message ( floodmsg ) ; return true ; Για κάθε τύπο µηνύµατος που έχουµε ϑα προσθέτουµε ένα παρόµοιο σύνολο εντολών στην process_messsage() µια νεο συνάρτηση για την επεξεργασία του handle_<name>_message() Παράδειγµα της message_process για 2 τύπους µηνυµάτων const FloodingMessage floodmsg = dynamic_cast < const FloodingMessage > ( mh. get ( ) ) ; i f ( floodmsg! = NULL && owner_w ( )! = floodmsg >source ( ) ) { handle_flooding_message ( floodmsg ) ; return true ; const HelloMessage hellomsg = dynamic_cast < const HelloMessage > ( mh. get ( ) ) ; i f ( hellomsg! = NULL && owner_w ( )! = hellomsg >source ( ) ) { handle_hello_message ( hellomsg ) ; return true ; Οταν στέλνετε ένα µήνυµα χρησιµοποιώντας την send() το λαµβάνουν όλες οι γειτονικές διεργασίες, τι γίνετε στην περίπτωση που µόνο ϑέλουµε µόνο µια συγκεκριµένη διεργασία να το επεξεργαστεί; Προσθέτουµε στην κλάση του µηνύµατος ένα πεδίο int destination στο οποίο ο αποστολέας ϑα αποθηκεύει το ID της διεργασίας που είναι ο προορισµός. Οταν µια διεργασία λαµβάνει ένα µήνυµα τέτοιου τύπου ελέγχει πριν το επεξεργαστεί αν το ID της είναι ίδιο µε το destination του µηνύµατος. Σε περίπτωση που είναι ίδια συνεχίζει µε την επεξεργασία, σε διαφορετική περίπτωση το αγνοεί. Στο παρακάτω παράδειγµα υποθέτουµε ότι έχουµε προσθέσει µια µεταβλητή και µια αντίστοιχή συνάρτηση destination / desination() στη κλάση-µήνυµα flooding, και ϑέλουµε να το παραλάβει/επεξεργαστεί ένας κόµβος κάθε ϕορά. Στην message_process, όταν µια διεργασία λαµβάνει ένα µήνυµα flooding παρατηρούµε ότι ελέγχει πριν το επεξεργαστεί αν το ID της είναι ίδιο µε το destination του µηνύµατος. Παράδειγµα message_process για αποστολή σε συγκεκριµένο προορισµό const FloodingMessage floodmsg = dynamic_cast < const FloodingMessage > ( mh. get ( ) ) ; i f ( floodmsg! = NULL && owner_w ( )! = floodmsg >source ( ) ) { i f ( id ( )! = floodmsg >destination ( ) ) handle_flooding_message ( floodmsg ) ; return true ;
Επισκόπηση Τα µηνύµατα στο Shawn Χειρισµός διαφορετικών τύπων µηνυµάτων Rerun exactly the same Simulation Reading Tags from topology file Example 4η Ασκηση FooldSet Μέχρι τώρα όλα τα µηνύµατα που έστελνε ένας κόµβος παραδίδονταν µε επιτυχία. Τι γίνεται στην περίπτωση που ϑέλουµε να εξοµοιώσουµε σφάλµατα στην επικοινωνία (απώλεια µηνυµάτων); Το Shawn µας δίνει ένα transm model για την εξοµοίωση σφαλµάτων στην αποστολή µηνυµάτων το random_drop_chain. Πρέπει να προσθέσουµε το random_drop_chain στην αλυσίδα των transmission model Το random_drop_chain δέχεται 1 παράµετρο : probability: Η πιθανότητα να απορρίψει ένα µήνυµα chain_transm_model name=random_drop_chain p r o b a b i l i t y =0.1 prepare_world edge_model= l i s t comm_model= disk_graph \ transm_model= s t a t s _ c h a i n \ range=3 chain_transm_model name=random_drop_chain p r o b a b i l i t y =0.4 chain_transm_model name= r e l i a b l e r e c t _ w o r l d width =5 height =5 count =10 p r o c e s s o r s = h e l l o w o r l d s i m u l a t i o n m a x _ i t e r a t i o n s =10 d u m p _ t r a n s m i s s i o n _ s t a t s Το παραπάνω παράδειγµα απορρίπτει µηνύµατα µε πιθανότητα 40% Τα µηνύµατα που δεν απορρίπτονται, παραδίδονται στον τελικό προορισµό από το reliable model Η αλυσίδα των transmission models που προκύπτει είναι : Message > StatsChain > RandomDropChain > R e l i a b l e Σφάλµατα επικοινωνίας Example Αντιγράψτε τις προηγούµενες εντολές σε ένα αρχείο msgserrors.conf και εκτελέστε την εξοµοίωση. Στην εξοδο ϐλέπουµε τις παρακάτω πληροφορίες στο τέλος κάθε εξοµοιώσης: stats_chain transmission model information general a l l _ t y p e s messages 10 general a l l _ t y p e s s i z e 10 general N10helloworld17HelloworldMessageE messages 10 general N10helloworld17HelloworldMessageE s i z e 10 Simulation : Task done dump_transmission_stats random_drop : 10 msgs. to be sent, 4 of them dropped_, 40% dropped_ ; \ d e s i r e d p r o b a b i l t y was : 40% Τροποποιήστε την πιθανότητα στο random_drop_chain του msgserrors.conf και επαναλάβεται την εξοµοίωση Παρατηρήστε ότι κάθε ϕορά έχετε διαφορετικά αποτελέσµατα, και ότι να µηνύµατα προµετρούνται ακοµά και αν χαθούν.
Επαναλαµβάνοντας την ίδια εξοµοίωση Επαναλαµβάνοντας την ίδια εξοµοίωση Το προηγούµενο παράδειγµα παρατηρούµε ότι σε κάθε εξοµοίωση παίρνουµε διαφορετικά αποτελέσµατα Αυτό συµβαίνει γιατί : 1. Το rect_world τοποθετεί τους κόµβους τυχαία 2. Το random_drop_chain απορρίπτει τα µηνύµατα τυχαία Αν ϑέλουµε να επαναλαµβάνουµε την ίδια εξοµοίωση πρέπει να αρχικοποιούµε την γεννήτρια τυχαίων αριθµών µε το ίδιο seed Υπάρχουν 2 τρόποι για να την αρχικοποιήσουµε : 1. Να ϑέσουµε απευθείας τιµή στο seed στην αρχή του config αρχείου µας µε την εντολή: random_seed action = s e t seed =123456789 3. Να σώσουµε το seed από µια εξοµοίωση και έπειτα να την χρησιµοποιήσουµε σε µια άλλη π.χ. Παράδειγµα αποκεύκευσης του τρεχοντος seed random_seed action = create filename = f i l e _ c o n t a i n i n g _ t h e _ s e e d prepare_world edge_model= l i s t...... Παράδειγµα ϕόρτωσης του seed από αρχείο random_seed action =load filename = f i l e _ c o n t a i n i n g _ t h e _ s e e d prepare_world edge_model= l i s t...... Επισκόπηση Τα µηνύµατα στο Shawn Χειρισµός διαφορετικών τύπων µηνυµάτων Rerun exactly the same Simulation Reading Tags from topology file Example 4η Ασκηση FooldSet Μέχρι τώρα χρησιµοποιούσαµε processors για να υλοποιήσουµε έναν αλγόριθµο Κάθε processor είχε την δικιά του εσωτερική κατάσταση (µεταβλητές που ήταν ιδιωτικές). Ο µόνος τρόπος για έχουµε πρόσβαση στην εσωτερική κατάσταση του processor ήταν µέσω debug µηνυµάτων Οµως σε πολλές περιπτώσεις ϑέλουµε να αποθηκεύσουµε την κατάσταση ενός κόµβου Επιπλέον σε αρκετές περιπτώσεις αλγόριθµοι χρειάζονται τιµές σαν είσοδο που παράγονται από άλλους πολύπλοκους αλγόριθµους Τα Tags είναι ένας µηχανισµός του shawn για να προσθέτουµε πληροφορία (µεταβλητές) στους κόµβους Χρησιµοποιώντας τα tasks load/save world µπορούµε να αποθηκεύουµε και να ανακτούµε αυτές τις πληροφορίες
Πώς ϕτιάχνουµε ένα νέο Tag 1. Ορίζουµε µια µεταβλητή δείκτη τύπου shawn::<type name>tag στο processor.h π.χ. Πρακτικά τα Tags είναι κλάσεις που περιέχουν µία τιµή (π.χ. µια µεταβλητή) και ένα όνοµα (string) Υπάρχουν 3 διαφορετικές κατηγορίες Tags : 1. Simple Tags Περιέχουν τιµές ενός συγκεκριµένου τύπου (π.χ. boolean, int, string) 2. Group Tags Περιέχουν άλλα Tags 3. Map Tags Περιέχουν Ϲεύγη τιµών συγκεκριµένου τύπου shawn : : IntegerTag i n t _t a g ; 2. Την αρχικοποιούµε µε ένα νέο αντικείµενο shawn::<type name>tag συνήθως στην boot() του processor.cpp π.χ. i n t _t a g = new shawn : : IntegerTag ( "tag_name ", value ) ; 3. Προαιρετικά µπορούµε να ϑέσουµε το Tag µόνιµο ( set_persistency(true) ) ώστε να αποθηκεύετε αυτόµατα µε το save_world π.χ. int_ tag >s e t _ p e r s i s t e n c y ( true ) ; 4. Προσθέτουµε το νεο Tag στον κόµβο µε την συνάρτηση owner_w().add_tag( ); π.χ. owner_w ( ). add_tag ( i n t _t a g ) ; Πώς ϕτιάχνουµε ένα νέο Tag Πώς διαβάζουµε/θέτουµε τιµή σε ένα Tag Παραδείγµατα αρχικοποίησης διαφορετικών Simple Tags Integer Tag : shawn : : IntegerTag i n t _t a g ; int_tag = new shawn : : IntegerTag ( "tag_name ", value ) ; int_ tag >s e t _ p e r s i s t e n c y ( true ) ; owner_w ( ). add_tag ( i n t _t a g ) ; Boolean Tag : shawn : : BoolTag bool_tag ; bool_tag = new shawn : : BoolTag ( "tag_name ", value ) ; owner_w ( ). add_tag ( bool_tag ) ; String Tag : shawn : : S t r i n g r T a g s t r i n g _t a g ; s t r i n g _t a g = new shawn : : S t r i n g T a g ( "tag_name ", value ) ; owner_w ( ). add_tag ( s t r i n g _t a g ) ; Στην περίπτωση που έχουµε ένα δείκτη στην µεταβλητή Tag : Καλούµε την µέθοδο set_value() για να ϑέσουµε µια νέα τιµή Καλούµε την µέθοδο value() για να πάρουµε την τιµή Παράδειγµα χρήσης Tag int_tag >set_value ( 4 2 ) ; i n t int_value = int_tag >value ( ) ; string_tag >set_value ( " t e s t " ) ; S t r i n g s t r i n g _v a l u e = string_tag >value ( ) ; bool_tag >s e t _v a l u e ( t r u e ) ; bool bool_value = bool_tag >value ( ) ;
Πώς ϐρίσκουµε ένα υπάρχων Tag από το αρχείο τοπολογίας Οταν υπάρχουν Tag στο αρχείο τοπολογίας (π.χ topology.xml) τότε ϕορτώνονται από τον εξοµοιωτή πριν ξεκινήσει η εξοµοίωση Σε αυτή την περίπτωση ξέρουµε µόνο το όνοµα του Tag και ϑέλουµε να πάρουµε την τιµή του: Καλούµε την µέθοδο owner_w().find_tag_w("tag name") για να πάρουµε το TagHandle που περιέχει το Tag shawn : : TagHandle tag = owner_w ( ). find_tag_w ( " parent " ) ; Καλούµε την µέθοδο tag.get() και κάνουµε dynamic_cast για να πάρουµε το πραγµατικό Tag shawn : : S t r i n g T a g parenttag = dynamic_cast <shawn : : S t r i n g T a g >( tag. get ( ) ) ; Καλούµε την µέθοδο set_value(), value() για να πάρουµε/θέσουµε τιµή s t r i n g _t a g >set_value ( " v0 " ) ; s t r i n g s t r i n g _ v a l u e = in t _ ta g >value ( ) Προσπέλαση των Tags από το αρχείο τοπολογίας Παράδειγµα προσπέλασης Tag από topology.xml shawn : : TagHandle tag = node. find_tag_w ( " i n p u t " ) ; / / check i f the tag e x i s t and has been added to the node i f ( tag. i s _ n o t _ n u l l ( ) ) { / / cast to the s p e c i f i c Tag type i n p u t _ t a g = dynamic_cast <shawn : : IntegerTag >( tag. get ( ) ) ; input_tag >s e t _ v a l u e ( 42 ) ; input_tag >s e t _ p e r s i s t e n c y ( true ) ; Tags Example Κατεβάζουµε το ϕάκελο του FloodSet-template και των αρχείων τοπολογίας που περιέχουν: http : / /www. ceid. upatras. gr / courses /katanemhmena/ w i k i /images / 4/48/SHAWN Floodset Template. t a r. bz2 http : / /www. ceid. upatras. gr / courses /katanemhmena/ w i k i /images / 9/9b/SHAWN FullyConnected Topologies withvals. tar. bz2 Αντιγράφουµε τον ϕάκελο στην ϑέση shawn/src/legacyapps/ ίνουµε την εντολή ccmake../src στον ϕάκελο shawn/buildfiles και µετά: c, για να ενηµερωθεί για τον νέο κώδικα που προσθέσατε Παρατηρούµε πως έχει εµφανιστεί µια νέα επιλογή στην πρώτη ϑέση µε το όνοµα της εφαρµογής Ενεργοποιήστε την επιλογή (πατώντας enter) c, για να ενηµερωθεί για τις αλλαγές g, για να αποθηκεύσει τις αλλαγές Τέλος δώστε την εντολή make για να γίνει compile to shawn Tags Example Θα τροποποιήσετε το processor.cpp στο shawn/src/legacyapps/floodset ώστε να ϕορτώνει την µεταβλητή input_value_ από το Tag "input_value" του "findval-topology-10-4.xml" : <snapshot i d ="0" >... <node i d = " v0 56cHXB C" > < l o c a t i o n x = " 2. 9 5 7 4 " y = " 0. 2 4 6 3 0 8 " z = " 0 " / > <tag type = " i n t " name= " input_ value " value = " 4690" /> </node> <node i d = " v1 56cHXB D" > < l o c a t i o n x = " 1. 6 2 0 4 4 " y = " 3. 4 1 8 3 " z = " 0 " / > <tag type = " i n t " name= " input_ value " value = " 537" /> </node.... </ snapshot >
Tags Example Tags Example 1. ηλώστε 1 µεταβλητή τύπου shawn::integertag* στο processor.h µε όνοµα input_tag_: shawn : : IntegerTag input_tag_ ; 2. ηλώστε 1 µεταβλητή τύπου int στο processor.h µε όνοµα input_value_: i n t i n p u t _value_ ; 3. ιαβάζουµε την τιµή του tag για κάθε διεργασία στην boot() shawn : : TagHandle tag = owner_w ( ). find_tag_w ( " i n p u t _v a l u e " ) ; i f ( tag. i s _ n o t _ n u l l ( ) ) { i n p u t _ t a g _ = dynamic_cast <shawn : : IntegerTag >( tag. get ( ) ) ; input_value_ = input_tag_ >value ( ) ; std : : cout << "Node " << id ( ) << " input_value " << input_value_ << s t d : : endl ; Τροποποιήστε το fooldset.conf ώστε να χρήσιµοποιεί το findval-topology-10-4.xml και εκτελέστε µια εξοµοίωση Στην έξοδο πρέπει να ϐλέπετε τα παρακάτω: BEGIN ITERATION 0 Node 0 i n p u t _ v a l u e 4690 Node 1 i n p u t _ v a l u e 537 Node 2 i n p u t _ v a l u e 2569 Node 3 i n p u t _ v a l u e 7239 Node 4 i n p u t _ v a l u e 5281 Node 5 i n p u t _ v a l u e 7739 Node 6 i n p u t _ v a l u e 3546 Node 7 i n p u t _ v a l u e 3761 Node 8 i n p u t _ v a l u e 4271 Node 9 i n p u t _ v a l u e 30 DONE ITERATION 0 [ 10 active, 0 sleeping, 0 inactive ] Επισκόπηση FooldSet - Περιγραφή του προβλήµατος Τα µηνύµατα στο Shawn Χειρισµός διαφορετικών τύπων µηνυµάτων Rerun exactly the same Simulation Reading Tags from topology file Example 4η Ασκηση FooldSet Υλοποίηση του αλγορίθµου FooldSet: 1. Κάθε διεργασία ϑα διαβάζει την αρχική της τιµή από το αρχείο µε την τοπολογία µε όνοµα (input_value). 2. Θα διατηρεί ένα πίνακα/λίστα µε τις τιµές όλων των διεργασιών που γνωρίζει. 3. Σε κάθε γύρο ϑα στέλνει αυτή την λίστα. 4. Οταν λαµβάνει µια λίστα ϑα την ενοποιεί µε την δικιά της. 5. Το κριτήριο απόφασης που ϑα χρησιµοποιήσετε είναι το κριτήριο πλειοψηφίας, σε περίπτωση ισοβαθµίας επιλέγετε την µικρότερη τιµή.
FooldSet - Περιγραφή του προβλήµατος FooldSet - Υλοποίηση Για την άσκηση σας Ϲητάτε: 1. Να υλοποιήσετε τον FooldSet. 2. Θα τρέξετε τον αλγόριθµό σας για: 3 τοπολογίες µε όνοµα (findval-topology-*-*.xml) Για πιθανότητα σφάλµατος {0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9 3. Να µετρήσετε τον αριθµό γύρων και µηνυµάτων µέχρι ο αλγόριθµός να αποφασίσει για κάθε από τις παραπάνω περιπτώσεις. 4. Να τροποποιήσετε τον αλγόριθµο ώστε να λαµβάνει υπόψιν του την τιµή της FailProbability για να τερµατίζει εξασφαλίζοντας ότι οι διεργασίες συµφωνούν σε µια κοινή τιµή. Μπορείτε να ϑεωρήσετε γνωστό το αριθµό των κόµβων (Για τις τρεις τοπολογίες είναι 10-100 και 1000 αντίστοιχα). Σας δίνετε ένα έτοιµο µήνυµα να χρησιµοποιήσετε µε το οποίο µπορείτε να στείλετε ένα πίνακα (array από τιµές): / / sender i n t arrayofvalues = new i n t [ s i z e ] ; send (new floodsetmessage ( arrayofvalues, s i z e ) ) / / r e c e i v e r i n t arrayofvalues = floodsetmessage. values ( ) ; Στον template κώδικα που σας δίνετε υπάρχει ένα conf αρχείο που µπορείτε να χρησιµοποιήσετε σαν παράδειγµα, αν κατασκευάσετε δικό σας πρέπει να ϑέσετε range=200 ώστε η τοπολογία να είναι πλήρης γράφος.