Βελτιστοποίηση και Παραλληλοποίηση Κώδικα για Αλγορίθμους Γράφων ΔΙΠΛΩΜΑΤΙΚΗ ΕΡΓΑΣΙΑ

Σχετικά έγγραφα
Παρουσίαση 2 ης Άσκησης:

Προβλήματα, αλγόριθμοι, ψευδοκώδικας

Παρουσίαση 1 ης Άσκησης:

Συστήματα μνήμης και υποστήριξη μεταφραστή για MPSoC

Εισαγωγικές Έννοιες. ημήτρης Φωτάκης. Σχολή Ηλεκτρολόγων Μηχανικών και Μηχανικών Υπολογιστών. Εθνικό Μετσόβιο Πολυτεχνείο

Ασκήσεις Caches

Ασκήσεις Caches

Διακριτά Μαθηματικά ΙΙ Χρήστος Νομικός Τμήμα Μηχανικών Η/Υ και Πληροφορικής Πανεπιστήμιο Ιωαννίνων 2018 Χρήστος Νομικός ( Τμήμα Μηχανικών Η/Υ Διακριτά

Άσκηση 1η. Θεωρήστε ένα σύστημα μνήμης με μία cache: 4 way set associative μεγέθους 256ΚΒ,

Ασκήσεις Caches. Αρχιτεκτονική Υπολογιστών. 5ο εξάμηνο ΣΗΜΜΥ ακ. έτος: Νεκ. Κοζύρης

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ

Πανεπιστήμιο Θεσσαλίας Τμήμα Μηχανικών Η/Υ, Τηλεπικοινωνιών και Δικτύων

Οργάνωση επεξεργαστή (2 ο μέρος) ΜΥΥ-106 Εισαγωγή στους Η/Υ και στην Πληροφορική

Ειδικά θέματα Αλγορίθμων και Δομών Δεδομένων (ΠΛΕ073) Απαντήσεις 1 ου Σετ Ασκήσεων

Αλγόριθμοι και Πολυπλοκότητα

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΣΧΟΛΗ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΤΟΜΕΑΣ ΤΕΧΝΟΛΟΓΙΑΣ ΠΛΗΡΟΦΟΡΙΚΗΣ ΚΑΙ ΥΠΟΛΟΓΙΣΤΩΝ

Παράλληλος προγραμματισμός περιστροφικών αλγορίθμων εξωτερικών σημείων τύπου simplex ΠΛΟΣΚΑΣ ΝΙΚΟΛΑΟΣ

Αρχιτεκτονική Υπολογιστών

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΣΧΟΛΗ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΤΟΜΕΑΣ ΤΕΧΝΟΛΟΓΙΑΣ ΠΛΗΡΟΦΟΡΙΚΗΣ ΚΑΙ ΥΠΟΛΟΓΙΣΤΩΝ

9. Συστολικές Συστοιχίες Επεξεργαστών

Κεφάλαιο 5 Ανάλυση Αλγορίθμων

Κεφάλαιο 7 Ιεραρχία Μνήμης (Memory Hierarchy)

Αλγόριθμοι και Δομές Δεδομένων (Ι) (εισαγωγικές έννοιες)

Σημειωματάριο Δευτέρας 4 Δεκ. 2017

i Στα σύγχρονα συστήματα η κύρια μνήμη δεν συνδέεται απευθείας με τον επεξεργαστή

Μετρικές & Επιδόσεις. Κεφάλαιο V

Τι είναι αλγόριθμος; Υποπρογράμματα (υποαλγόριθμοι) Βασικές αλγοριθμικές δομές

Graph Algorithms. Παρουσίαση στα πλαίσια του μαθήματος «Παράλληλοι Αλγόριθμοι» Καούρη Γεωργία Μήτσου Βάλια

Ασκήσεις στα Προηγμένα Θέματα Αρχιτεκτονικής Υπολογιστών

Τμήμα Οικιακής Οικονομίας και Οικολογίας. Οργάνωση Υπολογιστών

Υπολογιστικό Πρόβληµα

Ψευδοκώδικας. November 7, 2011

Εισαγωγή στην πληροφορική

ΑΕΠΠ Ερωτήσεις θεωρίας

Τεχνολογίες Κύριας Μνήμης

Αλγόριθμοι και Δομές Δεδομένων (IΙ) (γράφοι και δένδρα)

ΤΕΧΝΟΛΟΓΙΚΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΚΥΠΡΟΥ ΣΧΟΛΗ ΜΗΧΑΝΙΚΗΣ ΚΑΙ ΤΕΧΝΟΛΟΓΙΑΣ. Πτυχιακή εργασία

Κεφάλαιο 4ο: Δικτυωτή Ανάλυση

All Pairs Shortest Path

FORTRAN και Αντικειμενοστραφής Προγραμματισμός

ΓΡΑΜΜΙΚΟΣ & ΔΙΚΤΥΑΚΟΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ

Δομές Δεδομένων & Αλγόριθμοι

Πληροφορική 2. Δομές δεδομένων και αρχείων

Προγραμματισμός Η/Υ 1 (Εργαστήριο)

ΚΕΦΑΛΑΙΟ 5. Κύκλος Ζωής Εφαρμογών ΕΝΟΤΗΤΑ 2. Εφαρμογές Πληροφορικής. Διδακτικές ενότητες 5.1 Πρόβλημα και υπολογιστής 5.2 Ανάπτυξη εφαρμογών

3η Σειρά Γραπτών Ασκήσεων

Πανεπιστήμιο Θεσσαλίας Τμήμα Ηλεκτρολόγων Μηχανικών & Μηχανικών Υπολογιστών Τμήμα Πληροφορικής

ΠΛΕ- 027 Μικροεπεξεργαστές 6ο μάθημα: Αρχιτεκτονική πυρήνα: υλοποίηση με διοχέτευση

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

Προγραμματισμός συστημάτων UNIX/POSIX. Θέμα επιλεγμένο από τους φοιτητές: Προγραμματιστικές τεχνικές που στοχεύουν σε επιδόσεις

ΕΘΝΙKΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΣΧΟΛΗ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΕΡΓΑΣΤΗΡΙΟ ΥΠΟΛΟΓΙΣΤΙΚΩΝ ΣΥΣΤΗΜΑΤΩΝ. Ονοματεπώνυμο: ΑΜ:

Τμήμα Ηλεκτρολόγων Μηχανικών και Μηχανικών H/Y Department of Electrical and Computer Engineering. Εργαστήριο 8. Χειμερινό Εξάμηνο

ΕΙΣΑΓΩΓΗ ΣΤΙΣ ΑΡΧΕΣ ΤΗΣ ΕΠΙΣΤΗΜΗΣ ΤΩΝ Η/Υ

Θέματα Μεταγλωττιστών

Κύρια μνήμη. Μοντέλο λειτουργίας μνήμης. Ένα τυπικό υπολογιστικό σύστημα σήμερα. Οργάνωση Υπολογιστών (ΙI)

Υπάρχουν δύο τύποι μνήμης, η μνήμη τυχαίας προσπέλασης (Random Access Memory RAM) και η μνήμη ανάγνωσης-μόνο (Read-Only Memory ROM).

Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών Κρυφές Μνήμες. (οργάνωση, λειτουργία και απόδοση)

Για τις λύσεις των προβλημάτων υπάρχει τρόπος εκτίμησης της επίδοσης (performance) και της αποδοτικότητας (efficiency). Ερωτήματα για την επίδοση

Παρουσίαση 2 ης Άσκησης:

Ποσοτικές Μέθοδοι στη Διοίκηση Επιχειρήσεων ΙΙ Σύνολο- Περιεχόμενο Μαθήματος

Οι βασικές λειτουργίες (ή πράξεις) που γίνονται σε μια δομή δεδομένων είναι:

Κατευθυνόμενα γραφήματα. Μαθηματικά Πληροφορικής 6ο Μάθημα. Βρόχοι. Μη κατευθυνόμενα γραφήματα. Ορισμός

Πανεπιστήμιο Θεσσαλίας Τμήμα Ηλεκτρολόγων Μηχανικών & Μηχανικών Υπολογιστών

Εισαγωγή στους Αλγόριθµους. Αλγόριθµοι. Ιστορικά Στοιχεία. Ο πρώτος Αλγόριθµος. Παραδείγµατα Αλγορίθµων. Τι είναι Αλγόριθµος

ΗΥ562 Προχωρημένα Θέματα Βάσεων Δεδομένων Efficient Query Evaluation over Temporally Correlated Probabilistic Streams

ΕΙΣΑΓΩΓΗ ΣΤΗΝ ΑΝΑΛΥΣΗ ΑΛΓΟΡΙΘΜΩΝ

Διδακτικά προβλήματα σχετικά με την έννοια της επανάληψης

Λειτουργικά Συστήματα Η/Υ

Α Ν Α Λ Τ Η Α Λ Γ Ο Ρ Ι Θ Μ Ω Ν Κ Ε Υ Α Λ Α Ι Ο 5. Πως υπολογίζεται ο χρόνος εκτέλεσης ενός αλγορίθμου;

Κατευθυνόμενα γραφήματα. Μαθηματικά Πληροφορικής 6ο Μάθημα. Βρόχοι. Μη κατευθυνόμενα γραφήματα. Ορισμός

Οργάνωση Υπολογιστών (ΙI)

ΠΕΡΙΕΧΟΜΕΝΑ. Μονοδιάστατοι πίνακες Πότε πρέπει να χρησιμοποιούνται πίνακες Πολυδιάστατοι πίνακες Τυπικές επεξεργασίες πινάκων

Λειτουργικά Συστήματα 7ο εξάμηνο, Ακαδημαϊκή περίοδος

Στοχαστικές Στρατηγικές. διαδρομής (1)

Θεωρία Γραφημάτων 6η Διάλεξη

Σειρά Προβλημάτων 5 Λύσεις

Ενδεικτικές Ερωτήσεις Θεωρίας

Παράλληλη Επεξεργασία Κεφάλαιο 1 Γιατί Παράλληλος Προγραμματισμός;

Ιεραρχία Μνήμης. Ιεραρχία μνήμης και τοπικότητα. Σκοπός της Ιεραρχίας Μνήμης. Κρυφές Μνήμες

ΕΙΣΑΓΩΓΗ ΣΤΗΝ ΑΝΑΛΥΣΗ ΑΛΓΟΡΙΘΜΩΝ

Κεφ.11: Ευρετήρια και Κατακερματισμός

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

Δυναμικός προγραμματισμός για δέντρα

Εισαγωγή στην Επεξεργασία Ερωτήσεων. Βάσεις Δεδομένων Ευαγγελία Πιτουρά 1

3η Σειρά Γραπτών Ασκήσεων

Αλγόριθμοι και Πολυπλοκότητα

Μάθημα 8: Επικοινωνία Συσκευών με τον Επεξεργαστή

5 ΕΙΣΑΓΩΓΗ ΣΤΗ ΘΕΩΡΙΑ ΑΛΓΟΡΙΘΜΩΝ

5. Απλή Ταξινόμηση. ομές εδομένων. Χρήστος ουλκερίδης. Πανεπιστήμιο Πειραιώς Σχολή Τεχνολογιών Πληροφορικής και Επικοινωνιών Τμήμα Ψηφιακών Συστημάτων

ΠΑΝΕΠΙΣΤΗΜΙΟ ΘΕΣΣΑΛΙΑΣ ΣΧΟΛΗ ΘΕΤΙΚΩΝ ΕΠΙΣΤΗΜΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ

ΔΙΑΣΧΙΣΗ ΓΡΑΦΗΜΑΤΩΝ 1

Προγραμματισμός Ι (HY120)

Κεφάλαιο 11 Ένωση Ξένων Συνόλων

Πληροφορική 2. Αλγόριθμοι

Πανεπιστήμιο Δυτικής Μακεδονίας. Τμήμα Μηχανικών Πληροφορικής & Τηλεπικοινωνιών. Διακριτά Μαθηματικά. Ενότητα 4: Εισαγωγή / Σύνολα

i Throughput: Ο ρυθμός ολοκλήρωσης έργου σε συγκεκριμένο χρόνο

Λύσεις Παλιών Θεµάτων. Συστήµατα Παράλληλης Επεξεργασίας, 9ο εξάµηνο Υπεύθ. Καθ. Νεκτάριος Κοζύρης

Αριθμητική εύρεση ριζών μη γραμμικών εξισώσεων

Transcript:

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΣΧΟΛΗ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΤΟΜΕΑΣ ΤΕΧΝΟΛΟΓΙΑΣ ΠΛΗΡΟΦΟΡΙΚΗΣ & ΥΠΟΛΟΓΙΣΤΩΝ Βελτιστοποίηση και Παραλληλοποίηση Κώδικα για Αλγορίθμους Γράφων ΔΙΠΛΩΜΑΤΙΚΗ ΕΡΓΑΣΙΑ Γεώργιος Κ. Βακαλόπουλος Επιβλέπων : Νεκτάριος Κοζύρης Αναπληρωτής Καθηγητής ΕΜΠ Αθήνα, Σεπτέμβριος 2008

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΣΧΟΛΗ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΤΟΜΕΑΣ ΤΕΧΝΟΛΟΓΙΑΣ ΠΛΗΡΟΦΟΡΙΚΗΣ & ΥΠΟΛΟΓΙΣΤΩΝ Βελτιστοποίηση και Παραλληλοποίηση Κώδικα για Αλγορίθμους Γράφων ΔΙΠΛΩΜΑΤΙΚΗ ΕΡΓΑΣΙΑ Γεώργιος Κ. Βακαλόπουλος Επιβλέπων : Νεκτάριος Κοζύρης Αναπληρωτής Καθηγητής ΕΜΠ Εγκρίθηκε από την τριμελή εξεταστική επιτροπή την 30 η Σεπτεμβρίου 2008.... Νεκτάριος Κοζύρης Αναπληρωτής Καθηγητής ΕΜΠ... Ευστάθιος Ζάχος Καθηγητής ΕΜΠ... Τιμολέων Σελλής Καθηγητής ΕΜΠ Αθήνα, Σεπτέμβριος 2008

... Γεώργιος Κ. Βακαλόπουλος Διπλωματούχος Ηλεκτρολόγος Μηχανικός και Μηχανικός Υπολογιστών Ε.Μ.Π. Copyright Γεώργιος Βακαλόπουλος, 2008 Με επιφύλαξη παντός δικαιώματος. All rights reserved. Απαγορεύεται η αντιγραφή, αποθήκευση και διανομή της παρούσας εργασίας, εξ ολοκλήρου ή τμήματος αυτής, για εμπορικό σκοπό. Επιτρέπεται η ανατύπωση, αποθήκευση και διανομή για σκοπό μη κερδοσκοπικό, εκπαιδευτικής ή ερευνητικής φύσης, υπό την προϋπόθεση να αναφέρεται η πηγή προέλευσης και να διατηρείται το παρόν μήνυμα. Ερωτήματα που αφορούν τη χρήση της εργασίας για κερδοσκοπικό σκοπό πρέπει να απευθύνονται προς τον συγγραφέα. Οι απόψεις και τα συμπεράσματα που περιέχονται σε αυτό το έγγραφο εκφράζουν τον συγγραφέα και δεν πρέπει να ερμηνευθεί ότι αντιπροσωπεύουν τις επίσημες θέσεις του Εθνικού Μετσόβιου Πολυτεχνείου.

Περίληψη Σκοπός της εργασίας αυτής είναι η βέλτιστη δυνατή υλοποίηση και παραλληλοποίηση αλγορίθμων γράφων. Οι γραφοθεωρητικοί αλγόριθμοι βρίσκουν ολοένα περισσότερες εφαρμογές, ενώ με την πρόοδο της τεχνολογίας η ανάγκη για παράλληλους αλγορίθμους γίνεται επιτακτική. Για τον σκοπό αυτό εξετάζουμε την έννοια της βελτιστοποίησης, κυρίως από την σκοπιά της διαχείρισης της μνήμης καθώς και μερικές βασικές έννοιες παράλληλων συστημάτων. Επικεντρώνουμε την προσοχή μας στον αλγόριθμο των Floyd Warshall που επιλύει το πρόβλημα All Pair Shortest Path. Το πρόβλημα αυτό είναι ιδιαίτερα σημαντικό στην βιβλιογραφία με ποικίλες εφαρμογές. Μελετούμε τις ιδιαιτερότητες του αλγορίθμου ώστε να σχεδιάσουμε παραλλαγές του που να εκμεταλλεύονται καλύτερα την διαθέσιμη μνήμη ώστε να εκτελούνται πιο γρήγορα. Στην συνέχεια εφαρμόζουμε τις ίδιες ιδέες για σχεδιασμό αντίστοιχων παράλληλων αλγορίθμων. Τα πειραματικά αποτελέσματα είναι ιδιαίτερα ενθαρρυντικά και για περαιτέρω έρευνα. Λέξεις κλειδιά αλγόριθμοι γράφων, βελτιστοποίηση, παράλληλα συστήματα, Floyd Warshall, ιεραρχία μνήμης, κρυφή μνήμη 5

6

Abstract The purpose of this work is the best possible implementation and parallelization of graph algorithms. Theoretical graph algorithms have more and more applications, while the progress of technology makes urgent the need for parallel algorithms. For this purpose we examine optimization techniques, mainly from the perspective of memory management as well as fundamental concepts of parallel systems. We focus on Floyd Warshall algorithm which solves the All Pair Shortest Path Problem. This problem is very important in the bibliography with lots of applications. We investigate the specific details of the algorithm in order to design variants of the algorithm such that can take advantage of the available memory and execute faster. Subsequently we apply the same ideas for the design of parallel algorithms. Experimental results are more than encouraging for further research Keywords graph algorithms, optimization, parallel systems, Floyd Warshall, memory hierarchy, cache memory 7

8

Ευχαριστίες Ολοκληρώνοντας τις σπουδές μου με την παρούσα διπλωματική εργασία στην Σχολή Ηλεκτρολόγων Μηχανικών & Μηχανικών Υπολογιστών θα ήθελα να ευχαριστήσω όλους εκείνους που, καταβάλλοντας φιλότιμες προσπάθειες, διατηρούν ακόμα υψηλό το επίπεδο των σπουδών στο Ε. Μ. Πολυτεχνείο και μας δίνουν την δυνατότητα να μετάσχουμε κι εμείς. Ευχαριστώ τον κ. Νεκτάριο Κοζύρη για την ευκαιρία που μου έδωσε τόσο με την παρούσα εργασία όσο και με την θέση του Υ/Δ στο εργαστήριό του. Η καθοδήγηση και η βοήθειά του είναι ιδιαίτερα σημαντική για μένα και θα είναι ακόμα περισσότερο στο μέλλον. Από την σκοπιά αυτή θέλω να ευχαριστήσω και τον κ. Γιώργο Γκούμα ο οποίος ως μεταδιδακτορικός ερευνητής με βοήθησε σημαντικά στην εκπόνηση της διπλωματικής μου εργασίας. Τέλος ιδιαίτερη μνεία πρέπει να γίνει στους κ.κ. Στάθη Ζάχο, Τίμο Σελλή και Άρη Παγουρτζή οι οποίοι καθ όλη την διάρκεια των σπουδών μου, μου συμπαραστάθηκαν και ήταν αρωγοί σε όλες μου τις προσπάθειες. Η συμβολή τους τόσο στην έως τώρα όσο και στην περαιτέρω πορεία μου κρίνεται πολύτιμη και καθοριστική. 9

10

Περιεχόμενα 0. Εισαγωγή...11 1. Περί Βελτιστοποίησης...15 2. Περί Παραλληλοποίησης...19 3. Το πρόβλημα & ο αλγόριθμος...23 3.1 Μερικοί ορισμοί...23 3.2 Παράσταση γράφων...24 3.3 Το πρόβλημα...25 4. Εξαρτήσεις Δεδομένων...29 5. Υλοποίηση Σειριακού Αλγορίθμου...33 5.1 Απλός αλγόριθμος...35 5.2 Αναστροφή...37 5.3 IKJ...39 5.4 IBKJ...43 5.5 KBIJ...45 5.6 Tiled...47 5.7 BKBIJ...49 6. Υλοποίηση Παράλληλου Αλγόριθμου...51 6.1 Απλός αλγόριθμος...53 6.2 IKJ...55 6.3 BKBIJ...57 6.4 Tiled...59 7. Επιδόσεις...61 7.1 Επιδόσεις Σειριακών μεθόδων...63 7.2 Επιδόσεις Παράλληλων μεθόδων...65 8. Βιβλιογραφικές αναφορές...69 11

Ευρετήριο Διαγραμμάτων Διάγραμμα 1: Εξαρτήσεις στο (ij) επίπεδο...29 Διάγραμμα 2: Εξαρτήσεις στο (ki) επίπεδο...30 Διάγραμμα 3: Απλός Floyd Warshall...35 Διάγραμμα 4: Αναστροφή...37 Διάγραμμα 5: IKJ...39 Διάγραμμα 6: IBKJ κάτω τμήμα...43 Διάγραμμα 7: IBKJ πάνω τμήμα...44 Διάγραμμα 8: ΚBIJ κάτω τμήμα...45 Διάγραμμα 9: KBIJ πάνω τμήμα...46 Διάγραμμα 10: Tiled...48 Διάγραμμα 11: BKBIJ...49 Διάγραμμα 12: Απλός Floyd Warshall Παράλληλος...53 Διάγραμμα 13: IKJ Parallel κάτω τμήμα...56 Διάγραμμα 14: IKJ Parallel πάνω τμήμα...56 Διάγραμμα 15: BKBIJ Parallel...58 Διάγραμμα 16: Tiled Parallel πρώτη φάση...60 Διάγραμμα 17: Tiled Parallel δεύτερη φάση...60 Διάγραμμα 18: Επιδόσεις Σειριακών μεθόδων...63 Διάγραμμα 19: Χρόνος παράλληλων για χωρίο 2520 κόμβων...65 Διάγραμμα 20: Επιτάχυνση παράλληλων για χωρίο 2520 κόμβων...65 Διάγραμμα 21: Χρόνος παράλληλων για χωρίο 5040 κόμβων...66 Διάγραμμα 22: Επιτάχυνση παράλληλων για χωρίο 5040 κόμβων...66 Διάγραμμα 23: Χρόνος παράλληλων για χωρίο 7560 κόμβων...67 Διάγραμμα 24: Επιτάχυνση παράλληλων για χωρίο 7560 κόμβων...67 12

0. Εισαγωγή Η παρούσα εργασία διερευνά τις δυνατότητες βελτιστοποίησης και παραλληλοποίησης εκτελέσιμου κώδικα για αλγορίθμους της θεωρίας γραφημάτων. Στο κεφάλαιο 1 αναπτύσσονται θεμελιώδη στοιχεία σχετικά με την βελτιστοποίηση κώδικα με ιδιαίτερη έμφαση στην διαχείριση της ιεραρχίας μνήμης που διαθέτουν οι σύγχρονοι επεξεργαστές. Στο κεφάλαιο 2 παρουσιάζονται βασικές αρχές της αρχιτεκτονικής δομής και του προγραμματιστικού μοντέλου των παράλληλων συστημάτων. Στο κεφάλαιο 3 αναφέρονται βασικοί ορισμοί από την θεωρία γραφημάτων και παρουσιάζεται ο βασικός αλγόριθμος με τον οποίο ασχολείται η υπόλοιπη εργασία. Στο κεφάλαιο 4 εξετάζονται οι εξαρτήσεις δεδομένων που παρουσιάζει ο αλγόριθμος του προηγούμενου κεφαλαίου, οι οποίες καθορίζουν τα πλαίσια στα οποία θα κινηθεί η βελτιστοποίηση και η παραλληλοποίησή του. Στο κεφάλαιο 5 προτείνονται νέες αλλά παρουσιάζονται και γνωστές από την βιβλιογραφία παραλλαγές τροποποιήσεις του αρχικού αλγορίθμου Στο κεφάλαιο 6 γίνεται η παραλληλοποίηση των κυριοτέρων μεθόδων του προηγούμενου κεφαλαίου Στο κεφάλαιο 7 παρατίθενται και συγκρίνονται τα πειραματικά αποτελέσματα από την εκτέλεση όλων των προηγούμενων μεθόδων 13

14

1. Περί Βελτιστοποίησης Σύμφωνα με τον νόμο του Moore η πυκνότητα των τρανζίστορ στους επεξεργαστές διπλασιάζεται κάθε 12-18 μήνες με αποτέλεσμα να αυξάνεται αντίστοιχα η ταχύτητά τους. Αντίθετα η ταχύτητα πρόσβασης στην μνήμη αυξάνεται πολύ πιο αργά, με ρυθμό περίπου 7% τον χρόνο ενώ η χωρητικότητα της μνήμης αυξάνεται και αυτή πολύ γρήγορα. Η μεγάλη διαφορά στην αύξηση της ταχύτητας επεξεργαστή - μνήμης είναι γνωστή στην βιβλιογραφία ως processor memory gap. Αλγόριθμοι οι οποίοι έχουν μεγάλες απαιτήσεις δεδομένων από την μνήμη περιορίζονται σημαντικά εξαιτίας του processor memory gap. Σύμφωνα με την αρχή τοπικότητας τα προγράμματα προσπελαύνουν ένα σχετικά μικρό τμήμα του χώρου διευθύνσεων τους σε κάθε χρονική στιγμή. Η προσπέλαση αυτή παρουσιάζει χωρική και χρονική τοπικότητα: Χρονική Τοπικότητα [temporal locality]: το πρόγραμμα κάνει διαδοχικές αναφορές στα ίδια δεδομένα σε διαδοχικές εντολές Χωρική Τοπικότητα [spatial locality]: το πρόγραμμα κάνει διαδοχικές αναφορές σε γειτονικά δεδομένα κατά την εκτέλεση διαδοχικών εντολών. Η τοπι κότητα στα προγράμματα προέρχεται από απλές και φυσικές δομές προγραμμάτων. Για παράδειγμα, τα περισσότερα προγράμματα περιέχουν βρόχους, οπότε οι εντολές και τα δεδομένα είναι πιθανό να προσπελάζονται κατ επανάληψη, παρουσιάζοντας μεγάλο βαθμό χρονικής τοπικότητας. Εφόσον οι εντολές κανονικά προσπελάζονται με την σειρά, τα προγράμματα παρουσιάζουν υψηλή χωρική τοπικότητα. Οι προσβάσεις στα δεδομένα παρουσιάζουν επίσης μια φυσική χωρική τοπικότητα, Για παράδειγμα, οι προσβάσεις στα στοιχεία ενός πίνακα ή μιας εγγραφής φυσιολογικά παρουσιάζουν υψηλό βαθμό χωρικής τοπικότητας. 15

Για να καλυφθεί το κενό που αναφέρθηκε παραπάνω και για να εκμεταλλευτούμε την αρχή της τοπικότητας, η μνήμη ενός υπολογιστή υλοποιείται ως μία ιεραρχία μνήμης. Μια ιεραρχία μνήμης αποτελείται από πολλά επίπεδα μνήμης με διαφορετικές ταχύτητες και μεγέθη. Το τελευταίο επίπεδο είναι η μνήμη RAM του συστήματος, ενώ τα ενδιάμεσα επίπεδα λέγονται κρυφή μνήμη [cache memory]. Οι σύγχρονοι επεξεργαστές διαθέτουν τουλάχιστον δύο επίπεδα κρυφής μνήμης. Κάθε επίπεδο μνήμης γίνεται μικρότερο και πιο γρήγορο όσο πλησιάζουμε στον επεξεργαστή και φυσικά ακριβότερο ανά byte καθότι χρησιμοποιούνται διαφορετικές τεχνολογίες. Σε κάθε αναφορά στην μνήμη που δεν εξυπηρετείται από κάποιο επίπεδο μνήμης, αναζητούνται τα δεδομένα στα επόμενα επίπεδα έως την κύρια μνήμη. Όταν αυτά βρεθούν μεταφέρονται σε ολόκληρα block (cache line) προς τα παρακάτω επίπεδα όπου και διατηρούνται για να εξυπηρετήσουν μελλοντικές αναφορές στα ίδια δεδομένα. Σημαντικό ρόλο παίζουν παράμετροι της λειτουργίας της κρυφής μνήμης [write back/through, allocation, associativity] που είναι όμως συγκεκριμένες για κάθε επεξεργαστή. Βελτιστοποίηση από αυτή την σκοπιά είναι η καλύτερη δυνατή διαχείριση της ιεραρχίας της μνήμης ώστε να επαναχρησιμοποιούνται τα δεδομένα που βρίσκονται κοντά στον επεξεργαστή. Πρέπει δηλαδή να σχεδιάζουμε τους αλγορίθμους με όσο το δυνατόν περισσότερη χωρική και χρονική πολυπλοκότητα ώστε να μειώσουμε τις αναφορές στην κύρια μνήμη, οι οποίες κοστίζουν πολύ σε χρόνο, εκμεταλλευόμενοι τα δεδομένα που υπάρχουν στα ενδιάμεσα επίπεδα της κρυφής μνήμης. Οι αλγόριθμοι αξιολογούνται κυρίως από την χρονική πολυπλοκότητα [time complexity], δηλαδή από το πόσες λειτουργίες απαιτούνται για την εκτέλεσή τους ως συνάρτηση του μεγέθους της εισόδου. Κατά δεύτερο λόγο αξιολογούνται από την χωρική πολυπλοκότητα [space complexity], δηλαδή από την ποσότητα της μνήμης που απαιτείται. Η θεωρητική πληροφορική ασχολείται με τον σχεδιασμό αλγορίθμων και την μελέτη της ασυμπτωτικής πολυπλοκότητάς τους, δηλαδή την τάξη μεγέθους της πολυπλοκότητας και την συμπεριφορά της όταν μεγαλώνει πολύ το μέγεθος της εισόδου. Δεν είναι αντικείμενο μελέτης όμως η σειρά εκτέλεσης εντολών ανεξάρτητων μεταξύ τους και ο τρόπος που θα χρησιμοποιηθεί η μνήμη που απαιτείται. Οι δύο αυτοί παράγοντες επηρεάζουν σημαντικά την επίδοση του αλγορίθμου όταν εκτελείται σε έναν πραγματικό υπολογιστή, χωρίς βέβαια να αλλάζουν την ασυμπτωτική χρονική και χωρική πολυπλοκότητα. Αντίθετα οι σύγχρονοι επεξεργαστές διαθέτουν λειτουργίες pipeline και prefetching: Pipeline (διοχέτευση) είναι η δυνατότητα επικάλυψης των εντολών ώστε να εκτελούνται παράλληλα. Αυτό επιτυγχάνεται με την διάσπαση των εντολών σε μικρότερα τμήματα (μικροεντολές) τα οποία είναι ανεξάρτητα μεταξύ τους και εκτελούνται από διαφορετικό υποκύκλωμα του επεξεργαστή. Ο σχεδιασμός του επεξεργαστή και των εντολών του με τέτοιο τρόπο μας δίνει την δυνατότητα η εκτέλεση μιας εντολής να ξεκινάει προτού ολοκληρωθεί η προηγούμενη, αρκεί να μην υπάρχει εξάρτηση μεταξύ τους, π.χ. να χρειάζεται η επόμενη το αποτέλεσμα της 16

πρώτης (σε περιπτώσεις εντολών ανάθεσης, ελέγχου κλπ). Στην εφαρμογή της διοχέτευσης παρουσιάζονται διάφοροι κίνδυνοι, όπως δομικοί κίνδυνοι, κίνδυνοι δεδομένων & ελέγχου, οι οποίοι όμως αντιμετωπίζονται επιτυχώς. Prefetching (πρόωρη προσκόμιση δεδομένων) είναι η δυνατότητα του επεξεργαστή να φέρνει δεδομένα από την μνήμη προτού αναφερθούμε σε αυτά. Αυτό επιτυγχάνεται με την παρακολούθηση των αναφορών στην μνήμη. Όταν γίνονται διαδοχικές αναφορές σε διαδοχικές θέσεις της μνήμης, ο επεξεργαστής φέρνει τις επόμενες θέσεις προτού ζητηθούν εξοικονομώντας πολύτιμο χρόνο. Τα δεδομένα που έρχονται περιμένουν στην cache έως ότου όντως ζητηθούν από το πρόγραμμα. Οι μεταγλωττιστές (compilers) εκμεταλλεύονται τις παραπάνω δυνατότητες με γενικής φύσης τεχνικές όπως για παράδειγμα αναδιατάσσοντας εντολές ή ξεδιπλώνοντας βρόχους. Ωστόσο ούτε αυτά αρκούν καθότι το pipeline εξαρτάται εν μέρει από τον κώδικά μας (π.χ. διαδοχικές εγγραφές και αναγνώσεις στην ίδια μεταβλητή εμποδίζουν την ταυτόχρονη εκτέλεση των εντολών) ενώ το prefetching δεν μπορεί να καλύψει τις ανάγκες όλων των αλγορίθμων καθότι καθένας έχει το δικό του ιδιαίτερο memory access pattern. Άρα χρειαζόμαστε κώδικα που να λαμβάνει όλα τα παραπάνω υπόψιν με σημαντικότερο όλων την σωστή διαχείριση της ιεραρχίας της μνήμης, ειδικά όταν πρόκειται για αλγόριθμο με μεγάλες απαιτήσεις μνήμης. Έναν τέτοιο αλγόριθμο θα μελετήσουμε παρακάτω. Για την εκτίμηση της διαχείρισης της μνήμης, εισάγουμε την έννοια του memory traffic ως μέτρο της επίδοσης των αλγορίθμων, το οποίο αντιπροσωπεύει την ποσότητα των δεδομένων που ανταλλάσσονται με την κύρια μνήμη. Από τα παραπάνω γίνεται σαφής και η γνωστή άποψη ότι Computer theorists invent algorithms that solve important problems and analyze their asymptotic behavior. Computer architects set the constants factors for these algorithms. Από την σκοπιά αυτή εκπονείται η παρούσα εργασία. 17

18

2. Περί Παραλληλοποίησης Παρά την πρόοδο της τεχνολογίας και την κατασκευή ολοένα και καλύτερων και πιο γρήγορων επεξεργαστών (βλέπε Νόμο Moore) οι περισσότερες μεγάλες επιστημονικές και βιομηχανικές εφαρμογές απαιτούν εξαιρετικά μεγάλη υπολογιστική ισχύ. Για να εκτελεστούν σε πραγματικό χρόνο (κλασσικό παράδειγμα η πρόγνωση του καιρού) χρησιμοποιούμε συστοιχίες επεξεργαστών διασυνδεδεμένους μεταξύ τους ώστε να επικοινωνούν για την παράλληλη επίλυση του προβλήματος. Χαρακτηριστικές απαιτήσεις εφαρμογών σε υπολογιστική ισχύ φαίνονται στο παρακάτω σχήμα Η παραλληλία υπάρχει εξαρχής στην αρχιτεκτονική υπολογιστών: έως το 1985 η έμφαση δινόταν στην παραλληλία σε επίπεδο bit, καθώς διπλασιασμός των bit των λέξεων του επεξεργαστή σημαίνει επεξεργασία διπλάσιων δεδομένων στον ίδιο χρόνο. Σήμερα έχουμε φτάσει σε 64bit στους προσωπικούς υπολογιστές έως τα μέσα του 90 η έμφαση δινόταν στην παραλληλία σε επίπεδο εντολής (pipeline etc) πλέον η έμφαση δίνεται στην παραλληλία σε επίπεδο thread οπότε κάθε επεξεργαστής διαθέτει πολλαπλούς πυρήνες ή ακόμα και οι προσωπικοί υπολογιστές διαθέτουν πολλαπλούς επεξεργαστές που μπορούν να εκτελούν διαφορετικά threads. 19

Για την παραλληλοποίηση των υπολογισμών έχουν αναπτυχθεί διάφορες αρχιτεκτονικές καθώς και τα κατάλληλα προγραμματιστικά μοντέλα που διευκολύνουν ανάγκες συγχρονισμού και επικοινωνίας μεταξύ των επεξεργαστών. Αρχιτεκτονικές Ο κύριος στόχος κατά την σχεδίαση μιας αρχιτεκτονικής για παράλληλους υπολογισμούς, εκτός από την απόδοση, είναι η δυνατότητα επεκτασιμότητας με προσθήκη αργότερα νέων επεξεργαστικών μονάδων. Σημαντικοί στόχοι είναι επίσης η ευρεία εφαρμογή, ο εύκολος προγραμματισμός και το χαμηλό κόστος. Κάθε αρχιτεκτονική περιλαμβάνει και δίκτυο διασύνδεσης ανάμεσα στις μονάδες το οποίο επηρεάζει σημαντικά τόσο την απόδοση όσο και την επεκτασιμότητα του συστήματος Γνωστές αρχιτεκτονικές είναι το μοντέλο μοιραζόμενης μνήμης και το μοντέλο ανταλλαγής μηνυμάτων. Στην πρώτη περίπτωση όλοι οι επεξεργαστές βλέπουν τον ίδιο χώρο διευθύνσεων [κοινή κύρια μνήμη], προσπελαύνουν όποια δεδομένα θέλουν και η επικοινωνία τους προκύπτει έμμεσα, σαν αποτέλεσμα των αναγνώσεων/ εγγραφών στα κοινά δεδομένα. Στην δεύτερη περίπτωση κάθε επεξεργαστής αποτελεί μία πλήρη δομική μονάδα με δυνατότητες Ε/Ε που επικοινωνεί με τους υπόλοιπους μέσω μηνυμάτων πάνω στο δίκτυο διασύνδεσης. Καθένας έχει την δική του μνήμη. Άλλα μοντέλα είναι τα data parallel, συστολικές αρχιτεκτονικές, distributed shared memory. Προγραμματιστικά μοντέλα Τα προγραμματιστικά μοντέλα καθορίζουν την επικοινωνία και τον συγχρονισμό των επεξεργαστών. Είναι άρρηκτα συνδεδεμένα με την αρχιτεκτονική των συστημάτων, αλλά με την πρόοδο της τεχνολογίας τα όρια γίνονται δυσδιάκριτα καθώς υλοποιούνται δυνατότητες από διαφορετικές αρχιτεκτονικές στο ίδιο μοντέλο. Παραδείγματα μοντέλων: Multiprogramming Shared address space Message passing Data parallel Η αντιστοιχία των μοντέλων με τις αρχιτεκτονικές είναι προφανής Η βελτιστοποίηση από την σκοπιά αυτή έγκειται στον κατάλληλο σχεδιασμό του παράλληλου αλγόριθμου για την επίλυση ενός προβλήματος, δηλαδή την σωστή κατάτμηση του αρχικού προβλήματος σε άλλα που μπορούν να εκτελεστούν παράλληλα με την ελάχιστη δυνατή επικοινωνία μεταξύ των επεξεργαστών. Πολύ σημαντική είναι και η δυνατότητα «κλιμάκωσης» (scalability) του αλγορίθμου με την προσθήκη νέων επεξεργαστών, δηλαδή η δυνατότητα του αλγορίθμου θα εκτελεστεί σε ακόμα περισσότερους επεξεργαστές χωρίς να μειώνεται η επίδοσή του. 20

Για αυτό τον λόγο, η επίδοση του παράλληλου αλγορίθμου δεν μετριέται μόνο ως η επιτάχυνση συγκριτικά με τον αντίστοιχο σειριακό, αλλά κυρίως ως η επιτάχυνση ανά επεξεργαστή. Προφανώς η ιδανική περίπτωση είναι να έχουμε επιτάχυνση 1/ επεξεργαστή, το οποίο όμως είναι αρκετά δύσκολο. Στην εργασία αυτή χρησιμοποιήσαμε συστοιχία 8 επεξεργαστών σε αρχιτεκτονική κοινής μνήμης (shared address space) ενώ για τον προγραμματισμό χρησιμοποιήσαμε την βιβλιοθήκη των pthreads για C, καθότι οι δυνατότητες ελέγχου των threads και οι conditional variables είναι πολύ δυνατά εργαλεία που διευκόλυναν πολύ τον προγραμματισμό. 21

22

3. Το πρόβλημα & ο αλγόριθμος 3.1 Μερικοί Ορισμοί Γράφος είναι ένα διατεταγμένο ζευγάρι G = (V,Ε) όπου V ένα σύνολο του οποίου τα στοιχεία ονομάζονται κόμβοι ή κορυφές και Ε ένα σύνολο μη διατεταγμένων ζευγών από κορυφές που ονομάζονται ακμές. Κατ αντιστοιχία κατευθυνόμενος γράφος λέγεται ένας γράφος του οποίου οι ακμές είναι κατευθυνόμενες, ισοδύναμα τα ζεύγη είναι διατεταγμένα. Είναι προφανές ότι ο αριθμός των ακμών κυμαίνεται μεταξύ 0 και N 2. Αν οι ακμές είναι κοντά στο N 2 λέμε ότι ο γράφος είναι πυκνός ενώ αν είναι κοντά στο Ν λέμε ότι είναι αραιός. Γράφος με βάρη λέγεται ένας γράφος στον οποίο αντιστοιχούμε κάθε ακμή με ένα πραγματικό αριθμό που ονομάζεται βάρος ή τιμή της ακμής. Αυτό δίνει την δυνατότητα στους γράφους να μοντελοποιήσουν ακόμα πιο σύνθετα προβλήματα. Μέγεθος Ν ενός γράφου ονομάζεται το πλήθος των κόμβων του, δηλαδή ο V. Βαθμός degree(v) ενός κόμβου v λέγεται το πλήθος των ακμών που έχουν ένα τους άκρο τον κόμβο αυτό. Στην περίπτωση κατευθυνόμενου γράφου, διαχωρίζουμε τον βαθμό ενός κόμβου σε indegree & outdegree. Οι γράφοι είναι ένα ισχυρό μαθηματικό εργαλείο για την πληροφορική καθώς μπορούν να μοντελοποιήσουν πληθώρα διαφορετικών προβλημάτων και να τα γενικεύσουν σε απλές αλλά συνάμα αυστηρές μαθηματικές περιγραφές. Πολλοί αλγόριθμοι περιγράφονται με όρους της θεωρίας γραφημάτων και εφαρμόζονται απευθείας σε γράφους. 23

3.2 Παράσταση γράφων Εκτός από οπτική αναπαράσταση μέσω διαγραμμάτων (γραφημάτων), ένας γράφος μπορεί να παρασταθεί σε διάφορες μορφές: πίνακα γειτνίασης, όπου κάθε γραμμή και η αντίστοιχη στήλη αντιπροσωπεύει έναν κόμβο, και η τιμή του πίνακα σε μία θέση (i,j) υποδηλώνει o είτε την ύπαρξη/ ανυπαρξία της ακμής (τιμές 1/0) o είτε το βάρος της ακμής (άπειρο για ακμές που δεν υπάρχουν) από το i στο j Η μορφή αυτή απαιτεί Ν 2 χώρο στην μνήμη, ανεξαρτήτως του αριθμού των ακμών. Αν ο γράφος δεν είναι κατευθυνόμενος τότε ο πίνακας αυτός είναι συμμετρικός οπότε πολλές φορές προτιμούμε την παράστασή του ως τριγωνικό πίνακα, πίνακα πρόσπτωσης, όπου η μία διάσταση έχει τις ακμές και η άλλη τους κόμβους και στον οποίο πίνακα η τιμή σε μία θέση (i,j) έχει απόλυτη τιμή ίση με το βάρος της αντίστοιχης ακμής, το δε πρόσημό της δηλώνει αν η ακμή εισέρχεται ή εξέρχεται από τον κόμβο. Σε περίπτωση που δεν έχουμε βάρη οι τιμές είναι 0,±1 όπως παραπάνω. Η μορφή αυτή απαιτεί N* E χώρο στην μνήμη, το οποίο εν γένει δεν είναι μικρό, λίστα γειτνίασης, όπου σε κάθε κόμβο αντιστοιχείται μία συνδεδεμένη λίστα με όλους τους κόμβους με τους οποίους συνδέεται (περιλαμβάνεται πληροφορία για το βάρος) Η μορφή αυτή απαιτεί N*maxDegree(G), όπου maxdegree ο μέγιστος βαθμός όλων των κόμβων του γράφου. Αν ο γράφος είναι κατευθυνόμενος τότε η λίστα κάθε κόμβου περιλαμβάνει τους κόμβους στους οποίους καταλήγει ακμή από τον κόμβο αυτό. Ανάλογα με την πυκνότητα του γράφου, αλλά και τις ιδιαιτερότητες κάθε προβλήματος αλγορίθμου επιλέγουμε την κατάλληλη παράσταση του γράφου στην μνήμη. Συνηθέστερη επιλογή, ειδικά για πυκνούς γράφους, είναι ο πίνακας γειτνίασης ενώ για αραιότερους γράφους επιλέγουμε λίστα γειτνίασης. Ο πίνακας γειτνίασης παρουσιάζει μεγαλύτερη χωρική τοπικότητα, το οποίο είναι σημαντικό πλεονέκτημα. Ο τρόπος παράστασης των γράφων είναι πολύ σημαντικός καθότι σε όλες τις περιπτώσεις εμπλέκει μεγάλη ποσότητα μνήμης την οποία μας ενδιαφέρει να διαχειριστούμε σωστά σύμφωνα με όσα αναφέρθηκαν σε προηγούμενη ενότητα 24

3.3 Το πρόβλημα Δίνεται ένας κατευθυνόμενος γράφος G με Ν κόμβους, στον οποίο το βάρος κάθε ακμής (i,j) αναπαριστά το κόστος μετάβασης από τον κόμβο i στον κόμβο j. Ο γράφος δίνεται σε μορφή πίνακα γειτνίασης. Ζητείται το ελάχιστο κόστος μετάβασης από κάθε κόμβο σε όλους τους άλλους. Στην βιβλιογραφία το πρόβλημα λέγεται All Pair Shortest Paths, είναι ισοδύναμο με το πρόβλημα του μεταβατικού κλεισίματος μιας σχέσης (transitive closure) και μοντελοποιεί πολλά προβλήματα από διαφορετικούς επιστημονικούς τομείς, όπως: Εύρεση κανονικής έκφρασης [regular expression] που να περιγράφει την γλώσσα που αποδέχεται ένα πεπερασμένο αυτόματο Δρομολόγηση (routing) σε δίκτυα Έλεγχος αν ένας γράφος είναι διμερής [bipartite] Αντιστροφή πραγματικών πινάκων Bioinformatics Query processing VLSI/CAD Ένα άλλο πρόβλημα που μοντελοποιείται είναι η επαλήθευση της εικασίας ότι μας χωρίζουν μόλις 6 άνθρωποι από οποιονδήποτε άλλο πάνω στον πλανήτη (six degrees of separation). Αν θεωρήσουμε τους ανθρώπους ως κόμβους και ότι ακμή ενώνει μόνο όσους γνωρίζονται (ή είναι «φίλοι» όπως στα ηλεκτρονικά δίκτυα κοινωνικής δικτύωσης facebook, myspace), τότε αν κάθε συντομότερο μονοπάτι έχει μήκος το πολύ 6, η εικασία ισχύει. Ο αλγόριθμος που επιλύει το πρόβλημα είναι των Floyd Warshall και στην μορφή που τον γνωρίζουμε διατυπώθηκε το 1959 από τον Bernard Roy. Είναι αλγόριθμος δυναμικού προγραμματισμού καθώς σε κάθε βήμα επαναϋπολογίζει το κόστος ανάμεσα σε κάθε δύο κόμβους χρησιμοποιώντας ως ενδιάμεσο κόμβο διαδοχικά όλους τους υπόλοιπους και χρησιμοποιεί τις τιμές που υπολόγισε στα προηγούμενα βήματα. Η ορθότητα του αλγορίθμου βασίζεται στην παρακάτω αναδρομική σχέση στην οποία ως spath(i,j,k) συμβολίζουμε το κόστος του μικρότερου μονοπατιού από τον κόμβο i στον κόμβο j που διέρχεται μόνο από τους κόμβους 1..k: spath(i,j,k) = min( spath(i,j,k-1). spath(i,k,k-1) + spath(k,j,k-1) ) spath(i,j,0) = (i,j) Υποθέτοντας ότι ο πίνακας g είναι αρχικοποιημένος στις τιμές των ακμών και χρησιμοποιώντας σύνταξη C ο αντίστοιχος κώδικας είναι: for (k=0;k<ν;k++) for (i=0;i<ν;i++) for (j=0;j<ν;j++) g[i][j]=min(g[i][j],g[i][k]+g[k][j]); [Κώδικας 1] 25

Η χρονική πολυπλοκότητα του παραπάνω αλγορίθμου είναι o(n 3 ) ενώ η χωρική πολυπλοκότητα είναι o(n 2 ) αφού σε κάθε βήμα επαναχρησιμοποιούμε την αρχική μνήμη. Ο αλγόριθμος αυτός παρουσιάζει ιδιαίτερο ενδιαφέρον από την σκοπιά που μας ενδιαφέρει σε αυτήν εδώ την εργασία καθότι το υπολογιστικό κόστος είναι συγκρίσιμο με τις αναφορές στην μνήμη. Παρακάτω ποσοτικοποιούμε αυτή την σύγκριση και όπως θα δούμε οι υπολογισμοί είναι αμελητέοι. Συγκεκριμένα, ανάλογα και με την βελτιστοποίηση του compiler, αναμένουμε μία έως εφτά προσθέσεις [περιλαμβάνεται ο υπολογισμός των διευθύνσεων] και μία σύγκριση ανά επανάληψη ενώ έχουμε τρεις αναφορές στην μνήμη, οι οποίες όπως αναφέραμε σε προηγούμενη ενότητα αναμένουμε να κοστίζουν πολύ περισσότερο. Υπάρχει και το υπολογιστικό κόστος των βρόχων το οποίο επίσης είναι σημαντικό. Προς επιβεβαίωση των παραπάνω, δοκιμάζουμε το εξής: τρέχουμε τον παραπάνω αλγόριθμο ως έχει και συγκρίνουμε τον απαιτούμενο χρόνο με τον χρόνο που απαιτείται για αντίστοιχη υπολογιστική εργασία χωρίς αναφορές στην μνήμη. Ο κώδικας με τον οποίο συγκρίνουμε είναι ο εξής: for (k=0;k<ν;k++) for (i=0;i<ν;i++) for (j=0;j<ν;j++) { t1 = a+i+j; t2 = a+i+k; t3 = a+j+k; t1 = min(t1,t2+t3); } [Κώδικας 2] Οι μεταβλητές t1, t2, t3 αντιπροσωπεύουν τους υπολογισμούς των διευθύνσεων των στοιχείων που φέρνουμε από την μνήμη. Αν δεν χρησιμοποιήσουμε βελτιστοποίηση κατά την μεταγλώττιση των δύο προγραμμάτων, τότε ο αυθεντικός Floyd Warshall αλγόριθμος απαιτεί περίπου 20% περισσότερο χρόνο. Αν όμως εφαρμόσουμε το 3 ο επίπεδο βελτιστοποίησης που διαθέτει ο gcc [το οποίο χρησιμοποιούμε από δω και στο εξής σε όλες τις μετρήσεις], τότε ο δεύτερος κώδικας τρέχει μερικές χιλιάδες φορές πιο γρήγορα... Επειδή όμως η βελτιστοποίηση του compiler ίσως αντιλαμβάνεται ότι η εξωτερική loop δεν έχει νόημα με αποτέλεσμα να την εναλλάξει και να αποφύγει κάποιες επαναλήψεις, δοκιμάζουμε και τους εξής κώδικες, παρότι έχουμε μερικές πράξεις παραπάνω: for (k=0;k<size;k++) for (i=0;i<size;i++) for (j=0;j<size;j++) { t1 = a+i+j; t2 = a+i+k; t3 = a+j+k; t[(i+j+k)%4] = min(t1,t2+t3); } [Κώδικας 3] 26

Για να ελέγξουμε κατά πόσο το κόστος της μνήμης είναι οι αναγνώσεις ή οι εγγραφές δοκιμάζουμε και τα παρακάτω: int t[4]; for (k=0;k<size;k++) for (i=0;i<size;i++) for (j=0;j<size;j++) t[(k+i+j)%4]=min(g[i][j],g[i][k]+g[k][j]); [Κώδικας 4] for (k=0;k<size;k++) for (i=0;i<size;i++) for (j=0;j<size;j++) g[i][j]=min(g[i][j],g[i][k]+g+k+j); [Κώδικας 5] Η σύγκριση ανάμεσα στην επίδοση των παραπάνω κωδίκων καταδεικνύει την σημασία της σωστής διαχείρισης όλων των επιπέδων της cache ώστε να ελαχιστοποιήσουμε το I/O με την μνήμη. Η περίπτωση του αλγορίθμου Floyd Warshall ανήκει στην κατηγορία των αλγορίθμων που περιορίζονται από την μνήμη (memory bound) και όχι από τον υπολογιστικό φόρτο. Μάλιστα στους επεξεργαστές που χρησιμοποιούμε σε αυτήν την εργασία, οι απαιτήσεις από την μνήμη εξαντλούν το διαθέσιμο bandwidth της μνήμης, το οποίο θα μας επηρεάσει σημαντικά στην παραλληλοποίηση του αλγορίθμου, όταν πολλοί επεξεργαστές θα προσπελαύνουν την κοινή μνήμη. 27

28

4. Εξαρτήσεις Δεδομένων Σε κάθε k-επανάληψη ο αλγόριθμος διατρέχει όλο τον πίνακα και ενημερώνει τις τιμές του σύμφωνα με τον παραπάνω τύπο. Άρα μπορούμε να πούμε ότι ο αλγόριθμος υπολογίζει έναν κύβο μεγέθους NxNxN όπου οι τρεις διαστάσεις αντιστοιχούν στις k,i,j μεταβλητές των επαναληπτικών βρόχων. Η μεταβλητή k αντιστοιχεί στο ύψος του κύβου και αναπαριστά στην ουσία τον χρόνο, οπότε κάθε επίπεδο αντιστοιχεί στον πίνακά μας όπου αρχικά έχουμε τις τιμές των ακμών. Θεωρούμε ότι ο γράφος μας είναι αρκετά μεγάλος ώστε ο πίνακας γειτνίασης να μην χωράει στην διαθέσιμη cache του συστήματος στο οποίο υλοποιούμε τον αλγόριθμο. Άρα κατά την διάρκεια των υπολογισμών θα χρειαστεί να φέρνουμε από την μνήμη τα περισσότερα δεδομένα, κάτι το οποίο όπως είδαμε παραπάνω συνιστά τον κυριότερο παράγοντα που καθυστερεί την εκτέλεση. Στόχος μας είναι να διαχειριστούμε όσο γίνεται καλύτερα την διαθέσιμη cache ώστε να ελαχιστοποιήσουμε τις προσβάσεις στην κύρια μνήμη. Για να το πετύχουμε αυτό πρέπει να διερευνήσουμε αφενός την σειρά πρόσβασης στα δεδομένα της μνήμης και αφετέρου τις εξαρτήσεις ανάμεσα σε διαδοχικές επαναλήψεις. Η μελέτη και κατανόηση αυτών των εξαρτήσεων θα μας δώσει την δυνατότητα να αναδιατάξουμε την σειρά πρόσβασης στην μνήμη εκμεταλλευόμενοι τα στοιχεία που υπάρχουν ήδη στην cache χωρίς να παραβιάσουμε την σωστή σειρά εκτέλεσης των απαιτούμενων υπολογισμών. Ξεκινάμε μελετώντας το (ij) επίπεδο σε δεδομένο ύψος k. Όπως φαίνεται και στο παρακάτω διάγραμμα, όπου κάθε κύκλος αναπαριστά ένα στοιχείο του πίνακα, ο υπολογισμός κάθε στοιχείου απαιτεί, εκτός από την προηγούμενη τιμή του στοιχείου, τα στοιχεία που βρίσκονται στην k γραμμή και στήλη, στην αντίστοιχη στήλη και γραμμή με το στοιχείο που υπολογίζεται. i j Διάγραμμα 1 29

Άρα κατά την k επανάληψη πρέπει η k γραμμή και η k στήλη να βρίσκονται σε συνεπή κατάσταση, δηλαδή να έχουν γίνει όλοι οι υπολογισμοί που προβλέπονται από τον αρχικό αλγόριθμο. Είναι προφανές ότι τα στοιχεία αυτά δεν αλλάζουν τιμή κατά την k επανάληψη διότι είτε i=k είτε j=k και είναι προφανές ότι g[i][i]=g[j][j]=0. Στόχος όπως αναφέρθηκε και παραπάνω είναι η αναδιάταξη των υπολογισμών. Η αναδιάταξη αυτή δεν θα διασπάσει τον υπολογισμό ολόκληρων γραμμών καθότι το hardware prefetching που αναφέραμε παραπάνω είναι ιδιαίτερα αποδοτικό. Η αναδιάταξη λοιπόν θα γίνει σε επίπεδο ολόκληρων γραμμών. Αυτό που μας ενδιαφέρει λοιπόν είναι τα δεδομένα που χρειάζεται κάθε γραμμή σε κάθε επανάληψη. Σύμφωνα με τα παραπάνω λοιπόν: Ο υπολογισμός μιας συγκεκριμένης γραμμής i σε μία συγκεκριμένη επανάληψη k απαιτεί Τις προηγούμενες τιμές της γραμμής αυτής, δηλαδή τις τιμές στο τέλος της (k-1) επανάληψης στις οποίες περιλαμβάνεται και το στοιχείο στην k στήλη Τις προηγούμενες τιμές της γραμμής k, δηλαδή τις τιμές της γραμμής k στο τέλος της (k-1) επανάληψης. Τα στοιχεία αυτά από δω και πέρα θα αναφέρονται ως στοιχεία αναφοράς, καθώς σε αυτά αναφερόμαστε για την ενημέρωση των τιμών. Στο διάγραμμα που ακολουθεί κάθε κύκλος αναπαριστά τον υπολογισμό κάθε γραμμής i στην αντίστοιχη επανάληψη k. Τα βέλη αναπαριστούν τις εξαρτήσεις σύμφωνα με τους παραπάνω κανόνες. Από το διάγραμμα απουσιάζουν οι γραμμές για τις οποίες i=k, καθότι όπως αναφέρθηκε δεν αλλάζουν τιμή. j i Διάγραμμα 2 30

Τα γραμμοσκιασμένα στοιχεία στην (δευτερεύουσα) διαγώνιο είναι τα στοιχεία αναφοράς και όπως φαίνεται στο διάγραμμα είναι τα πλέον σημαντικά κατά τους υπολογισμούς, καθώς από αυτά εξαρτώνται όλα τα υπόλοιπα. Όλες οι μέθοδοι που θα αναπτυχθούν σε επόμενα κεφάλαια ασχολούνται πρωτίστως με την κατάλληλη «διάσχιση» του παραπάνω διαγράμματος, ώστε να μειωθούν οι αστοχίες της μνήμης. Αυτό που είναι ήδη εμφανές είναι ότι κινούμενοι κάθετα στο διάγραμμα επεξεργαζόμαστε την ίδια γραμμή με διαδοχικές αναγνώσεις και εγγραφές ενώ κινούμενοι οριζόντια χρησιμοποιούμε το ίδιο στοιχείο αναφοράς με διαδοχικές αναγνώσεις. 31

32

5. Υλοποίηση Σειριακού Αλγορίθμου Στις επόμενες ενότητες παραθέτουμε τις διαφορετικές προσεγγίσεις στην υλοποίηση του αλγορίθμου. Για κάθε προσέγγιση παρουσιάζεται ο σχετικός κώδικας σε γλώσσα C, καθώς και τα πλεονεκτήματα μειονεκτήματα που παρουσιάζει. Εκτιμούμε το κόστος στο memory traffic υπολογίζοντας πόσες φορές χρειάζεται να φέρουμε κάθε γραμμή (είτε αυτές που υπολογίζονται είτε τις γραμμές αναφοράς). Για τον κώδικα έχει γίνει η παραδοχή ότι η μεταβλητή a δείχνει σε ένα δισδιάστατο πίνακα του οποίου το μέγεθος είναι size x size, όπου size το μέγεθος του γράφου (αριθμός κόμβων) ενώ υπάρχουν οι κατάλληλες δηλώσεις και αρχικοποιήσεις για τις τοπικές μεταβλητές που χρησιμοποιούνται. Το πλήρες πρόγραμμα που χρησιμοποιούμε, περιλαμβάνει ρουτίνες αρχικοποίησης & ελέγχου του αποτελέσματος καθώς και τις κατάλληλες εντολές για την μέτρηση του απαιτούμενου χρόνου. Η ονοματολογία των μεθόδων, όταν δεν υπάρχει κάποιο ιδιαίτερο όνομα, προκύπτει από την σειρά με την οποία εκτελούνται οι 3 loops του αλγορίθμου. Στον αυθεντικό αλγόριθμο η σειρά είναι k-i-j. Στις μεθόδους που χρησιμοποιούμε τεχνικές blocking, αυτό φαίνεται στην ονομασία με την παρεμβολή ενός b πριν την loop στην οποία εφαρμόζεται το blocking. Στην πρώτη ενότητα παρουσιάζουμε τον αρχικό αλγόριθμο (kij) αναδεικνύοντας τα προβλήματά που παρουσιάζει στην υλοποίηση. Στην δεύτερη ενότητα δοκιμάζουμε μία αλλαγή αναστρέφοντας την φορά σάρωσης του πίνακα. Στην τρίτη ενότητα αλλάζουμε την σειρά των πρώτων loops (ikj). Οι επόμενες τέσσερις ενότητες περιέχουν τις μεθόδους με τις καλύτερες επιδόσεις καθώς σε όλες εφαρμόζουμε blocking. Συγκεκριμένα στην τέταρτη ενότητα παρουσιάζεται η ibkj μέθοδος, στην πέμπτη ενότητα η kbij μέθοδος, στην έκτη η μέθοδος tiled ενώ στην έβδομη ενότητα η bkbij μέθοδος. 33

34

5.1 Απλός αλγόριθμος Floyd Warshall KIJ Ο αλγόριθμος όπως παρουσιάστηκε αρχικά υπολογίζει διαδοχικά όλες τις γραμμές ενός επιπέδου και μετά συνεχίζει σε επόμενο ύψος. Άρα διατρέχει το διάγραμμα εξαρτήσεων πρώτα από αριστερά προς τα δεξιά και μετά από κάτω προς τα πάνω. Σε κάθε επανάληψη διατηρεί σταθερή την γραμμή αναφοράς ενώ υπολογίζει και τα άχρηστα στοιχεία της διαγωνίου. Κώδικας for (k=0;k<size;k++) for (i=0;i<size;i++) for (j=0;j<size;j++) a[i][j]=min(a[i][j],a[i][k]+a[k][j]); Διάγραμμα πρόσβασης στα δεδομένα k 9 10 11 12... 1 2 3 4 5 6 7 8 i Διάγραμμα 3 Πλεονεκτήματα: Κρατώντας σταθερή την γραμμή αναφοράς επιτυγχάνει την διατήρησή της στην cache για όλες τις γραμμές [i=1..size] χωρίς να έχει cache miss, παρά μόνο στην αρχή κάθε k- επανάληψης οπότε και αλλάζει η γραμμή αναφοράς. 35

Μειονεκτήματα: Δεν επαναχρησιμοποιούνται καθόλου οι γραμμές που υπολογίζονται, αλλά σε κάθε βήμα χρειάζεται να τις ξαναφέρουμε από την κύρια μνήμη. Επίσης υπολογίζει τα διαγώνια στοιχεία το οποίο όμως δεν επηρεάζει την ασυμπτωτική συμπεριφορά του αλγορίθμου. Εκτιμώμενο Memory Traffic Size γραμμές από τα στοιχεία αναφοράς + Size 2 γραμμές από τα στοιχεία που υπολογίζονται Σύνολο: Size 3 + Size 2 στοιχεία 36

5.2 Αναστροφή Η παραλλαγή αυτή συνιστά μία πολύ μικρή βελτίωση στην αρχική εκδοχή. Προσπαθούμε να εκμεταλλευτούμε τις τελευταίες γραμμές που φέραμε στην cache κατά την διάρκεια της k-επανάληψης, ξεκινώντας την (k+1)-επανάληψη από αυτές. Οπότε εναλλάξ αλλάζουμε φορά σάρωσης έως ότου τελειώσουμε. Από το μέγεθος c της cache και το μέγεθος του προβλήματος εξαρτάται ο βαθμός επαναχρησιμοποίησης των γραμμών [έστω N = N(size,c) ο αριθμός των γραμμών που επαναχρησιμοποιούνται από επανάληψη σε επανάληψη]. Κώδικας for (k=0;k<size;k+=2){ for (i=0;i<size;i++) for (j=0;j<size;j++) a[i][j]=min(a[i][j],a[i][k]+a[k][j]); if (k==size-1) break; for (i=size-1;i>=0;i--) for (j=size-1;j>=0;j--) a[i][j]=min(a[i][j],a[i][k+1]+a[k+1][j]); } Διάγραμμα πρόσβασης στα δεδομένα k 12 11 10 9 1 2 3 4 5 6 7 8 i Διάγραμμα 4 37

Πλεονεκτήματα Οι τελευταίες Ν γραμμές επαναχρησιμοποιούνται οπότε σε μικρά προβλήματα με μεγάλη cache η βελτίωση είναι σημαντική Μειονεκτήματα Για μεγάλα προβλήματα μειώνεται το Ν καθότι σε δεδομένο μέγεθος cache χωράνε λιγότερες γραμμές μεγαλύτερου μεγέθους. Οπότε η όποια βελτίωση ασυμπτωτικά είναι αμελητέα. Ισχύουν φυσικά και τα μειονεκτήματα του αρχικού αλγορίθμου σχετικά με την ανάγνωση των γραμμών Εκτιμώμενο Memory Traffic Size γραμμές από τα στοιχεία αναφοράς + Size(Size N) γραμμές από τα στοιχεία που υπολογίζονται Σύνολο: Size 2 (Size N) + Size 2 στοιχεία 38

5.3 IKJ Το κύριο πρόβλημα του αρχικού αλγορίθμου ήταν η μη επαναχρησιμοποίηση των γραμμών που υπολογίζονται. Έτσι η μέθοδος IKJ αντιστρέφει την σειρά σάρωσης του πίνακα, διατρέχοντας πρώτα την i-διάσταση και μετά την k-διάσταση με στόχο την εκτέλεση όσο περισσότερων υπολογισμών γίνεται επί των ίδιων γραμμών. Για να τηρηθούν οι εξαρτήσεις των δεδομένων υπολογίζουμε πρώτα το κάτω τριγωνικό τμήμα του διαγράμματος και μετά το πάνω, έτσι ώστε τα διαγώνια στοιχεία να έχουν υπολογιστεί προτού χρειαστούν. Η σειρά των υπολογισμών φαίνεται πιο καθαρά και στο διάγραμμα που παρατίθεται. Κώδικας for (i=1;i<size;i++) for (k=0;k<i;k++) for (j=0;j<size;j++) a[i][j]=min(a[i][j],a[i][k]+a[k][j]); for (i=0;i<size;i++) for (k=i+1;k<size;k++) for (j=0;j<size;j++) a[i][j]=min(a[i][j],a[i][k]+a[k][j]); Διάγραμμα πρόσβασης στα δεδομένα 31 37 10 30 36 6 9 k 29 3 5 8 1 2 4 7 11 i Διάγραμμα 5 39

Πλεονεκτήματα Κάθε γραμμή διαβάζεται μόνο δύο φορές [μία για κάθε τριγωνικό τμήμα] από την μνήμη, καθότι σε όλες τις επόμενες επαναλήψεις βρίσκεται ήδη στην cache. Δεδομένων των εξαρτήσεων ανάμεσα στις επαναλήψεις η ανάγνωση των γραμμών είναι η ελάχιστη δυνατή. Μειονεκτήματα Τα στοιχεία αναφοράς δεν επαναχρησιμοποιούνται αφού σε κάθε επανάληψη αναφερόμαστε στην επόμενη γραμμή. Με εξαίρεση ίσως τις αμελητέες πρώτες επαναλήψεις του κάτω τμήματος και τις τελευταίες του πάνω, σε όλες τις άλλες η γραμμή αναφοράς δεν βρίσκεται στην cache. Το πρόβλημα αυτό δεν είναι σημαντικό για προβλήματα μικρού μεγέθους, αλλά αντιστοιχεί στο πρόβλημα του αρχικού αλγορίθμου σχετικά με την ανάγνωση των γραμμών Εκτιμώμενο Memory Traffic Size 2 γραμμές από τα στοιχεία αναφοράς + 2*Size γραμμές από τα στοιχεία που υπολογίζονται Σύνολο: Size 3 + 2*Size 2 στοιχεία Αναμένουμε καλύτερη συμπεριφορά για μικρού μεγέθους χωρία. 40

Όπως είδαμε στις τρεις πρώτες μεθόδους, το πρόβλημα του αλγορίθμου είναι τόσο η ανάγνωση των διαγώνιων στοιχείων αναφοράς όσο και η ανάγνωση των γραμμών που υπολογίζονται. Δίνοντας έμφαση στον ένα εκ των δύο παραγόντων, και βελτιστοποιώντας το memory traffic το οποίο εισάγει, καταλήγουμε στην χείριστη δυνατή επίδοση στον δεύτερο παράγοντα. Είναι προφανές ότι η βέλτιστη επίδοση θα προκύψει από τον συνδυασμό και των δύο παραγόντων. Από τα πειραματικά αποτελέσματα που δίνουν υπεροχή στην IKJ μέθοδο, αναμένουμε η βέλτιστη επίδοση να δίνει περισσότερη έμφαση στην ανάγνωση των γραμμών. Στις αμέσως επόμενες ενότητες δοκιμάζουμε 4 μεθόδους που προσπαθούν να συμβιβάσουν και τις δύο παραπάνω παραμέτρους εφαρμόζοντας blocking σε μία (IBKJ, KBIJ), δύο (BKBIJ) ή τρεις (Tiled) διαστάσεις. 41

42

5.4 IBKJ Η μέθοδος αυτή ξεκινάει από την IKJ που έχει βέλτιστη διαχείριση της μνήμης για τις γραμμές και προσπαθεί να βελτιώσει την ανάγνωση των διαγώνιων στοιχείων. Η βελτίωση αυτή βέβαια επηρεάζει αρνητικά τις γραμμές. Η μέθοδος IKJ λοιπόν βελτιώνει την προηγούμενη κάνοντας blocking στην k-διάσταση ώστε να διατηρεί τα διαγώνια στοιχεία αναφοράς στην cache για όλες τις γραμμές. Κατάλληλη επιλογή του blocking factor B ισοσταθμίζει το επιπλέον κόστος από τις γραμμές που δεν διαβάζονται πλέον μόνο δύο φορές. Κώδικας for (k=0;k<size;k+=b1) for (i=k+1;i<size;i++) for (kk=k;kk<k+b1&&kk<i;kk++) for (j=0;j<size;j++) a[i][j]=min(a[i][j],a[i][kk]+a[kk][j]); for (k=1;k<size;k+=b1) for (i=0;i<k+b1-1;i++) for (kk=max(k,i+1);kk<k+b1&&kk<size;kk++) for (j=0;j<size;j++) a[i][j]=min(a[i][j],a[i][kk]+a[kk][j]); Διάγραμμα πρόσβασης στα δεδομένα k 3 5 7 9 11 13 1 2 4 6 8 10 12 i Διάγραμμα 6 43

5 7 9 10 4 6 8 k 2 3 1 i Διάγραμμα 7 Πλεονεκτήματα Τα διαγώνια στοιχεία διαβάζονται μόνο δύο φορές, ενώ οι γραμμές διατηρούνται στην cache για Β επαναλήψεις. Η βελτίωση είναι σημαντική! Μειονεκτήματα Το Β πρέπει να επιλεγεί έτσι ώστε να διατηρούνται τα διαγώνια στοιχεία στην cache για όλες τις επόμενες γραμμές, άρα όσο μεγαλώνει το μέγεθος του προβλήματος τόσο θα μειώνεται, οπότε αυξάνεται το κόστος ανάγνωσης των γραμμών. Στην πράξη το κατάλληλο Β δεν ήταν πολύ μεγάλο Εκτιμώμενο Memory Traffic Με κατάλληλη επιλογή του Β: 2*Size γραμμές από τα στοιχεία αναφοράς + Size2 B γραμμές από τα στοιχεία που υπολογίζονται Σύνολο: Size3 B + 2*Size2 στοιχεία 44

5.5 KBIJ Η μέθοδος αυτή ξεκινάει από την KIJ που έχει βέλτιστη διαχείριση της μνήμης για τα διαγώνια στοιχεία αναφοράς και προσπαθεί να βελτιώσει την ανάγνωση των γραμμών. Και σε αυτή την περίπτωση θα επηρεαστεί αρνητικά η ανάγνωση των διαγώνιων στοιχείων. Η μέθοδος KBIJ λοιπόν εφαρμόζει blocking στον αρχικό αλγόριθμο στην i-διάσταση για να πετύχει memory traffic αντίστοιχο της προηγούμενης μεθόδου. Η κατάλληλη επιλογή του Β παίζει πάλι σημαντικό ρόλο. Για να διατηρήσουμε τις εξαρτήσεις χωρίζουμε πάλι το πρόβλημα στα δύο τριγωνικά μέρη όπως στις προηγούμενες μεθόδους. Κώδικας for (i=1;i<size;i+=b1) for (k=0;k<i+b1-1;k++) for (ii=max(i,k+1);ii<i+b1&&ii<size;ii++) for (j=0;j<size;j++) a[ii][j]=min(a[ii][j],a[ii][k]+a[k][j]); for (i=0;i<size;i+=b1) for (k=i+1;k<size;k++) for (ii=i;ii<k && ii<i+b1;ii++) for (j=0;j<size;j++) a[ii][j]=min(a[ii][j],a[ii][k]+a[k][j]); Διάγραμμα πρόσβασης στα δεδομένα 6 k 4 5 10 11 12 1 2 3 7 8 9 i Διάγραμμα 8 45

4 5 6 k 2 3 1 Πλεονεκτήματα Με κατάλληλη επιλογή του Β φέρνουμε κάθε γραμμή μόνο δύο φορές από την μνήμη, ενώ διατηρούμε τα διαγώνια στοιχεία για Β επαναλήψεις στην cache. Μειονεκτήματα Το Β πρέπει να επιλεγεί έτσι ώστε να διατηρούνται οι γραμμές που υπολογίζονται στην cache για κάθε διαγώνιο στοιχείο, άρα όσο μεγαλώνει το μέγεθος του προβλήματος τόσο θα μειώνεται, οπότε αυξάνεται το κόστος ανάγνωσης των διαγώνιων στοιχείων που διαβάζονται μία φορά για κάθε ομάδα Β γραμμών Εκτιμώμενο Memory Traffic Με κατάλληλη επιλογή του Β: Size 2 B γραμμές από τα στοιχεία αναφοράς + 2*Size γραμμές από τα στοιχεία που υπολογίζονται Σύνολο: Size3 B + 2*Size2 στοιχεία i Διάγραμμα 9 Στην πράξη το κατάλληλο Β για αυτήν την μέθοδο είναι αρκετά μεγαλύτερο από την κατάλληλη τιμή της προηγούμενης μεθόδου. Η μέθοδος αυτή μαζί με την μέθοδο BKBIJ που παρουσιάζεται στην ενότητα 5.7 συνιστούν τις πιο γρήγορες. 46

5.6 Tiled Η μέθοδος αυτή ισοδυναμεί με εφαρμογή blocking και στις τρεις διαστάσεις, υπό την επιπλέον συνθήκη το μέγεθος του block να είναι το ίδιο σε όλες τις διαστάσεις του προβλήματος. Η μέθοδος αναπτύχθηκε πριν την εισαγωγή του hardware prefetching και σε συνδυασμό με αλλαγή του τρόπου αναπαράστασης στην μνήμη πέτυχε πολύ μεγάλες επιταχύνσεις στον χρόνο εκτέλεσης. Για λόγους παρουσίασης παραθέτουμε το διάγραμμα πρόσβασης στα δεδομένα για το (ij)- επίπεδο. Χωρίζουμε το επίπεδο σε tiles x tiles τμήματα μεγέθους sizet και σε κάθε επανάληψη ξεκινάμε από το επόμενο τμήμα επί της διαγωνίου του κύβου. Οι επαναλήψεις που γίνονται αντιστοιχούν στην χρήση αυτών των γραμμών ως στοιχεία αναφοράς. Πρώτα ενημερώνεται το διαγώνιο τμήμα, μετά τα υπόλοιπα τμήματα που βρίσκονται είτε στις ίδιες γραμμές είτε στις ίδιες στήλες και τέλος τα υπόλοιπα τμήματα. Για τον υπολογισμό κάθε τμήματος μπορούμε να χρησιμοποιήσουμε οποιαδήποτε από τις παραπάνω μεθόδους κατάλληλα προσαρμοσμένη ώστε να ξεκινάει από τα σωστά k 0, i 0, j 0. Κώδικας for (t=0;t<tiles;t++){ FW_Simple_Offset(t*sizeT,t*sizeT,t*sizeT,sizeT); i=t; for (j=0;j<tiles;j++) if (j!=t) FW_Simple_Offset(i*sizeT,j*sizeT,t*sizeT,sizeT); j=t; for (i=0;i<tiles;i++) if (i!=t) FW_Simple_Offset(i*sizeT,j*sizeT,t*sizeT,sizeT); for (i=0;i<tiles;i++) for (j=0;j<tiles;j++) if ((i!=t)&&(j!=t)) FW_Simple_Offset(i*sizeT,j*sizeT,t*sizeT,sizeT); } 47

Διάγραμμα πρόσβασης στα δεδομένα (i-j επίπεδο) Πλεονεκτήματα Διάγραμμα 10 Με κατάλληλη επιλογή του αριθμού των τμημάτων (άρα και του μεγέθους τους) μπορούμε να επιτύχουμε να χωράει το κάθε τμήμα στην cache. Για την ακρίβεια χρειαζόμαστε να χωράνε δύο τμήματα στην cache ώστε να διατηρούνται και οι γραμμές αναφοράς για κάθε επανάληψη στην t διάσταση (ως t διάσταση θεωρούμε τα χωρία) Μειονεκτήματα Η διάσπαση της j-διάστασης δεν εκμεταλλεύεται το hardware prefetching και η απαίτηση για ίδιο μέγεθος block σε όλες τις διαστάσεις δυσχεραίνει την προσαρμογή του τμήματος στην cache καθώς όπως είδαμε στις δύο προηγούμενες μεθόδους, η κατάλληλη επιλογή του blocking factor διαφέρει σε κάθε διάσταση Παραλλαγή αυτής της μεθόδου είναι η Simple Recursive που χωρίζει το χωρίο σε 2x2x2 τμήματα, τα υπολογίζει με την σωστή σειρά όπως και η Tiled μέθοδος, αλλά για κάθε χωρίο ο υπολογισμός γίνεται αναδρομικά με τον ίδιο τρόπο έως ότου το τμήμα είναι αρκετά μικρό για να χωρέσει στην cache. 48

5.7 BKBIJ Στις προηγούμενες μεθόδους είδαμε ότι το blocking σε μία διάσταση δεν αρκεί ενώ είναι σπατάλη να κάνουμε blocking και στις τρεις διαστάσεις. Η τελευταία αυτή μέθοδος εφαρμόζει blocking στις δύο πρώτες διαστάσεις ώστε να συνδυάσει όλα τα πλεονεκτήματα των προηγούμενων μεθόδων. Με κατάλληλη επιλογή των παραμέτρων του blocking στις δύο διαστάσεις προσαρμόζεται τέλεια στην cache ελαχιστοποιώντας τις αστοχίες της μνήμης. Η μέθοδος συνίσταται στην κατάτμηση του προβλήματος σε τμήματα Β 1 xb 2 και σε κάθε τμήμα την εκτέλεση όλων των δυνατών επαναλήψεων. Για την διατήρηση των εξαρτήσεων χωρίζουμε το χωρίο σε δύο τριγωνικά όπως και στις προηγούμενες μεθόδους. Κώδικας for (k=0;k<size;k+=b1) for (i=k;i<size;i+=b2) for (kk=k;kk<k+b1&&kk<size;kk++) for (ii=max(kk,i);ii<i+b2&&ii<size;ii++) for (j=0;j<size;j++) a[ii][j]=min(a[ii][j],a[ii][kk]+a[kk][j]) for (k=0;k<size;k+=b1) for (i=0;i<k+b1;i+=b2) for (kk=k;kk<k+b1 && kk<size;kk++) for (ii=i;ii<i+b2 && ii<kk;ii++) for (j=0;j<size;j++) a[ii][j]=min(a[ii][j],a[ii][kk]+a[kk][j]); Διάγραμμα πρόσβασης στα δεδομένα k 29 4 5 9 10 11 i 1 2 3 6 7 8 Διάγραμμα 11 49

Πλεονεκτήματα Με κατάλληλη επιλογή των Β 1, Β 2 πετυχαίνουμε τον βέλτιστο συνδυασμό ο οποίος εξασφαλίζει μεγάλο αριθμό υπολογισμών για κάθε γραμμή και τον μέγιστο αριθμό γραμμών για τις οποίες διατηρούνται στην cache τα διαγώνια στοιχεία. Η παράμετρος B 1 αυξάνει τον βαθμό επαναχρησιμοποίησης των γραμμών χωρίς να χάνουμε παράλληλα τα διαγώνια στοιχεία ούτε μια φορά! Η παράμετρος Β 2 από την άλλη περιορίζει τις επαναλήψεις στην i-διάσταση ώστε να μην χάνουμε τις γραμμές από την cache. Αναμένουμε την καλύτερη επίδοση για σχετικά μεγάλη τιμή του Β 1, από το οποίο προκύπτει και η μεγάλη ομοιότητα με την μέθοδο KBIJ της ενότητας 5.5. Μειονεκτήματα Όσο μεγαλώνει το πρόβλημα τόσο μικρότερες είναι οι κατάλληλες τιμές για τις παραμέτρους. Εκτιμώμενο Memory Traffic 2*Size γραμμές από τα στοιχεία αναφοράς + Size2 B 1 γραμμές από τα στοιχεία που υπολογίζονται Σύνολο: Size3 B 1 + 2*Size 2 στοιχεία. 50

6. Υλοποίηση παράλληλου αλγόριθμου Το προγραμματιστικό μοντέλο που χρησιμοποιήσαμε για την υλοποίηση των παράλληλων αλγορίθμων είναι τα pthreads σε γλώσσα C. Υποθέτουμε ότι έχουμε Ν επεξεργαστές που βλέπουν κοινή μνήμη RAM ενώ στην μεταβλητή id έχουμε τον αριθμό του κάθε επεξεργαστή με βάση τον οποίο καθορίζουμε ποιο μέρος του προβλήματος υπολογίζει. Οι συναρτήσεις Wait(), SignalAll(), Last(id) κλπ υλοποιούνται με χρήση mutexes & conditional variables καθώς και την σχετική βιβλιοθήκη των pthreads. Συγκεκριμένα η Last(id) ελέγχει αν ο επεξεργαστής id είναι ο τελευταίος που τελειώνει το συγκεκριμένο τμήμα, η Wait τον θέτει σε κατάσταση αναμονής αν δεν είναι ο τελευταίος ενώ η SignalAll ειδοποιεί όλους τους επεξεργαστές να συνεχίσουν. Στην πρώτη ενότητα παρουσιάζουμε την παραλληλοποίηση του αρχικού αλγορίθμου. Στην δεύτερη ενότητα τον παράλληλο IKJ που παρότι αργός είναι αρκετά σταθερός. Στην τρίτη ενότητα παρουσιάζουμε μία παραλλαγή του BKBIJ που είναι και η πλέον αποδοτική ενώ στην τέταρτη ενότητα μετατρέπουμε τον Tiled σε έναν πολύ γρήγορο παράλληλο αλγόριθμο. To σύστημα στο οποίο εκτελέσαμε τα προγράμματά μας αποτελούνταν από 8 επεξεργαστές σε κοινή μνήμη, όπου ανά δύο είχαν κοινή Level-2 Cache. Η λεπτομέρεια αυτή θα επηρεάσει (θετικά) τις επιδόσεις των αλγορίθμων μας, όπως θα δούμε και παρακάτω. 51

52

6.1 Απλός αλγόριθμος Floyd Warshall Βλέποντας το διάγραμμα εξαρτήσεων και πώς το διασχίζει ο αρχικός αλγόριθμος, παρατηρούμε ότι μόνο κάθε επανάληψη στην k διάσταση μπορεί να παραλληλοποιηθεί. Μοιράζουμε λοιπόν τις γραμμές σε Ν ομάδες και κάθε επεξεργαστής αναλαμβάνει τον υπολογισμό μίας ομάδας. Οι υπολογισμοί αυτοί είναι ανεξάρτητοι μεταξύ τους. Πριν την επόμενη επανάληψη όμως οι επεξεργαστές επικοινωνούν ώστε να συντονιστούν και να αρχίσουν όλοι μαζί ώστε να διασφαλιστεί ότι τα απαραίτητα δεδομένα της διαγωνίου είναι σε συνεπή κατάσταση. Έτσι επιτυγχάνουμε την διατήρηση των εξαρτήσεων από επανάληψη σε επανάληψη. Κώδικας for (k=0;k<size;k++){ for (i=size/n*id;i<size/n*(id+1);i++) for (j=0;j<size;j++) a[i][j] = min(a[i][j],a[i][k]+a[k][j]); if(!last(id)) Wait(); else SignalAll(); } Διάγραμμα πρόσβασης στα δεδομένα Διάγραμμα 12 53

Όπως και ο σειριακός αλγόριθμος, έτσι και ο παράλληλος έχει μεγάλο traffic με την μνήμη. Η μνήμη όμως έχει περιορισμένο bandwidth το οποίο εξαντλούσε ο επεξεργαστής στον σειριακό αλγόριθμο. Τώρα απαιτείται πολλαπλάσιο bandwidth ώστε να έχουμε την αντίστοιχη μείωση στον χρόνο εκτέλεσης. Το bandwidth αυτό δεν είναι διαθέσιμο, οπότε αναμένουμε μεγάλη καθυστέρηση ακόμα και με δύο μόνο επεξεργαστές. Όσο αυξάνονται οι επεξεργαστές τόσο γίνεται χειρότερο το πρόβλημα με την μνήμη και η επιτάχυνση που έχουμε αυξάνεται πολύ αργά. Μειονέκτημα επίσης είναι η ανάγκη συντονισμού μετά από κάθε επανάληψη Όπως θα δούμε και στις μετρήσεις σε επόμενη ενότητα, η μικρή διαφορά του απλού αλγόριθμου από τους υπόλοιπους σειριακούς, πολλαπλασιάζεται στην παράλληλη επεξεργασία του προβλήματος. 54

6.2 IKJ Στην μέθοδο αυτή κάθε επεξεργαστής αναλαμβάνει διαδοχικά κάποια γραμμή και εκτελεί όλες τις επαναλήψεις που είναι διαθέσιμες (μέχρι ή από την διαγώνιο). Για την διατήρηση των εξαρτήσεων όσο είμαστε στο κάτω τριγωνικό τμήμα του προβλήματος, πριν από κάθε επανάληψη ελέγχει αν τα δεδομένα που χρειάζεται (διαγώνιο στοιχείο) είναι σε συνεπή κατάσταση, δηλαδή αν έχουν ενημερωθεί. Αυτό εισάγει μία καθυστέρηση στις πρώτες Ν επαναλήψεις οπότε και οι επεξεργαστές προχωράνε μαζί και πρέπει να περιμένουν. Μετά τις Ν πρώτες επαναλήψεις κάθε επεξεργαστής βρίσκεται σε διαφορετικό k οπότε αναμένουμε να μην χρειαστεί να περιμένει. Διατηρούμε σε έναν πίνακα την πληροφορία για το αν μία γραμμή είναι έτοιμη (ως διαγώνιο στοιχείο αναφοράς). Η εγγραφή/ ανάγνωση σε αυτόν τον πίνακα προστατεύεται με τα κατάλληλα locks, αλλά τον σχετικό κώδικα δεν τον παραθέτουμε για λόγους παρουσίασης. Ο αλγόριθμος αυτός δεν είναι ιδιαίτερα γρήγορος, αλλά έχει το σημαντικό πλεονέκτημα ότι είναι σταθερός στις επιδόσεις του. Αυτό θα φανεί καθαρά στις μετρήσεις που ακολουθούν σε επόμενο κεφάλαιο. Κώδικας for (i=id;i<size;i+=ν){ for (k=0;k<i;k++){ if (linewritten[k]==0) Wait(); for (j=0;j<size;j++) a[i][j]=min(a[i][j],a[i][k]+a[k][j]); } linewritten[i]=1; SignalAll(); } for (i=id;i<size;i+=n) for (k=i+1;k<size;k++) for (j=0;j<size;j++) a[i][j]=min(a[i][j],a[i][k]+a[k][j]); 55

Διάγραμμα πρόσβασης στα δεδομένα Διάγραμμα 13 Διάγραμμα 14 56

6.3 BKBIJ Το ζητούμενο είναι να γίνονται οι υπολογισμοί σε χωρία μεγέθους B1xB2, όπως και στον σειριακό αλγόριθμο ώστε να έχουμε όλα τα οφέλη της καλύτερης διαχείρισης της cache. Στόχος μας είναι να μειώσουμε την ανάγκη συγχρονισμού ανάμεσα στους επεξεργαστές. Προσαρμόζουμε λοιπόν τον σειριακό αλγόριθμο ως εξής: 1. υπολογίζουμε πρώτα ένα τετράγωνο μεγέθους B 1 xb 1 πάνω στην διαγώνιο 1.1. Παραλληλοποιούμε τον υπολογισμό αυτό χωρίζοντας το χωρίο σε τμήματα Β2xB2. 1.2. Υπολογίζουμε σειριακά το τετράγωνο της διαγωνίου και μετά τα υπόλοιπα παράλληλα. 2. Μοιράζουμε τις υπόλοιπες Size-B1 γραμμές στους Ν επεξεργαστές και καθένας υπολογίζει ότι του αναλογεί με τον BKBIJ αλγόριθμο χωρίς να συντονίζεται με τους υπόλοιπους Επιτυγχάνουμε έτσι μικρό κόστος συντονισμού ενώ ο χρόνος που δαπανάται στο σειριακό τμήμα είναι πολύ μικρότερος από τον χρόνο που κερδίζουμε Κώδικας sizepart = (size-b1)/n; sizediagonal = (B1-B2)/N; for (k=0;k<size;k+=b1){ for (kk=k;kk<k+b1;kk+=b2){ if (id==0){ FW_Simple_Offset(kk,B2,0,size,kk,B2); SignalAll(); } else Wait(); starti = (kk-k+b2+id*sizediagonal)%b1+k; endi = (kk-k+b2+id*sizediagonal+sizediagonal)%b1+k; if (starti<endi) FW_Simple_Offset(a,startI,sizeDiagonal,0,size,kk,B2); else { FW_Simple_Offset(a,startI,k+B1-startI,0,size,kk,B2); FW_Simple_Offset(a,k,endI-k,0,size,kk,B2); } } if(!last(id)){ Wait(); } else { SignalAll(); } starti = (k+b1+id*sizepart)%size; 57

} endi = (k+b1+id*sizepart+sizepart)%size; if (starti<endi) FW_BKBIJ_Offset(a,startI,sizePart,0,size,k,B1,B1,B2); else{ FW_BKBIJ_Offset(a,startI,size-startI,0,size,k,B1,B1,B2); FW_BKBIJ_Offset(a,0,endI,0,size,k,B1,B1,B2); } if(!last(id)){ Wait(); } else SignalAll(); Διάγραμμα πρόσβασης στα δεδομένα Διάγραμμα 15 58

6.4 Tiled Η παραλληλοποίηση αυτής της μεθόδου μοιάζει με την προηγούμενη και προκύπτει άμεσα από την αρχική μέθοδο. Υπολογίζουμε σειριακά το πρώτο τμήμα [επί της διαγωνίου στο (ij)-επίπεδο], μοιράζουμε τους επεξεργαστές τα τμήματα που βρίσκονται στις ίδιες γραμμές ή στήλες με το πρώτο και τέλος μοιράζουμε όλα τα υπόλοιπα. Κώδικας for (t=0;t<tiles;t++){ if (id==0) { FW_Simple_Offset(t*sizeTile,t*sizeTile,t*sizeTile,sizeTile); SignalAll(); } else Wait(); for (j=id;j<tiles-1;j+=n) { FW_Simple_Offset(j,t,id); FW_Simple_Offset(t,j,id); } if(!last(id)) Wait(); else SignalAll(); for (i=id;i<tiles-1;i+=n) for (j=0;j<tiles;j++) FW_Simple_Offset(i,j,id); if(!last(id)) Wait(); else SignalAll(); } 59

Διάγραμμα πρόσβασης στα δεδομένα Διάγραμμα 16 Διάγραμμα 17 60