+ All Categories
Home > Documents > ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ ΤΜΗΜΑ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ...

ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ ΤΜΗΜΑ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ...

Date post: 21-Feb-2023
Category:
Upload: utech
View: 0 times
Download: 0 times
Share this document with a friend
122
ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ ΤΜΗΜΑ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΤΕΧΝΟΛΟΓΙΑΣ ΥΠΟΛΟΓΙΣΤΩΝ ΤΟΜΕΑΣ: ΗΛΕΚΤΡΟΝΙΚΗΣ ΚΑΙ ΥΠΟΛΟΓΙΣΤΩΝ ΕΡΓΑΣΤΗΡΙΟ Διπλωματική Εργασία του φοιτητή του Τμήματος Ηλεκτρολόγων Μηχανικών και Τεχνολογίας Υπολογιστών της Πολυτεχνικής Σχολής του Πανεπιστημίου Πατρών Μπεμπέλης Ευάγγελος του Χαριλάου Αριθμός Μητρώου: 5702 Θέμα «Υλοποίηση συστήματος κρυφών μνημών» Επιβλέπων Γκούτης Κωνσταντίνος Αριθμός Διπλωματικής Εργασίας: Πάτρα, (Οκτώβρης, 2009)
Transcript

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

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

Πανεπιστημίου Πατρών

Μπεμπέλης Ευάγγελος του Χαριλάου

Αριθμός Μητρώου: 5702

Θέμα

«Υλοποίηση συστήματος κρυφών μνημών»

ΕπιβλέπωνΓκούτης Κωνσταντίνος

Αριθμός Διπλωματικής Εργασίας:

Πάτρα, (Οκτώβρης, 2009)

ΠΙΣΤΟΠΟΙΗΣΗ

Πιστοποιείται ότι η Διπλωματική Εργασία με θέμα

«Υλοποίηση συστήματος κρυφών μνημών»

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

Μπεμπέλης Ευάγγελος του Χαριλάου

Αριθμός Μητρώου: 5702

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

…….../……../………

Ο Επιβλέπων Ο Διευθυντής του Τομέα

Γκούτης Κωνσταντίνος Χούσος Ευθύμιος Καθηγητής Καθηγητής

2

Αριθμός Διπλωματικής Εργασίας:

Θέμα: «Υλοποίηση συστήματος κρυφών μνημών»

Φοιτητής: Επιβλέπων: Μπεμπέλης Ευάγγελος Γκούτης Κωνσταντίνος

Περίληψη

Στα πλαίσια της παρούσας διπλωματικής εργασίας υλοποιήθηκε ένας εξομοιωτής κρυφών μνημών και ένας επεξεργαστής αρχιτεκτονικής τύπου MIPS σε γλώσσα προγραμματισμού Java. Με την χρήση του εξομοιωτή και ενός αλγορίθμου πολλαπλασιασμού πίνακα επί πίνακα γραμμένο σε συμβολική γλώσσα assembly αξιολογήθηκαν τα χαρακτηριστικά των κρυφών μνημών όπως συσχετιστικότητα, μέγεθος μπλοκ, μέγεθος μνήμης και ιεραρχία μνήμης. Ως αποτέλεσμα κατασκευάστηκε ένα εργαλείο αξιολόγησης τόσο της αρχιτεκτονικής του υλικού για έναν αλγόριθμο όσο και της αποδοτικότητας ενός αλγορίθμου που εισάγεται στο πρόγραμμα για μια αρχιτεκτονική.

Περιεχόμενα .............................................................................................................................................................1ΕΙΣΑΓΩΓΗ...........................................................................................................................................6ΑΠΟΔΟΣΗ...........................................................................................................................................8

Απόδοση υπολογιστικού συστήματος.............................................................................................8Βελτίωση της απόδοσης................................................................................................................10Απόδοση της CPU.........................................................................................................................11Παραλληλισμός..............................................................................................................................13

ΜΝΗΜΗ............................................................................................................................................14Τοπικότητα.....................................................................................................................................14Ιεραρχία μνήμης............................................................................................................................14Κρυφή μνήμη.................................................................................................................................16Βασικές λειτουργίες κρυφών μνημών...........................................................................................18

Τοποθέτηση...............................................................................................................................19Αναγνώριση...............................................................................................................................20Αντικατάσταση..........................................................................................................................21Εγγραφή....................................................................................................................................22

Απόδοση κρυφής μνήμης...............................................................................................................23Μέσος όρος προσπέλασης μνήμης και απόδοση του επεξεργαστή..........................................24

Βελτίωση της απόδοσης της κρυφής μνήμης................................................................................25Μείωση ποινής αστοχίας κρυφής μνήμης.................................................................................25Μείωση ρυθμού αστοχίας.........................................................................................................28Μειώνοντας την ποινή ή το ρυθμό αστοχίας μέσω παραλληλισμού........................................30Μείωση χρόνου ευστοχίας........................................................................................................31

ΥΛΟΠΟΙΗΣΗ.....................................................................................................................................33Υλοποίηση εξομοιωτή...................................................................................................................33

Αρχιτεκτονική επεξεργαστή.....................................................................................................33Ιεραρχία μνήμης........................................................................................................................34Εξομοίωση.................................................................................................................................34

ΜΕΤΡΗΣΕΙΣ – ΣΥΜΠΕΡΑΣΜΑΤΑ..................................................................................................36Διάταξη L1 Shared Cache – L2 Shared Cache..............................................................................38

Αλλαγή associativity.................................................................................................................38Αλλαγή μεγέθους μνήμης.........................................................................................................41Αλλαγή μεγέθους μνήμης – Write Through..............................................................................43Αλλαγή μεγέθους μνήμης – Write Back...................................................................................45Εξομοίωση bottleneck...............................................................................................................46Αλλαγή μεγέθους block με σταθερό μέγεθος μνήμης..............................................................47

Διάταξη L1 Instruction Cache, L1 Data Cache – L2 Shared Cache..............................................50Αλλαγή associativity.................................................................................................................50Αλλαγή μεγέθους μνήμης.........................................................................................................53Αλλαγή μεγέθους μνήμης – Write Through..............................................................................56Αλλαγή μεγέθους μνήμης – Write Back...................................................................................58Εξομοίωση bottleneck...............................................................................................................61Αλλαγή μεγέθους block με σταθερό μέγεθος μνήμης..............................................................61

ΠΑΡΑΡΤΗΜΑΤΑ...............................................................................................................................65Παράρτημα 1: Κώδικας Java του εξομοιωτή.................................................................................65Παράρτημα 2: Αλγόριθμος πολλαπλασιασμού πινάκων σε C.....................................................117

4

Παράρτημα 3: Αλγόριθμοι πολλαπλασιασμού πινάκων σε assembly.........................................11716 x 16.....................................................................................................................................11732 x 23.....................................................................................................................................119

ΕΙΣΑΓΩΓΗ

Από την εποχή που ο άνθρωπος άρχισε να μηχανεύεται, εμφανίστηκαν και οι πρώτες μηχανές που απλοποιούσαν τις αριθμητικές πράξεις και τους υπολογισμούς. Έτσι από το 5000 π.Χ. εμφανίζεται ο άβακας. Κατά τα επόμενα χρόνια εμφανίστηκαν διάφορες άλλες υπολογιστικές μηχανές όπως ο αστρολάβος και ο μηχανισμός των Αντικυθήρων, αλλά η έννοια του υπολογιστή εμφανίστηκε την εποχή της αναγέννησης και δεν αναφερόταν σε μηχανή αλλά σε άνθρωπο που έκανε υπολογισμούς και πράξεις.Το 1822 ο Charles Babbage πρότεινε την κατασκευή μιας αναλυτικής μηχανής (analytical engine) η οποία υλοποιήθηκε το 1847 και ήταν ικανή να υπολογίζει λογαρίθμους και τριγωνομετρικές συναρτήσεις. Χρησιμοποιούσε ως είσοδο διάτρητες κάρτες που είχαν ήδη εμφανισθεί στους αργαλειούς και ως έξοδο είχε έναν εκτυπωτή, έναν σχεδιογράφο καμπυλών και ένα καμπανάκι. Η δομή του περιείχε στοιχεία που μοιάζουν πολύ με τους σύγχρονους υπολογιστές, όπως μνήμη και αριθμητική μονάδα ενώ η γλώσσα προγραμματισμού έμοιαζε πολύ με σύγχρονες συμβολικές γλώσσες (assembly language), ικανή να περιέχει διακλαδώσεις και επαναλήψεις.Στην αρχή του 20ου αιώνα η όλο και αυξανόμενη ανάγκη για επιστημονικούς υπολογισμούς οδήγησε στην ανάπτυξη των πρώτων σύγχρονων υπολογιστικών συστημάτων καθώς και την εμφάνιση εννοιών όπως η άλγεβρα Boolean, η διάτρητη κάρτα και η λυχνία. Το 1936 o Allan Turing διατύπωσε μια τυποποίηση της έννοιας του αλγορίθμου και του υπολογισμού με την μηχανή Turing. Κατά την διάρκεια του 2ου Παγκοσμίου Πολέμου η ανάγκη κρυπτογραφίας και ανάλυσης δεδομένων των αντιμαχόμενων πλευρών ώθησε στην ανάπτυξη όλο και πιο σύνθετων υπολογιστικών μηχανών όπως αυτή του Γερμανού Konrad Zuse, Z3, η οποία χρησιμοποιήθηκε για την στατιστική ανάλυση των ανεπιθύμητων ταλαντώσεων στα φτερά των αεροσκαφών. Άλλες μηχανές είναι η βρετανική Colossus που χρησιμοποιήθηκε για την αποκωδικοποίηση γερμανικών μηνυμάτων. Τελικά το 1946 εμφανίσθηκε ο πρώτος υπολογιστής γενικής χρήσης ο ENIAC (Electronic Numerical Integrator And Computer).Στις αρχές της δεκαετίας του 50 τα transistor ήταν διαθέσιμα δίνοντας την δυνατότητα κατασκευής φθηνότερων και πιο γρήγορων υπολογιστών. Οι υπολογιστές που υλοποιήθηκαν την εποχή αυτή αποτέλεσαν την δεύτερη γενιά υπολογιστών και έθεσαν τις βάσεις για την επόμενη και πιο σημαντική γενιά: των ολοκληρωμένων κυκλωμάτων (integrated circuits). Η εμφάνιση ολοκληρωμένων οφείλεται στους Jack St. Clair Kilby και Robert Noyce κατά την δεκαετία του 1960. Στα χρόνια που ακολούθησαν η ανάπτυξη των υπολογιστών ήταν ραγδαία. Στα μέσα του 1970 η απόδοση των υπολογιστών τόσο αυτών μεγάλης ισχύος (mainframes) όσο των προσωπικών υπολογιστών (mini-computers) αυξανόταν κατά 25% με 30% ετησίως. Η ανάπτυξη των ολοκληρωμένων κυκλωμάτων οδήγησε στην εμφάνιση του μικροεπεξεργαστή. Λόγω της μεγάλης εξάρτησης την ανάπτυξης των υπολογιστών με αυτή των ολοκληρωμένων κυκλωμάτων η απόδοση των μικροεπεξεργαστών αυξανόταν με ακόμα μεγαλύτερο ρυθμό αφού μπορούσε να ενσωματώνει πιο πολύ τις βελτιώσεις των ολοκληρωμένων κυκλωμάτων από τους λιγότερο ολοκληρωμένους υπολογιστές. Το μειωμένο κόστος λόγω μαζικής παραγωγής καθώς και η αυξανόμενη απόδοση ώθησε τους μικροεπεξεργαστές να αποσπούν όλο και μεγαλύτερο μέρος της αγοράς υπολογιστών.Εκείνη την περίοδο εμφανίστηκαν δυο σημαντικές αλλαγές στην αγορά των υπολογιστών. Πρώτον η εξάλειψη του προγραμματισμού συμβολικής γλώσσας μείωσε την ανάγκη συμβατότητας του πηγαίου κώδικα (source code) ενώ δεύτερον η δημιουργία

6

τυποποιημένων λειτουργικών συστημάτων τα οποία ήταν ανεξάρτητα από τους κατασκευαστές, όπως το UNIX μείωσε το ρίσκο της εισαγωγής μιας νέας αρχιτεκτονικής στην αγορά.Στην αρχή της δεκαετίας του 1980 πράγματι εμφανίστηκε έναν νέο σύνολο αρχιτεκτονικών, οι οποίες ονομάστηκαν αρχιτεκτονικές Υπολογιστών Μειωμένου Συνόλου Εντολών (Reduced Instruction Set Computers, RISC). Οι αρχιτεκτονικές αυτές οδήγησαν τους σχεδιαστές προς την χρήση δυο πολύ κρίσιμων τεχνικών όσον αφορά την απόδοση: την εκμετάλλευση του παραλληλισμού σε επίπεδο εντολής, μέσω της σωλήνωσης (pipeline) και την πολλαπλή έκδοση εντολών (multiple instruction issue), και την χρήση κρυφών μνημών (cache memory). Πλέον η ανάπτυξη των υπολογιστών δεν βασιζόταν μόνο στην ανάπτυξη της τεχνολογίας αλλά και στην ανάπτυξη της αρχιτεκτονικής. Το γεγονός αυτός εκτίναξε την ετήσια αύξηση της απόδοσης σε πάνω του 50%.Έτσι, ενώ το 1980 οι μικροεπεξεργαστές σχεδιάζονταν χωρίς κρυφές μνήμες οι μεταγενέστεροι ενσωματώνουν πολλαπλά επίπεδα κρυφής μνήμης. Όμως ενώ όπως αναφέρθηκε οι επεξεργαστές αναπτύσσονται με έναν ετήσιο ρυθμό άνω του 50% η αντίστοιχη ανάπτυξη των μνημών είναι της τάξης του 7% με αποτέλεσμα να υπάρχει ένα μεγάλο κενό απόδοσης επεξεργαστή – μνήμης το οποίο πρέπει να καλυφθεί.Η εργασία αυτή μελετά την βελτιστοποίηση των διαφόρων χαρακτηριστικών των κρυφών μνημών ώστε να επιτευχθεί καλύτερη εκμετάλλευση των πόρων του επεξεργαστή.

ΑΠΟΔΟΣΗ

Απόδοση υπολογιστικού συστήματος

Ο χρόνος ανάμεσα στην έναρξη και την λήξη μιας διεργασίας ονομάζεται χρόνος εκτέλεσης (execution time). Αυτός ο χρόνος είναι πολύ σημαντικό χαρακτηριστικό ενός υπολογιστή αφού αποτελεί μέτρο σύγκρισης του υπολογιστή αυτού με άλλα συστήματα. Η απόδοση ενός υπολογιστή είναι:

απόδοση= 1χρόνος εκτέλεσης

(1)

Από την παραπάνω σχέση φαίνεται ότι η απόδοση (performance) και ο χρόνος εκτέλεσης (execution time) είναι αντίστροφα μεγέθη, αν αυξήσουμε την απόδοση θα μειωθεί ο χρόνος εκτέλεσης.Άλλο ένα σημαντικό χαρακτηριστικό ενός υπολογιστή είναι ο ρυθμός ολοκλήρωσης (throughtput). Το μέγεθος αυτό εκφράζει το συνολικό ποσό δουλειάς που γίνεται μέσα σε ένα συγκεκριμένο χρονικό διάστημα. Είτε χρησιμοποιούμε τον ρυθμό ολοκλήρωσης είτε τον χρόνο εκτέλεσης ο τρόπος μέτρησης είναι ο χρόνος, ο υπολογιστής που εκτελεί το ίδιο ποσό δουλείας σε λιγότερο χρόνο είναι ο πιο γρήγορος. Η διαφορά στα δυο μεγέθη είναι στο αν υπολογίζουμε βάση μιας εργασίας (χρόνος εκτέλεσης) ή βάση πολλών εργασιών (ρυθμός ολοκλήρωσης). Όμως ο χρόνος δεν είναι πάντα το κατάλληλο μέτρο σύγκρισης της απόδοσης των υπολογιστών.Αυτό συμβαίνει γιατί ο χρόνος εκτέλεσης μπορεί να οριστεί με διαφόρους τρόπους, ανάλογα με το τι μετράμε. Ο πιο άμεσος ορισμός του χρόνου λέγεται χρόνος ρολογιού τοίχου (wall – clock time). Αυτός ο χρόνος περιλαμβάνει τον συνολικό χρόνο εκτέλεσης μιας εργασίας μαζί με τους χρόνους πρόσβασης δίσκου, των προσπελάσεων μνήμης των λειτουργιών εισόδου – εξόδου (Ε/Ε), των επιβαρύνσεων του λειτουργικού συστήματος και ο,τιδήποτε άλλο επιβαρύνει το υπολογιστικό σύστημα την ώρα της μέτρησης. Ένας άλλος ορισμός είναι ο χρόνος CPU (Central Processing Unit - Κεντρική Μονάδα Επεξεργασίας, ΚΜΕ) που διακρίνει τους χρόνους που δαπανήθηκαν στις διεργασίες συστήματος και στον χρόνο που η CPU κάνει υπολογισμούς, χωρίς να περιλαμβάνεται ο χρόνος αναμονής για την Ε/Ε ή για την εκτέλεση άλλων προγραμμάτων. Ο χρόνος CPU υποδιαιρείται σε χρόνο εκτέλεσης του προγράμματος χρήστη και σε χρόνο εκτέλεσης του λειτουργικού συστήματος.Σημαντικό είναι να αναφερθούν τα προγράμματα τα οποία χρησιμοποιούνται για σύγκριση υπολογιστικών συστημάτων. Αυτά χωρίζονται στις παρακάτω κατηγορίες:1. Πραγματικές εφαρμογές:Για την μέτρηση της απόδοσης μπορούν να χρησιμοποιηθούν πραγματικές εφαρμογές όπως μεταγλωττιστές της C, λογισμικό επεξεργασίας κειμένου και άλλα προγράμματα καθημερινής χρήσης. Οι εφαρμογές αυτές έχουν ένα μεγάλο μειονέκτημα ως προγράμματα σύγκρισης (benchmarks), αυτό είναι η περιορισμένη φορητότητα τους αφού εξαρτώνται από το λειτουργικό σύστημα ή από τον μεταγλωττιστή. Για την βελτίωση της φορητότητας

8

χρησιμοποιούνται τροποποιήσεις του πηγαίου κώδικα αλλά αυτό συνήθως έχει ως αποτέλεσμα την κατάργηση σημαντικών διεργασιών, όπως των αλληλεπιδρώντων γραφικών τα οποία συνήθως είναι εξαρτημένα από το σύστημα.2. Τροποποιημένες (ή στημένες) εφαρμογές:Σε αυτήν την περίπτωση τα μετροπρογράμματα που χρησιμοποιούνται περιέχουν δομικά στοιχεία από πραγματικές εφαρμογές. Αυτό έχει ως σκοπό την βελτίωση της φορητότητας και την εστίαση της εκτέλεσης σε συγκεκριμένη πλευρά της απόδοσης του συστήματος. Δηλαδή για να γίνει πιο αποτελεσματική μέτρηση της απόδοσης μιας CPU πρέπει να απομακρυνθούν ή να ελαχιστοποιηθούν η επίδραση της Ε/Ε στον χρόνο εκτέλεσης. Αυτά τα μετροπρογράμματα χρησιμοποιούνται για την προσομοίωση πραγματικών εφαρμογών ή για προσομοίωση σύνθετων αλληλεπιδράσεων χρηστών σε έναν εξυπηρετητή.3. ΠυρήνεςΣε αυτήν την περίπτωση αποσπώνται μικρά βασικά κομμάτια από πραγματικές εφαρμογές και χρησιμοποιούνται για την μέτρηση της απόδοσης. Σε αντίθεση με τις πραγματικές εφαρμογές κανείς δεν θα έτρεχε προγράμματα πυρήνα αφού χρησιμοποιούνται μόνο για την μέτρηση της απόδοσης. Οι πυρήνες χρησιμοποιούνται περισσότερο για την απομόνωση της απόδοσης κάποιων μεμονωμένων χαρακτηριστικών μιας μηχανής. Αυτό έχει ως σκοπό την εξήγηση των λόγων για τους οποίους υπάρχουν διαφορές στην απόδοση πραγματικών εφαρμογών.4. ΠαιχνιδομετροπρογράμματαΤα προγράμματα αυτά είναι από 10 έως 100 γραμμές κώδικα και παράγουν ένα αποτέλεσμα το οποίο ο χρήστης γνωρίζει πριν τρέξει το παιχνιδοπρόγραμμα. Η καλύτερη χρήση αυτών των προγραμμάτων είναι για προγραμματιστικές ασκήσεις χαμηλού επιπέδου.5. Συνθετικά μετροπρογράμματαΌπως και η πυρήνες τα συνθετικά μετροπρογράμματα προσπαθούν να επιτύχουν τη μέση συχνότητα λειτουργιών και τελεστών λειτουργίας ενός μεγάλου συνόλου προγραμμάτων. Κανένας χρήστης δεν τρέχει συνθετικά μετροπρογράμματα διότι δεν υπολογίζουν τίποτα από αυτά που ο χρήστης θα ήθελε. Για την ακρίβεια τα συνθετικά μετροπρογράμματα απέχουν ακόμα πιο πολύ από την πραγματικότητα από ότι οι πυρήνες επειδή ο κώδικας των πυρήνων προέρχεται από πραγματικά προγράμματα ενώ ο κώδικας των συνθετικών μετροπρογραμμάτων έχει δημιουργηθεί τεχνητά προκειμένου να επιτευχθεί ένα μέσο προφίλ εκτέλεσης. Τα συνθετικά μετροπρογράμματα δε είναι καν κομμάτια αληθινού προγράμματος, ενώ οι πυρήνες είναι.Πλέον είναι δημοφιλής η δημιουργία συλλογών από μετροπρογράμματα σε ια προσπάθεια μέτρησης της απόδοσης των επεξεργαστών με μια ποικιλία εφαρμογών. Αν και τέτοιου είδους συλλογές είναι τόσο καλές όσο τα μετροπρογράμματα που τις αποτελούν, το βασικό πλεονέκτημα που προσφέρουν είναι ότι η αδυναμία ενός μετροπρογράμματος μειώνεται από την παρουσία των άλλων. Αυτό είναι ιδιαίτερα αληθινό αν οι μέθοδοι που χρησιμοποιούνται για την σύνοψη της απόδοσης της συλλογής, απεικονίζουν τον χρόνο που χρειάζεται για να τρέξει ολόκληρη η συλλογή, και δεν επιβραβεύουν αυξήσεις της απόδοσης των επιμέρους προγραμμάτων, οι οποίες μπορούν να ανατραπούν από κάποιες βελτιστοποιήσεις.

Βελτίωση της απόδοσης

Με βάση τα παραπάνω αξίζει πλέον να αναφερθούν τρόποι με τους οποίους βελτιώνεται η απόδοση ενός υπολογιστικού συστήματος.Η πλέον σημαντική και ευρεία αρχή του σχεδιασμού υπολογιστών είναι να γίνεται γρήγορη η κοινή περίπτωση. Σε κάθε σχεδίαση υπάρχει προτίμηση της συχνής περίπτωσης από ότι της σπάνιας. Η βελτίωση του συχνού γεγονότος έναντι του σπάνιου, προφανώς θα βελτίωση της απόδοση. Επίσης η συχνή περίπτωση συνήθως είναι απλούστερη και βελτιώνεται πιο γρήγορα από ότι η σπάνια. Για να εφαρμοστεί αυτή η αρχή πρέπει να εντοπιστεί η συνηθισμένη περίπτωση και κατά πόσο μπορεί να βελτιωθεί η απόδοση κάνοντας την γρηγορότερη. Ένας θεμελιώδης νόμος, ο Νόμος του Amdahl, μπορεί να χρησιμοποιηθεί για να ποσοτικοποιήσουμε αυτή την αρχή.Το κέρδος σε απόδοση που μπορούμε να έχουμε βελτιώνοντας κάποιο μέρος του υπολογιστή μπορεί να μετρηθεί χρησιμοποιώντας τον Νόμο του Amdahl. Ο Νόμος του Amdahl υποστηρίζει ότι η βελτίωση της απόδοσης που επιτυγχάνεται με την χρήση μιας πιο γρήγορης μεθόδου εκτέλεσης περιορίζεται από το κλάσμα του χρόνου στον οποίο μπορεί να χρησιμοποιηθεί η μέθοδος αυτή.Ο Νόμος του Amdahl ορίζει την επιτάχυνση που μπορούμε να αποκτήσουμε με ένα συγκεκριμένο χαρακτηριστικό. Επιτάχυνση είναι ο λόγος:

Επιτάχυνση= Απόδοση χρησιμοποιώντας την βελτιστοποίησηόπου μπορούμεΑπόδοση χωρίς την βελτιστοποίηση

(2)

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

Επιτάχυνση= Χρόνος Εκτέλεσης χωρίς βελτιστοποίησηΧρόνος εκτέλεσης χρησιμοποιώντας την βελτιστοποίηση όπου μπορούμε (3)

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

10

ΧρόνοςΕκτέλεσηςνέος

= ΧρόνοςΕκτέλεσηςπαλιός

×1−Κλάσμα βελτιστοποίησηςΚλάσμα βελτιστοποίησης

Επιτάχυνση βελτιστοποίησης (4)

Τελικά:

Επιτάχυνσησυνολική=

Χρόνοςεκτέλεσης παλιός

Χρόνοςεκτέλεσης νέος

= 1

1−Κλάσμα βελτιστοποίησηςΚλάσμα βελτιστοποίησης

Επιτάχυνση βελτιστοποίησης (5)

Ο νόμος του Amdahl εκφράζει το νόμο των μειούμενων αποδόσεων: Η ποσοστιαία βελτίωση της επιτάχυνσης που αποκτάται με μια επιπρόσθετη βελτίωση της απόδοσης ενός μέρους μόνο του υπολογισμού, μειώνεται καθώς προστίθενται βελτιώσεις. Ένα σημαντικό πόρισμα που προκύπτει από τον νόμο του Amdahl είναι ότι, αν μια βελτιστοποίηση μπορεί να χρησιμοποιηθεί μόνο για ένα κλάσμα της εργασίας, τότε δεν μπορούμε να επιταχύνουμε την εργασία περισσότερο από το αντίστροφο του 1 μείον αυτό το κλάσμα.Ο Νόμος του Amdahl μπορεί να χρησιμεύσει ως οδηγός για το πόσο μια βελτιστοποίηση θα βελτιώσει την απόδοση και για το πως θα κατανέμουμε τους πόρους για να βελτιώσουμε τη σχέση κόστους προς απόδοση. Ξεκάθαρα, ο στόχος είναι να χρησιμοποιούνται οι πόροι αναλογικά με το που καταναλώνεται ο χρόνος.

Απόδοση της CPU

Η πλειοψηφία των σύγχρονων υπολογιστικών συστημάτων δουλεύουν σύγχρονα βάση ενός ρολογιού το οποίο λειτουργεί με σταθερή συχνότητα. Οι διακριτοί χρόνοι που προκύπτουν από το ρολόι που χρησιμοποιήται έχουν διάφορα ονόματα: τικ (ticks), χτύποι ρολογιού (clock ticks), περίοδοι ρολογιού (clock periods), ρολόγια (clocks), κύκλοι (cycles) ή κύκλοι ρολογιού (clock cycles). Σε αυτήν την εργασία θα χρησιμοποιηθεί ο όρος κύκλος (cycle). Η ποσότητα αυτή μετριέται είτε σε συχνότητα (Ghz) είτε σε χρόνο (ns). Ο χρόνος της CPU εκφράζεται ως:

ΧρόνοςCPU= ΚύκλοιCPUγια έναπρόγραμμα

×Χρόνοςκύκλου

=

ΚύκλοιCPUγια έναπρόγραμμαΣυχνότητα ρολογιού

(6)

Εκτός από τον αριθμό των κύκλων ρολογιού που χρειάζονται για να εκτελεστεί ένα πρόγραμμα ένα άλλο σημαντικό μέγεθος είναι ο αριθμός των εντολών που εκτελούνται – το μήκος μονοπατιού εντολών (instruction path length) ή τον αριθμό των εντολών ( instruction count, IC). Αν είναι γνωστός ο αριθμός των κύκλων ρολογιού και ο αριθμός των

εντολών τότε υπολογίζεται ο μέσος αριθμός των κύκλων ρολογιού ανά εντολή (clock cycles per instruction, CPI). Μερικές φορές χρησιμοποιούνται και οι εντολές ανά κύκλο ρολογιού (instructions per clock, IPC), το αντίστροφο του CPI. Το CPI υπολογίζεται ως εξής:

CPI= ΚύκλοιCPU γιαένα πρόγραμμαΑριθμός εντολών (7)

Λόγω της ανεξαρτησίας του συνόλου εντολών το CPI είναι πολύ σημαντικό μέτρο απόδοσης το οποίο χρησιμοποιήται στην παρούσα εργασία. Επίσης το CPI μπορεί να συσχετιστεί με τον χρόνο CPU ως εξής:

ΧρόνοςCPU=Αριθμός εντολών×Χρόνος κύκλου ρολογιού×CPI= Αριθμός Εντολών×CPIΣυχνότητα ρολογιού (8)

Συμπερασματικά η απόδοση μιας CPU εξαρτάται από τον κύκλο ρολογιού, τους κύκλους ρολογιού ανά εντολή (CPI) και τον αριθμό εντολών. Οι τρεις αυτοί παράγοντες εξαρτώνται ως εξής:

• Χρόνος κύκλου ρολογιού - Τεχνολογία υλικού και οργάνωση

• CPI – Οργάνωση και αρχιτεκτονική συνόλου εντολών

• Αριθμός εντολών – Αρχιτεκτονική συνόλου εντολών και τεχνολογία μεταγλώττισηςΛόγω της άμεσης αλληλεπίδρασης των τριών αυτών χαρακτηριστικών είναι δύσκολο να βελτιωθεί μόνο ένα από τα τρία χωρίς να επηρεαστούν τα άλλα δυο. Η κύρια τακτική είναι η βελτίωση του ενός από τα τρία με προβλέψιμες και μικρές επιδράσεις στα άλλα δυο.Σημαντικός επίσης είναι ο αριθμός των συνολικών κύκλων ρολογιού της CPU με τον τύπο:

Κύκλοι ρολογιούCPU=∑i=1

n

IC i×CPI i (9)

όπου IC ο αριθμός τον φορών που εκτελείται η εντολή i σε ένα πρόγραμμα και το CPI συμβολίζει το μέσο αριθμό κύκλων ρολογιού για την εντολή i. Αυτός ο τύπος μπορεί να χρησιμοποιηθεί για να εκφραστεί ο χρόνος CPU ως:

ΧρόνοςCPU=∑i=1

n

IC i×CPI i×Χρόνος κύκλου ρολογιού (10)

και το συνολικό CPI:

12

CPI=∑i=1

n

IC i×CPI i

Αριθμός εντολών=∑

i=1

n IC i

Αριθμός εντολών×CPI i

(11)

Το CPI υπολογίζεται με τον πολλαπλασιασμό κάθε ξεχωριστού CPI με το κλάσμα των εμφανίσεων της εντολής i σε ένα πρόγραμμα. Το CPI της κάθε i εντολής πρέπει να μετριέται και όχι να υπολογίζεται ώστε να περιλαμβάνονται οι επιδράσεις του pipeline (σωλήνωση) και οι αστοχίες της κρυφής μνήμης που θα αναλυθούν παρακάτω.

Παραλληλισμός

Η έννοια του παραλληλισμού είναι πολύ σημαντική στους υπολογιστές για αυτόν τον λόγο μια σύντομη αναφορά σε αυτήν την έννοια θα γίνει σε αυτήν την παράγραφο. Υπάρχουν τρία κύρια επίπεδα παραλληλισμού. Το πρώτο είναι ο παραλληλισμός σε επίπεδο συστήματος. Για να έχουμε άμεση επιτάχυνση ενός υπολογιστικού συστήματος μπορούμε να χρησιμοποιήσουμε πολλαπλούς επεξεργαστές και πολλαπλούς δίσκους. Αυτός είναι ο κύριος λόγος που ένα από τα πιο σημαντικά χαρακτηριστικά ενός υπολογιστή είναι η επεκτασιμότητα του. Το δεύτερο επίπεδο είναι ο παραλληλισμός μεταξύ εντολών ο οποίος είναι σημαντικός για την επίτευξη υψηλής απόδοσης. Ο πιο απλός τρόπος επίτευξης αυτού του είδους παραλληλισμού είναι μέσω του pipeline. Η βασική ιδέα του pipeline είναι η επικάλυψη της εκτέλεσης των εντολών για να μειωθεί ο συνολικός χρόνος εκτέλεσης μιας ακολουθίας εντολών. Η άμεση επίδραση του pipeline στην απόδοση της CPU φαίνεται από τους παραπάνω τύπους αφού μπορούμε να θεωρήσουμε ότι το pipeline μειώνει το CPI καθώς επιτρέπει την επικάλυψη εντολών με πολλαπλούς κύκλους εκτέλεσης. Ένα βασικό χαρακτηριστικό που επιτρέπει στο pipeline να έχει αυτά τα αποτελέσματα είναι ότι αρκετές εντολές είναι ανεξάρτητες από τις αμέσως προηγούμενες τους και έτσι η εκτέλεση των εντολών μερικώς ή εξ ολοκλήρου παράλληλα μπορεί να είναι δυνατή.Τέλος μπορούμε να εκμεταλλευτούμε τον παραλληλισμό στο επίπεδο της λεπτομερούς ψηφιακής σχεδίασης. Αυτές οι τεχνικές χρησιμοποιούνται για παράδειγμα στις συνολοσυσχετιστικές κρυφές μνήμες συνάφειας συνόλου όπου πολλές υπομονάδες μνήμης ενεργοποιούνται παράλληλα κατά την διάρκεια μιας αναζήτησης.

ΜΝΗΜΗ

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

Τοπικότητα

Παρά το γεγονός ότι ο Νόμος του Amdahl είναι ένα θεώρημα που εφαρμόζεται σε οποιοδήποτε σύστημα, άλλες σημαντικές παρατηρήσεις προέρχονται από τις ιδιότητες των προγραμμάτων. Η πιο σημαντική ιδιότητα την οποία χρησιμοποιούμε συχνά είναι η αρχή της τοπικότητας (locality). Τα προγράμματα τείνουν να ξαναχρησιμοποιήσουν δεδομένα και εντολές που χρησιμοποίησαν πρόσφατα. Ένας γενικά αποδεκτός εμπειρικός κανόνας λέει ότι ένα πρόγραμμα περνά το 90% του χρόνου εκτέλεσης σε μόνο 10% του κώδικα. Αποτέλεσμα της τοπικότητας είναι η δυνατότητα πρόβλεψης με σχετική ακρίβεια ποιες εντολές και ποια δεδομένα θα χρησιμοποιηθούν από ένα πρόγραμμα στο άμεσο μέλλον βάση των προσβάσεων του προγράμματος στο πρόσφατο παρελθόν.Η αρχή της τοπικότητας ισχύει και για προσβάσεις δεδομένων αλλά όχι τόσο όσο στις προσβάσεις κώδικα. Έχουν παρατηρηθεί δυο διαφορετικά είδη τοπικότητας: Η χρονική τοπικότητα η οποία ορίζει ότι τα αντικείμενα που χρησιμοποιήθηκαν πρόσφατα θα χρησιμοποιηθούν στο εγγύς μέλλον. Η χωρική τοπικότητα λέει ότι αντικείμενα των οποίων οι διευθύνσεις είναι κοντινές μεταξύ τους τείνουν να προσπελάζονται σε κοντινούς μεταξύ τους χρόνους.

Ιεραρχία μνήμης

Είναι κοινά αποδεκτό ότι θα ήταν ιδανικό ένας υπολογιστής να έχει πρόσβαση σε απεριόριστες ποσότητες ταχείας μνήμης. Αυτό όμως δεν είναι δυνατόν λόγω κόστους για αυτό μια οικονομικότερη λύση η οποία εκμεταλλεύεται την τοπικότητα και το κόστος απόδοσης των τεχνολογιών μνήμης. Αυτή είναι η δημιουργία μιας ιεραρχικής δομής μνημών διαφορετικού μεγέθους, ταχύτητας και κόστους.Έτσι οι μνήμες χωρίζονται σε μικρές μνήμες οι οποίες επιτρέπουν γρήγορη πρόσβαση και πολύ μεγάλο bandwidth, σε αργότερες μεγαλύτερες μνήμες οι οποίες μπορούν να καλύπτουν μεγαλύτερο μέρος της μνήμης και έχουν μια ικανοποιητικά γρήγορη ταχύτητα και τέλος σε τεράστιες μνήμες οι οποίες κρατούν δεδομένα που σπανίως χρειάζονται και χρησιμοποιούνται για την ορθότητα του συστήματος. Όλα μαζί αυτά τα επίπεδα παρέχουν την εμφάνιση μιας μεγάλης γρήγορης μνήμης με κόστος φθηνής αργής μνήμης.Όπως ήδη αναφέρθηκε η ιεραρχική μνήμη δουλεύει λόγω της τοπικότητας τόσο χωρικής όσο και τοπικής καθώς και του γεγονότος του ότι γίνονται περισσότερες αναγνώσεις από εγγραφές στην μνήμη. Αυτό συμβαίνει γιατί όλες οι φορτώσεις εντολών (instruction fetches) είναι αναγνώσεις και οι περισσότερες προσβάσεις σε δεδομένα είναι αναγνώσεις. Έτσι οι ιεραρχικές μνήμες σχεδιάζονται με βελτιστοποιημένη την ικανότητα ανάγνωσης.Ένα απλό μοντέλο κρυφής μνήμης μπορεί να εκτιμηθεί ως εξής:

Με t c τον χρόνο προσπέλασης της κρυφής μνήμης, tm ο επιπρόσθετος χρόνος σε

14

περίπτωση αστοχίας της κρυφής μνήμης και h το κλάσμα της μνήμης που προσπελαύνεται επιτυχώς στην κρυφή μνήμη τότε η μέσος ή αποτελεσματικός χρόνος προσπέλασης μνήμης είναι:

t eff=h t c1−h tm (12)

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

Μια τυπική ιεραρχία μνήμης είναι η παρακάτω:

Όπως φαίνεται στο σχήμα 1 οι μνήμες με το μικρότερο μέγεθος και το μικρότερο χρόνο προσπέλασης βρίσκονται “κοντινότερα” στον επεξεργαστή ενώ μεγαλώνουν όσο η μνήμη απομακρύνεται από τον επεξεργαστή.

Κρυφή μνήμη

Κρυφή μνήμη είναι το όνομα που δίνεται στο πρώτο επίπεδο της ιεραρχίας της μνήμης που συναντάται από την στιγμή που η διεύθυνση αφήνει την CPU.[CA001] Από την στιγμή που η αρχή της τοπικότητας ισχύει σε σε πολλά επίπεδα και η εκμετάλλευση της

Σχήμα 1: Ιεραρχία Μνήμης

τοπικότητας για βελτίωση της απόδοσης είναι δημοφιλής, ο όρος κρυφή μνήμη πλέον απευθύνεται οπουδήποτε γίνεται εκμετάλλευση της μνήμης για να χρησιμοποιηθούν στοιχεία που συχνά συναντώνται.Όταν η CPU βρίσκει ένα αιτούμενο στοιχείο δεδομένων στην κρυφή μνήμη αυτό ονομάζεται ευστοχία κρυφής μνήμης (cache hit). Όταν η CPU δεν βρίσκει ένα στοιχείο δεδομένων στην κρυφή μνήμη συμβαίνει αστοχία κρυφής μνήμης (cache miss). Μια συγκεκριμένου μεγέθους συλλογή δεδομένων, που περιλαμβάνει την απαιτούμενη λέξη που ονομάζεται μπλοκ, ανασύρεται από την κυρία μνήμη και τοποθετήται στην κρυφή μνήμη. Το μέγεθος είναι τις τάξης των 2k bytes όπου το k συνήθως παίρνει τιμές από 1 μέχρι 15. Το μέγεθος του μπλοκ μπορεί να διαφέρει από την κύρια μνήμη στην cache και από την κύρια μνήμη στον δίσκο (σελιδοποίηση εικονικής μνήμης, virtual memory page).Η χρονική τοπικότητα προβλέπει ότι μια λέξη θα χρησιμοποιηθεί πάλι στο εγγύς μέλλον έτσι είναι σωστό να τοποθετηθεί στην κρυφή μνήμη όπου θα είναι γρήγορα προσπελάσιμη. Λόγω της χωρικής τοπικότητας υπάρχει υψηλή πιθανότητα ότι και τα τα άλλα δεδομένα του μπλοκ θα χρειαστούν σύντομα.Ο χρόνος που χρειάζεται για την αστοχία κρυφής μνήμης εξαρτάται από την καθυστέρηση και από το εύρος ζώνης της μνήμης. Η καθυστέρηση αποφασίζει το χρόνο απόσυρσης της πρώτης λέξης του μπλοκ και το εύρος ζώνης τον χρόνο απόσυρσης του υπόλοιπου μπλοκ. Μια αστοχία μνήμης χειρίζεται το υλικό και αναγκάζει του επεξεργαστές , οι οποίοι ακολουθούν εκτέλεση σε σειρά να σταματήσουν ή να καθυστερήσουν μέχρι να είναι διαθέσιμα τα δεδομένα.Ομοίως, δεν είναι ανάγκη να είναι παρόντα στην κυρια μνήμη όλα τα αντικείμενα που αναφέρονται σε ένα πρόγραμμα/ Αν ο υπολογιστής έχει εικονική μνήμη, τότε μερικά αντικείμενα μπορούν να βρίσκονται στον δίσκο. Ο χώρος διευθύνσεων συνήθως διαιρείται σε μπλοκ σταθερού μεγέθους, που ονομάζονται σελίδες. Οποιαδήποτε στιγμή οποιαδήποτε σελίδα βρίσκεται είτε στην μνήμη είτε στον σκληρό δίσκο. Όταν η CPU αναφέρεται σε ένα στοιχείο μέσα σε μια σελίδα που δεν είναι παρούσα στην κρυφή μνήμη ή στην κύρια μνήμη, συμβαίνει ένα σφάλμα σελίδας και ολόκληρη η σελίδα μετακινείται από τον δίσκο στην κύρια μνήμη. Από την στιγμή που τα σφάλματα σελίδας καθυστερούν τόσο πολύ τα χειρίζεται λογισμικό και η CPU δεν σταματά. Η CPU συνήθως στρέφεται σε κάποια άλλη εργασία, ενώ συμβαίνει η προσπέλαση στο δίσκο. Η κρυφή μνήμη και η κύρια μνήμη έχουν την ίδια σχέση όπως η κύρια μνήμη και ο δίσκος.Εξαιτίας της τοπικότητας και της μεγαλύτερης ταχύτητας των μικρότερων μνημών, μια ιεραρχία μνήμης μπορεί ουσιαστικά να βελτιώσει την απόδοση σημαντικά. Μια μέθοδος αξιολόγησης της απόδοσης της κρυφής μνήμης είναι να επεκτείνουμε τον τύπο του χρόνου εκτέλεσης CPU που ήδη αναφέρθηκε. Υπολογίζοντας τον αριθμό των κύκλων, στη διάρκεια των οποίων η CPU σταματά περιμένοντας για μια προσπέλαση μνήμης , τους οποίους ονομάζουμε κύκλους καθυστέρησης μνήμης. Η απόδοση τότε είναι το γινόμενο του χρόνου κύκλου ρολογιού και του αθροίσματος των κύκλων CPU και των κύκλων καθυστέρησης μνήμης:

Χρόνος εκτέλεσηςCPU=Κύκλοι ρολογιούCPUκύκλοι καθυστέρησης ×Χρόνος κύκλου (13)

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

16

και από το κόστος ανά αστοχία, το οποίο ονομάζεται ποινή αστοχίας:

Κύκλοι καθυστέρησης μνήμης=Αριθμός αστοχιών×ποινή αστοχίας (14)

Κύκλοι καθυστέρησης μνήμης= IC× ΑστοχίεςΕντολή

×Ποινή αστοχίας (15)

Κύκλοι καθυστέρησης μνήμης=IC×προσπελάσεις μνήμηςεντολή

×ρυθμόαστοχίας×ποινήαστοχίας (16)

Τα πλεονεκτήματα του τελευταίου τύπου είναι ότι τα συστατικά στοιχεία μπορούν εύκολα να μετρηθούν. Είναι ήδη γνωστό πως μετράμε τον αριθμό. Το μέτρημα του αριθμού αναφορών μνήμης ανά εντολή μπορεί να γίνει με τον ίδιο τρόπο κάθε εντολή απαιτεί μια προσπέλασης εντολής και μπορούμε εύκολα να αποφασίσουμε αν απαιτεί επίσης και προσπέλαση δεδομένων.Αν και η ποινή αστοχίας έχει υπολογιστεί ως μέσος όρος παρακάτω χρησιμοποιήται σαν να ήταν σταθερή. Η μνήμη πίσω από την κρυφή μνήμη μπορεί να είναι απασχολημένη τη στιγμή της αστοχίας εξαιτίας απαιτήσεων της μνήμης που έγιναν νωρίτερα ή λόγω ανανέωσης της μνήμης. Ο αριθμός των κύκλων ρολογιού επίσης ποικίλλει στις διεπαφές ανάμεσα στα διαφορετικά ρολόγια του επεξεργαστή, διαύλου και μνήμης. Γι' αυτό μόνο ένας αριθμός για ποινή αστοχία είναι απλούστευση.Ο ρυθμός αστοχίας είναι ο λόγος του αριθμού προσπελάσεων που αστοχούν προς τον αριθμό των συνολικών προσπελάσεων. Οι ρυθμοί αστοχίας μπορούν να μετρηθούν με προσομοιωτές κρυφής μνήμης οι οποίοι παίρνουν ένα ίχνος διευθύνσεων και των αναφορών σε εντολές και δεδομένα, προσομοιώνουν την συμπεριφορά των κρυφών μνημών για να εντοπίσουν ποιες αναφορές ευστοχούν και ποιες αστοχούν και μετά αναφέρουν τα αθροίσματα των ευστοχιών και αστοχιών. Κάποιοι μικροεπεξεργαστές παρέχουν υλικό για να μετρούν τν αριθμό των αστοχιών και των αναφορών μνήμης, γεγονός το οποίο αποτελεί έναν πολύ γρηγορότερο και ευκολότερο τρόπο για τη μέτρηση του ρυθμού αστοχίας.Ο τύπος που που αναφέρθηκε πιο πάνω αποτελεί μια προσέγγιση από την στιγμή που οι ρυθμοί αστοχίας και οι ποινές αστοχίας συχνά διαφέρουν από τις αναγνώσεις και τις εγγραφές. Για αυτόν τον λόγο οι χρόνοι ρολογιού καθυστέρησης μνήμης μπορούν να οριστούν με δικούς τους όρους: τον αριθμό προσπελάσεων μνήμης ανά εντολή, την ποινή αστοχίας (σε κύκλους ρολογιού) για εγγραφές και αναγνώσεις και τον ρυθμό αστοχίας για εγγραφές και αναγνώσεις.

Κύκλοι ρολογιούκαθυστέρησης μνήμης

= IC×Αναγνώσειςανά εντολή

×Ποινήαστοχίαςαναγνώσεων

×Ποινήαστοχίαςαναγνώσεων (17)

IC× Εγγραφέςανάεντολές

×Ρυθμός αστοχίαςεγγραφών

×Ποινήαστοχίαςεγγραφών (18)

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

Κύκλοι ρολογιούκαθυστέρησης μνήμης

= IC×Προσπελάσεις μνήμηςΕντολή

×Ρυθμός αστοχίας×Ποινήαστοχίας (19)

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

ΑστοχίεςΕντολή

=

Ρυθμόςαστοχίας

×Προσπελάσεις μνήμης

Αριθμός εντολών= Ρυθμόςαστοχίας

×Προσπελάσεις μνήμηςΕντολή

(20)

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

Βασικές λειτουργίες κρυφών μνημών

Υπάρχουν τέσσερα βασικά ζητήματα που προκύπτουν με την ιεραρχία μνήμης και την χρήση των κρυφών μνημών.[MEM001] Αυτά είναι τα παρακάτω:

• Τοποθέτηση

• Αναγνώριση

• Αντικατάσταση

• Εγγραφή

Τοποθέτηση

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

• Αν κάθε μπλοκ έχει μόνο μια θέση που μπορεί να εμφανίζεται στην κρυφή μνήμη,η κρυφή μνήμη ονομάζεται άμεσα αντιστοιχισμένη (direct mapped). Αυτή η αντιστοίχηση ορίζεται ως εξής:

διεύθυνση του μπλοκ MOD αριθμός μπλοκ στην κρυφή μνήμη (21)

• Αν ένα μπλοκ μπορεί να τοποθετηθεί οπουδήποτε μέσα στην μνήμη, η κρυφή μνήμη ονομάζεται πλήρως συσχετιστική (fully associative).

18

• Αν ένα μπλοκ μπορεί να τοποθετηθεί σε περιορισμένο αριθμό θέσεων στην κρυφή μνήμη τότε η κρυφή μνήμη είναι συνολοσυσχετιστική (set associative). Ένα σύνολο είναι μια ομάδα από μπλοκ στην κρυφή μνήμη. Ένα block αρχικά αντιστοιχίζεται σε ένα σύνολο και μετά το μπλοκ μπορεί να τοποθετηθεί οπουδήποτε μέσα σε αυτό το σύνολο. Το σύνολο επιλέγεται όπως επιλέγεται η θέση στην περίπτωση direct mapped μνήμης:

διεύθυνση του μπλοκ MOD αριθμός συνόλων στην κρυφή μνήμη (22)

Αν υπάρχουν n μπλοκ σε ένα σύνολο, η τοποθέτηση στη κρυφή μνήμη ονομάζεται συνολοσυσχετιστική n δρόμων (n-way set- associative).

Το εύρος κρυφών μνημών από την άμεσα αντιστοιχισμένη μέχρι την πλήρως συσχετιστική είναι στην πραγματικότητα μια συνέχεια επιπέδων από συνολοσυσχετισμό. Η άμεσα αντιστοιχισμένη είναι μια συνολοσυσχετιστική ενός δρόμου και μια πλήρως συσχετιστική κρυφή μνήμη με m μπλοκ είναι μια συνολοσυσχετιστική m δρόμων. Ισοδύναμα η άμεσα αντιστοιχισμένη μπορεί να θεωρηθεί ότι έχει m σύνολα και η πλήρως συσχετιστική ότι έχει ένα σύνολο. Η τεράστια πλειοψηφία των κρυφών μνημών επεξεργαστών σήμερα είναι άμεσα αντιστοιχισμένες, συνολοσυσχετιστικές 2 ή 4 δρόμων.Για παράδειγμα όπως φαίνεται στο παραπάνω σχήμα, η διεύθυνση 13 στην περίπτωση της άμεσα συσχετισμένης μπορεί να τοποθετηθεί στο τέταρτο μπλοκ (13mod8 = 5 άρα στην 5η θέση δηλαδή στο #4 μπλοκ). Στη περίπτωση της συνολοσυσχετιστικής 4 δρόμων πρέπει να τοποθετηθεί στο πρώτο σετ (13mod4 = 1 άρα στο πρώτο σετ άρα στο #0 σετ.). Έτσι μπορεί να τοποθετηθεί είτε στο #0 είτε στο #1 μπλοκ. Στην πλήρως συσχετιστική μνήμη η διεύθυνση 13 μπορεί να τοποθετηθεί σε οποιοδήποτε μπλοκ της κρυφής μνήμης.Στο σχήμα φαίνεται και ο όρος tag (ετικέτα) ο οποίος θα εξηγηθεί αμέσως πιο κάτω στην

Σχήμα 1: Τοποθέτηση μπλοκ σε κρυφή μνήμη

αναγνώριση.

Αναγνώριση

Οι κρυφές μνήμες έχουν μια ετικέτα διεύθυνσης (tag) σε κάθε πλαίσιο μπλοκ η οποία δίνει την διεύθυνση του μπλοκ. Η ετικέτα κάθε μπλοκ κρυφής μνήμης που θα μπορούσε να περιέχει την επιθυμητή πληροφορία ελέγχεται για να δούμε αν ταιριάζει με την διεύθυνση του μπλοκ από την CPU. Ως κανόνας, όλες οι πιθανές ετικέτες ελέγχονται παράλληλα γιατί η ταχύτητα είναι σημαντική υπόθεση.Πρέπει να υπάρχει ένας τρόπος για να είναι δυνατόν να γνωστοποιήται αν ένα μπλοκ κρυφής μνήμης δεν έχει έγκυρες πληροφορίες. Η συνηθέστερη διαδικασία είναι να προστεθεί ένα bit εγκυρότητας στην ετικέτα το οποίο θα δείχνει αν μια εγγραφή περιέχει μαι έγκυρη διεύθυνση. Αν το bit δεν έχει τεθεί δεν μπορεί να υπάρχει ταίριασμα σε αυτήν την διεύθυνση.Έτσι μια διεύθυνση διαιρείται ως εξής: Η πρώτη υποδιαίρεση είναι ανάμεσα στην διεύθυνση του μπλοκ (hash) και και την απόκλιση του μπλοκ (offset). Η διεύθυνση του πλαισίου του μπλοκ μπορεί να διαιρεθεί περαιτέρω σε πεδίο ετικέτας (tag) και πεδίο δείκτη (index). Το πεδίο απόκλισης επιλέγει το επιθυμητό δεδομένο από το μπλοκ, το πεδίο δέικτη επιλέγει το σετ και το πεδίο της ετικέτας συγκρίνεται με αυτό για μια ευστοχία.Αν και η σύγκριση μπορούσε να γίνει περισσότερο πάνω στην διεύθυνση παρά πάνω στην ετικέτα, δεν υπάρχει τέτοια ανάγκη για τους παρακάτω λόγους:

• Η απόκλιση δεν θα έπρεπε να χρησιμοποιήται στη σύγκριση, αφού ολόκληρο το μπλοκ ή είναι παρόν ή όχι και συνεπώς όλες οι αποκλίσεις έχουν ως αποτέλεσμα ένα ταίριασμα εξ' ορισμού.

• Το να ελέγχουμε το δείκτη είναι πλεονάζον, αφού είχε χρησιμοποιηθεί για να επιλέξει το σύνολο που έπρεπε να ελεγχθεί. Μια διεύθυνση αποθηκευμένη στο σύνολο 0,για παράδειγμα, πρέπει να έχει 0 στο πεδίο δείκτη, αλλιώς δεν θα μπορούσε να αποθηκευθεί στο σύνολο 0. Αυτή η βελτιστοποίηση εξοικονομεί υλικό και ισχύ, μειώνοντας το πλάτος του μεγέθους μνήμης για την ετικέτα της κρυφής μνήμης.

Αν το συνολικό μέγεθος της κρυφής μνήμης έχει διατηρηθεί ίδιο, αυξάνοντας τη συσχετιστικότητα αυξάνεται και ο αριθμός των μπλοκ αν σύνολο και κατ' αυτόν τον τρόπο μειώνοντας το μέγεθος του δείκτη και αυξάνοντας το μέγεθος της ετικέτας. Κύριος σκοπός της ετικέτας όπως φαίνεται είναι να μοναδικοποιεί ένα μπλοκ δεδομένων μέσα σε ένα σύνολο. Τυπικά οι παραπάνω τιμές υπολογίζονται ως εξής:

διεύθυνση μπλοκ Hash=διεύθυνσηMOD μέγεθος cache (23)

20

Σχήμα 2: Τμήματα μιας διεύθυνσης μιας συνολοσυσχετιστικής κρυφής μνήμης

δείκτης index=hash/ μέγεθος ενός συνόλου σε δεδομένα (24)

ετικέτα tag =hashMOD μέγεθος συνόλου σε δεδομένα (25)

απόκλιση offset =διεύθυνσηMOD μέγεθος ενός μπλοκ σε δεδομένα (26)

Μέγεθος σε δεδομένα είναι το ελάχιστο ποσό δεδομένων που μπορούν να διαβαστούν. Έτσι, για παράδειγμα, αν το μέγεθος ενός μπλοκ είναι 16 bytes και το ελάχιστο ποσό δεδομένων είναι 4 bytes τότε το μέγεθος του μπλοκ σε δεδομένα είναι 4.

Αντικατάσταση

Το επόμενο σημαντικό ζήτημα είναι τι συμβαίνει όταν συμβεί μια αστοχία στην κρυφή μνήμη. Όταν συμβαίνει μια αστοχία, ο ελεγκτής της κρυφής μνήμης πρέπει να επιλέξει ένα μπλοκ να αντικατασταθεί με τα επιθυμητά δεδομένα. Ένα πλεονέκτημα της τοποθέτησης της άμεσης αντιστοίχισης είναι ότι οι επιλογές υλικού απλοποιούνται στην πραγματικότητα τόσο πολύ που δεν υπάρχει καμία επιλογή, μόνο ένα πλαίσιο του μπλοκ ελέγχεται για ευστοχία και μόνο αυτό το μπλοκ μπορεί να αντικατασταθεί. Με πλήρως συσχετιστική ή συνολοσυσχετιστική τοποθέτηση, υπάρχουν πολλά μπλοκ να επιλέξει κάνεις σε περίπτωση αστοχίας.Υπάρχουν τρεις βασικές στρατηγικές που χρησιμοποιούνται για την επιλογή του μπλοκ που αντικαθίστανται:

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

• Λιγότερο προσφάτως χρησιμοποιημένο (least-recently used, LRU). Για να μειωθεί η πιθανότητα να απαλειφθεί πληροφορία που θα είναι χρήσιμη σύντομα, οι προσπελάσεις στα μπλοκ καταγράφονται. Χρησιμοποιώντας στο παρελθόν γίνεται μια πρόβλεψη του μέλλοντος, το μπλοκ που αντικαθίσταται είναι αυτό που δεν έχει χρησιμοποιηθεί για το μεγαλύτερο χρονικό διάστημα. Το LRU στηρίζεται σε ένα συμπέρασμα της τοπικότητας: αν πρόσφατα χρησιμοποιημένα μπλοκ έιναι πιθανόν να χρησιμοποιηθούν πάλι, τότε ένας καλός υποψήφιος για απόρριψη είναι το λιγότερο προσφάτως χρησιμοποιημένο μπλοκ.

• Πρώτο μέσα, πρώτο έξω (First in First out, FIFO). Επειδή το LRU μπορεί να είναι περίπλοκο να το υπολογίσει κανείς, αυτό προσεγγίζει το LRU αποφασίζοντας το παλαιότερο μπλοκ αντί για το LRU.

• Όχι το πιο προσφάτως χρησιμοποιημένο (Not Most Recently Used, NMRU). Είναι μια λογική η οποία μοιάζει πολύ με την LRU αλλά είναι πιο δύσκολη σε υλοποίηση και δεν προτιμάτε.

Ένα πλεονέκτημα της τυχαίας αντικατάστασης είναι ότι είναι απλή να την υλοποιήσεις στο υλικό. Καθώς ο αριθμός των μπλοκ που παρακολουθούνται αυξάνεται το LRU γίνεται βαθμιαία ακριβό και συχνά μόνο προσεγγίζεται.

Εγγραφή

Οι αναγνώσεις κυριαρχούν στις προσπελάσεις κρυφής μνήμης του επεξεργαστή. Όλες οι προσπελάσεις εντολών είναι αναγνώσεις και οι περισσότερες εντολές δεν γράφουν στη μνήμη. Από την κυκλοφορία δεδομένων κρυφής μνήμης, οι εγγραφές είναι 10%. Για να επιταχυνθεί η συνηθισμένη περίπτωση πρέπει να βελτιστοποιηθούν οι αναγνώσεις, ειδικά επειδή οι επεξεργαστές παραδοσιακά περιμένουν τις αναγνώσεις να ολοκληρωθούν ενώ δεν χρειάζεται να περιμένουν για την ολοκλήρωση μιας εγγραφής.Ευτυχώς η συνηθισμένη περίπτωση είναι επίσης και η εύκολη περίπτωση για να επιταχυνθεί. Το μπλοκ μπορεί να διαβαστεί από την κρυφή μνήμη την ίδια στιγμή που διαβάζεται και συγκρίνεται η ετικέτα, έτσι η ανάγνωση του μπλοκ ξεκινά αμέσως μόλις η διεύθυνση του μπλοκ είναι διαθέσιμη. Αν η ανάγνωση είναι εύστοχη, το αιτούμενο κομμάτι του μπλοκ περνιέται στην CPU αμέσως. Εάν είναι μια αστοχία δεν υπάρχει όφελος αλλά ούτε ζημιά στους επεξεργαστές. Απλώς αγνοούν την τιμή που αναγνώστηκε.Θα ήταν επιθυμητό σε μια ανάγνωση για να αποφευχθεί περιττή δουλειά να έχουν διαχωριστεί τα δεδομένα ανάγνωσης από τον έλεγχο της διεύθυνσης, έτσι ώστε τα δεδομένα να μην διαβάζονται σε μια αστοχία. Αυτό δεν είναι δυνατόν σε μια εγγραφή. Η αλλαγή σε ένα μπλοκ δεν μπορεί να ξεκινήσει αν δεν ελεγχθεί η ετικέτα για να δούμε αν η διεύθυνση είναι εύστοχη. Επειδή ο έλεγχος της ετικέτας δεν μπορεί να συμβεί παράλληλα, οι εγγραφές φυσιολογικά παίρνουν περισσότερο χρόνο από ότι οι αναγνώσεις. Μια άλλη επιπλοκή είναι ότι ο επεξεργαστής επίσης προσδιορίζει το μέγεθος της εγγραφής συνήθως ανάμεσα σε 1 και 8 bytes, μόνο αυτό το τμήμα του μπλοκ μπορεί να αλλαχθεί. Αντιθέτως οι αναγνώσεις μπορούν να προσπελάσουν περισσότερα bytes από ότι είναι απαραίτητο άφοβα. Οι πολιτικές εγγραφών συχνά διακρίνουν τα σχέδια κρυφών μνημών. Υπάρχουν δυο βασικές επιλογές όταν γράφεις στην κρυφή μνήμη:

• Διεγγραφή (write through). Η πληροφορία γράφεται στο μπλοκ και στην κρυφή μνήμη και στο μπλοκ στην μνήμη χαμηλότερου επιπέδου.

• Ετεροχρονισμένη εγγραφή (write back). Η πληροφορία γράφεται μόνο στο μπλοκ στην κρυφή μνήμη. Το αλλαγμένο μπλοκ κρυφής μνήμης γράφεται στην κύρια μνήμη μόνο όταν αντικαθίσταται.

Για να μειωθεί τη συχνότητα των ετεροχρονισμένα εγγεγραμμένων μπλοκ στην αντικατάσταση, ένα χαρακτηριστικό που ονομάζεται bit αλλοίωσης χρησιμοποιήται συχνά. Αυτό το bit κατάστασης υποδεικνύει αν το μπλοκ είναι αλλοιωμένο (αλλαγμένο ενώ είναι στην κρυφή μνήμη) ή καθαρό (μη αλλαγμένο). Αν είναι καθαρό, το μπλοκ δεν εγγράφεται ετεροχρονισμένα στην περίπτωση αστοχίας αφού η ίδια πληροφορία με αυτή της κρυφής μνήμης βρίσκεται σε χαμηλότερα επίπεδα.Τόσο το write back όσο το write through έχουν τα δικά τους πλεονεκτήματα το καθένα. Με το write back, οι εγγραφές συμβαίνουν με την ταχύτητα της κρυφής μνήμης και πολλαπλές εγγραφές μέσα σε ένα μπλοκ απαιτούν μόνο μία εγγραφή στη μνήμη χαμηλότερου επιπέδου. Αφού κάποιες εγγραφές δεν πηγαίνουν στην μνήμη, το write back χρησιμοποιεί λιγότερο εύρος ζώνης μνήμης κάνοντας το ελκυστικό για πολυεπεξεργαστές που χρησιμοποιούνται σε εξυπηρετητές. Αφού το write back χρησιμοποιεί το υπόλοιπο της ιεραρχίας μνήμης και τους διαύλους μνήμης λιγότερο από το write through εξοικονομεί επίσης ισχύ όντας πιο ελκυστικό σε ενσωματωμένες εφαρμογές.Το write through είναι ευκολότερο να υλοποιηθεί από ότι το write back. Η κρυφή μνήμη

22

είναι πάντα καθαρή, οπότε αντίθετα με τις αστοχίες ανάγνωσης στην περίπτωση του write back ποτέ δεν έχουν ως αποτέλεσμα εγγραφές στο χαμηλότερο επίπεδο. Το write through έχει επίσης το πλεονέκτημα ότι το αμέσως επόμενο χαμηλότερο επίπεδο έχει το πιο πρόσφατο αντίγραφο των δεδομένων, το οποίο απλοποιεί την συμβατότητα των δεδομένων. Η συμβατότητα των δεδομένων είναι σημαντική για τους πολυεπεξεργαστές που χρησιμοποιούν κοινές κρυφές μνήμες.Όταν η CPU πρέπει να περιμένει να ολοκληρωθούν οι εγγραφές στη διάρκεια του write through, η CPU λέγεται ότι είναι σε καθυστέρηση εγγραφής. Μια συνηθισμένη βελτιστοποίηση για να μειώσει κανείς τις καθυστερήσεις εγγραφών είναι μια μνήμη εγγραφών η οποία επιτρέπει στον επεξεργαστή αμέσως μόλις τα δεδομένα έχουν εγγραφή στην μνήμη επιταχύνοντας έτσι παραλληλισμό στην εκτέλεση του επεξεργαστή με την ενημέρωση μνήμης. Καθώς τα δεδομένα δεν χρειάζονται σε μια εγγραφή, υπάρχουν δυο επιλογές σε μια αστοχία εγγραφής:

• Κατανομής εγγραφής (write allocate). Το μπλοκ κατανέμεται σε μια αστοχία εγγραφής ακολουθούμενο από τις παραπάνω ενέργειες εύστοχης εγγραφής. Σε αυτή τη φυσική επιλογή, οι αστοχίες εγγραφών λειτουργούν ως αστοχίες αναγνώσεων.

• Κατανομή μη εγγραφής (no-write allocate). Αυτή η προφανώς ασυνήθιστη εναλλακτική λύση είναι ότι οι αστοχίες εγγραφής δεν επηρεάζουν την κρυφή μνήμη. Αντιθέτως, το μπλοκ αλλάζει μόνο στην μνήμη χαμηλότερου επιπέδου.

Έτσι τα μπλοκ μένουν έξω από την κρυφή μνήμη στην κατανομή μη εγγραφής μέχρι το πρόγραμμα να προσπαθήσει να διαβάσει τα μπλοκ αλλά ακόμα και τα μπλοκ που είναι μόνο γραμμένα θα είναι ακόμα στην κρυφή μνήμη με την κατανομή εγγραφής.Η πολιτική της αστοχίας εγγραφής θα μπορούσε να χρησιμοποιηθεί είτε με write through είτε με write back. Φυσιολογικά οι κρυφές μνήμες χρησιμοποιούν write back και κατανομή εγγραφής, ελπίζοντας ότι οι επακόλουθες εγγραφές στο ίδιο μπλοκ θα συλληφθούν από την κρυφή μνήμη. Οι κρυφές μνήμες που χρησιμοποιούν write through συνήθως χρησιμοποιούν κατανομή μη εγγραφής. Η λογική σε αυτή την τακτική είναι ότι ακόμα κι αν υπάρχουν ακόλουθες εγγραφές στο ίδιο μπλοκ εφόσον οι εγγραφές σε αυτό το μπλοκ πρέπει να πάνε σε ακόμα χαμηλότερο επίπεδο δεν υπάρχει ουσιαστικό κέρδος.

Απόδοση κρυφής μνήμης

Όπως ήδη αναφέρθηκε η εκτίμηση της απόδοσης μιας CPU μόνο από τον αριθμό των εντολών είναι λάθος. Αντίστοιχα η εκτίμηση μιας ιεραρχίας μνήμης από τον ρυθμό αστοχίας είναι επίσης λάθος γιατί όπως ο αριθμός των εντολών είναι ανεξάρτητος από την ταχύτητα του υλικού. Μια καλύτερη μέτρηση της απόδοσης της μνήμης είναι ο μέσος χρόνος προσπέλασης της μνήμης:

Μέσος χρόνος προσπέλασηςτης μνήμης

= χρόνοςευστοχίας

ρυθμόςαστοχίας

× ποινήαστοχίας (27)

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

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

Ο μέσος όρος προσπέλασης μνήμης προβλέπει την απόδοση του επεξεργαστή. Επειδή βέβαια υπάρχουν κι άλλοι λόγοι για παύση του επεξεργαστή πέρα από την μνήμη όπως οι διακοπές και συγκρούσεις που οφείλονται σε συσκευές εισόδου εξόδου που χρησιμοποιούν μνήμη. Στα παρακάτω θεωρούμε ότι δεν υπάρχουν παύσεις πέρα από αυτές που οφείλονται στην μνήμη αν και αυτό είναι απλοποίηση. Επίσης είναι σαφές ότι η απόδοση του επεξεργαστή εξαρτάται από την CPU. Αν θεωρήσουμε μια CPU η οποία δουλεύει εν σειρά τότε πράγματι ο μέσος όρος προσπέλασης μνήμης μπορεί να προβλέψει την απόδοση του επεξεργαστή. Οι παύσεις της CPU στη διάρκεια των αστοχιών, και ο χρόνος συστήματος της μνήμης είναι ισχυρώς συσχετισμένα με το μέσο χρόνο προσπέλασης μνήμης. Με βάση τις δυο παραπάνω παραδοχές λοιπόν μπορούμε να υπολογίσουμε τον χρόνο της CPU ως εξής:

ΧρόνοςCPU=Κύκλοι ρολογιούεκτέλεσης CPUΚύκλοι ρολογιού

παύσης ×Χρόνος κύκλουρολογιού (28)

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

Κύκλοισταματήματος μνήμηςεντολή

=αστοχίεςΕντολή

× καθυστέρησησυνολικήςαστοχίας

− μηεπικαλυπτόμενηκαθυστέρησηαστοχίας (29)

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

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

• Μέγεθος της επικαλυπτόμενης καθυστέρησης. Ποια είναι η αρχή της επικάλυψης με τον επεξεργαστή ή πότε μια λειτουργία μνήμης σταματάει τον επεξεργαστή.

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

24

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

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

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

Μέσος χρόνος προσπέλασηςτης μνήμης

= χρόνοςευστοχίας

ρυθμόςαστχίας

× ποινήαστοχίας (30)

Έτσι οι βελτιστοποιήσεις τις κρυφής μνήμης οργανώνονται σε τέσσερις κατηγορίες:

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

• Μειώνοντας το ρυθμό αστοχίας: μεγαλύτερο μέγεθος μπλοκ, μεγαλύτερο μέγεθος κρυφής μνήμης, υψηλότερη συσχετιστικότητα πρόβλεψη δρόμων και ψευδοσυσχετιστικότητα και βελτιώσεις μεταφραστών (compilers).

• Μειώνοντας την ποινή ή το ρυθμό αστοχίας μέσω παραλληλισμού: κρυφές μνήμες που δεν μπλοκάρουν προ-λήψη υλικού και προ-λήψη compiler.

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

Μείωση ποινής αστοχίας κρυφής μνήμης

Πολυεπίπεδες κρυφές μνήμες:Πολλές τεχνικές για να μειωθεί η ποινή αστοχίας επηρεάζουν την CPU. Αυτή η τεχνική αγνοεί την CPU, εστιάζοντας στη διεπαφή ανάμεσα στην κρυφή μνήμη και στην κύρια μνήμη.Το κενό απόδοσης ανάμεσα στους επεξεργαστές και τις μνήμες οδηγεί στην ανάγκη τόσο μεγαλύτερων όσο και γρηγορότερων κρυφών μνημών. Προσθέτοντας άλλο ένα επίπεδο κρυφής μνήμης ανάμεσα στην αρχική κρυφή μνήμη και την μνήμη συνδυάζεται τόσο η ταχύτητα της μικρής κρυφής μνήμης που συμβαδίζει με την ταχύτητα του επεξεργαστή τόσο και η χωρητικότητα της δεύτερης κρυφής μνήμης που ελαττώνει τις αστοχίες μνήμης που θα πηγαίναν στην κύρια μνήμη, μειώνοντας έτσι την αποτελεσματική ποινή αστοχίας.Η απόδοση επηρεάζεται ως εξής: (L1 και L2 τα δυο επίπεδα κρυφής μνήμης)

Μέσος χρόνοςπροσπέλασης μνήμης

= χρόνοςευστοχίας L1

ρυθμόςαστοχίας L1

× ποινήαστοχίας L1

(31)

όπου

ποινήαστοχίας L1

= χρόνοςευστοχίας L2

ρυθμόςαστοχίας L2

× ποινήαστοχίας L2

(32)

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

• Τοπικός αριθμός αστοχίας: Αυτός ο ρυθμός είναι απλά ο αριθμός αστοχιών σε μια κρυφή μνήμη διηρημένη δια του συνολικού των προσπελάσεων μνήμης σε αυτήν την κρυφή μνήμη. Δηλαδή για τις L1 και L2 αντίστοιχα οι:

ρυθμόςαστοχίας L1

(33)

ρυθμόςαστοχίας L2

(34)

• Συνολικός ρυθμός αστοχίας: Ο αριθμός των αστοχιών στην κρυφή μνήμη διηρημένος με το συνολικό αριθμό προσπελάσεων μνήμης που παράχθηκαν από την CPU. Χρησιμοποιώντας τους πιο πάνω όρους, ο συνολικός ρυθμός αστοχίας για το πρώτο

επίπεδο κρυφής μνήμης είναι ορυθμός

αστοχίας L1, αλλά για την κρυφή μνήμη L2 είναι ο:

ρυθμόςαστοχίας L1

× ρυθμόςαστοχίας L2

(35)

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

Μεσος αριθμόςσυστημάτων ανά εντολή

= αστοχίεςανά εντολήL1

× χρόνοςευστοχίας L2

αστοχίεςανά εντολήL2

× ποινήαστοχίας L2

(36)

Κρίσιμη λέξη πρώτα και γρήγορη επανεκκίνηση:Μια άλλη προσέγγιση για την μείωση του ρυθμού αστοχίας που σε αντίθεση με την προσθήκη επιπλέον επιπέδων κρυφής μνήμης δεν απαιτεί επιπρόσθετο υλικό είναι η κρίσιμη λέξη πρώτα και η γρήγορη επανεκκίνηση. Αυτή βασίζεται στην παρατήρηση ότι η CPU κανονικά χρειάζεται μόνο μια λέξη από το μπλοκ τη φορά. Έτσι δεν χρειάζεται να περιμένουμε όλο το μπλοκ να φορτωθεί πριν σταλθεί η απαιτούμενη λέξη και επανεκκινηθεί η CPU. Έτσι έχουμε δυο τακτικές:

26

• Η κρίσιμη πρώτη λέξη: Η απαιτούμενη – κρίσιμη λέξη ζητιέται πρώτη από την μνήμη και αποστέλνεται στην CPU αμέσως μόλις φτάσει. Η CPU συνεχίζει την εκτέλεση ενω μεταφέρονται οι υπόλοιπες λέξεις στο μπλοκ. Η ανάκληση της κρίσιμης λέξης πρώτα ονομάζεται επίσης περιβαλλόμενη ανάκλαση και απαιτούμενη πρώτη λέξη.

• Γρήγορη επανεκκίνηση. Ανακαλούνται οι λέξεις με την κανονική σειρά αλλά μόλις η απαιτουμένη λέξη του μπλοκ φτάσει αποστέλλεται στην CPU και η CPU συνεχίζει την εκτέλεση.

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

Προτεραιότητα στις αστοχίες ανάγνωσης έναντι εγγραφής:Αυτή η βελτιστοποίηση εξυπηρετεί αναγνώσεις πριν να ολοκληρωθούν εγγραφές. Με μια κρυφή μνήμη write through η σπουδαιότερη βελτίωση είναι ένας προσωρινός χώρος αποθήκευσης εγγραφής. Οι προσωρινοί χώροι αποθήκευσης εγγραφής, ωστόσο περιπλέκουν τις προσπελάσεις μνήμης διότι θα μπορούσαν να κρατήσουν ενημερωμένη την τιμή μιας θέσης που χρειάζεται σε μια αστοχία ανάγνωσης. Ο απλούστερος τρόπος αντιμετώπισης της περίπτωσης αυτής είναι να περιμένει η αστοχία ανάγνωσης μέχρι να αδειάσει η προσωρινός χώρος αποθήκευσης εγγραφής. Εναλλακτική λύση είναι να ελεγχθούν τα περιεχόμενα του προσωρινού χώρου αποθήκευσης εγγραφής σε μια αστοχία ανάγνωσης και αν δεν υπάρχουν συγκρούσεις και το σύστημα μνήμης είναι διαθέσιμο η αστοχία ανάγνωσης να συνεχίσει. Έτσι οι αναγνώσεις έχουν προτεραιότητα.Το κόστος των εγγραφών στην περίπτωση του write back μπορεί επίσης να μειωθεί. Σε περίπτωση αστοχίας ανάγνωσης και αντικατάστασης ενός αλλοιωμένου μπλοκ, το αλλοιωμένο μπλοκ αντί να αντιγραφεί στην μνήμη μπορεί να γραφτεί σε έναν προσωρινό χώρο αποθήκευσης, να γίνει η ανάγνωση και μετά να γίνει η ολοκλήρωση της εγγραφής βελτιώνοντας την ποινή αστοχίας. Με όμοιο τρόπο με το write through, αν υπάρχουν ήδη δεδομένα στον προσωρινό χώρο αποθήκευσης, ο επεξεργαστής θα είτε θα ελέγξει αν τα δεδομένα αυτά συγκρούονται με την αστοχία ανάγνωσης, είτε θα περιμένει να αδειάσει ο προσωρινός χώρος αποθήκευσης και μετά θα προχωρήσει σε αστοχία ανάγνωσης.

Κρυφές μνήμες θυμάτων:Ένας ακόμη τρόπος μείωσης της ποινής αστοχίας είναι να να θυμάται ο επεξεργαστής τι είχε απορριφθεί, σε περίπτωση που χρειαστεί πάλι. Από τη στιγμή που τα απορριφθέντα δεδομένα έχουν ήδη ανακληθεί, μπορούν να χρησιμοποιηθούν ξανά με μικρό κόστος. Τέτοια ανακύκλωση απαιτεί μια μικρή, πλήρως συσχετιστική κρυφή μνήμη ανάμεσα σε μια κρυφή μνήμη και το μονοπάτι επαναπλήρωσης της. Η κρυφή μνήμη θύματος περιλαμβάνει μόνο μπλοκ που απορρίφθηκαν από μια κρυφή μνήμη λόγω μιας αστοχίας και ελέγχονται σε μια αστοχία αν έχουν τα επιθυμητά δεδομένα πριν χρειαστεί ανάκλησή (και μεγαλύτερη ποινή από χαμηλότερο επίπεδο μνήμης.

Μείωση ρυθμού αστοχίας

Οι αστοχίες μνήμης χωρίζονται σε τρεις κατηγορίες:

• Υποχρεωτικές (compulsory misses). Η πρώτη προσπέλαση σε ένα μπλοκ δεν μπορεί να είναι στην κρυφή μνήμη. Αυτές επίσης λέγονται και αστοχίες κρύας έναρξης ή αστοχίες πρώτης αναφοράς. Δεν μπορούν να αποφευχθούν και συμβαίνουν όταν τα δεδομένα έρχονται πρώτη φορά στην μνήμη.

• Χωρητικότητα (capacity misses). Αν η κρυφή μνήμη δεν μπορεί να περιλαμβάνει όλα τα μπλοκ που χρειάζονται κατά την διάρκεια της εκτέλεσης ενός προγράμματος, αστοχίες χωρητικότητας συμβαίνουν εξαιτίας των μπλοκ που απορρίφθηκαν και αργότερα επανακτήθηκαν λόγω ανεπάρκειας του μεγέθους της μνήμης. Όπως και οι υποχρεωτικές δεν μπορούν ν αποφευχθούν εκτός αν αυξηθεί το μέγεθος της μνήμης.

• Σύγκρουση (conflict misses). Αν η στρατηγική τοποθέτησης του μπλοκ είναι συνολοσυσχετιστική ή άμεσα αντιστοιχισμένη, οι αστοχίες σύγκρουσης θα συμβούν διότι ένα μπλοκ μπορεί να απορριφθεί και αργότερα να επανακτηθεί αν υπερβολικά πολλά μπλοκ αντιστοιχούν στο σύνολο του. Οι αστοχίες αυτές μπορούν να αποφευχθούν αν κάποιο χρήσιμο block δεν απομακρυνθεί από την μνήμη σε προηγούμενη αστοχία.

Αυτές οι αστοχίες λέγονται επίσης αστοχίες σύγκρουσης ή αστοχίες παρεμβολής. Η ιδέα είναι ότι ευστοχίες σε μια πλήρως συνολοσυσχετιστική κρυφή μνήμη που έχουν γίνει αστοχίες σε μια συνολοσυσχετιστική μνήμη ν- δρόμων, οφείλονται σε περισσότερες από ν αιτήσεις σε κάποια δημοφιλή σύνολα.Γενικά οι υποχρεωτικές αστοχίες δεν γίνεται να μειωθούν, πέρα από την αύξηση του μεγέθους του μπλοκ, αφού θα συμβούν ούτως ή άλλως – είναι υποχρεωτικές. Όμως, είναι πολύ λίγες σε αριθμό σε σχέση με τις υπόλοιπες.Οι αστοχίες χωρητικότητας πάλι μπορούν να γίνουν λιγότερες από την μεγέθυνση της κρυφής μνήμης. Αν η μνήμη ανωτέρου επιπέδου είναι πολύ μικρότερη από ότι χρειάζεται για ένα πρόγραμμα και σημαντικό ποσοστό του χρόνου ξοδεύεται για την μετακίνηση δεδομένων ανάμεσα σε δυο επίπεδα στην ιεραρχία, τότε λέγεται ότι η ιεραρχία της μνήμης καταβάλλεται. Επειδή απαιτούνται πολλές αντικαταστάσεις καταβολή σημαίνει ότι ο υπολογιστής δουλεύει κοντά στην ταχύτητα της μνήμης χαμηλότερου επιπέδου ή και πιο αργά λόγο του κόστους αστοχίας.Παρακάτω παρουσιάζονται κάποιες τεχνικές μείωσης του ρυθμού αστοχίας:

Μεγαλύτερο μέγεθος μπλοκ:Ο πιο απλός τρόπος μείωσης του ρυθμού αστοχίας είναι η αύξηση του μεγέθους του μπλοκ. Μεγαλύτερα μεγέθη μπλοκ μειώνουν τις υποχρεωτικές αστοχίες. Αυτή η μείωση συμβαίνει εξαιτίας της αρχής της τοπικότητας και συγκεκριμένα της χωρικής τοπικότητας.Παράλληλα όμως τα μεγαλύτερα μπλοκ αυξάνουν την ποινή αστοχίας. Αφού μειώνουν τον αριθμό των μπλοκ στην κρυφή μνήμη, τα μεγαλύτερα μπλοκ μπορεί να αυξήσουν τις αστοχίες σύγκρουσης και ακόμη τις αστοχίες χωρητικότητας, αν η κρυφή μνήμη είναι μικρή. Σαφώς δεν υπάρχει λόγος να αυξηθεί το μέγεθος του μπλοκ σε τέτοιο μέγεθος που να αυξήσει το ρυθμό αστοχίας. Επίσης δεν υπάρχει όφελος στη μείωση του ρυθμού αστοχίας, αν αυτό αυξάνει το μέσο χρόνο προσπέλασης της μνήμης. Η αύξηση στην ποινή αστοχίας μπορεί να υπερτερεί της μείωσης του ρυθμού αστοχίας.Η επιλογή του μεγέθους του μπλοκ εξαρτάται από την καθυστέρηση, καθώς και από το εύρος ζώνης της μνήμης χαμηλότερου επιπέδου. Υψηλή καθυστέρηση και υψηλό εύρος ζώνης ενθαρρύνουν τα μεγάλα μεγέθη μπλοκ αφού η κρυφή μνήμη παίρνει πολύ περισσότερα bytes ανά αστοχία για μικρή αύξηση στην ποινή αστοχίας. Αντιθέτως χαμηλή

28

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

Μεγαλύτερες κρυφές μνήμες:Ένας προφανής τρόπος για να μειωθούν οι αστοχίες χωρητικότητας είναι να αυξηθεί η χωρητικότητα της κρυφής μνήμης. Το εξίσου προφανές μειονέκτημα είναι ο μεγαλύτερος χρόνος ευστοχίας και το υψηλό κόστος. Αυτή η τεχνική έχει υπάρξει ιδιαιτέρως δημοφιλής στις κρυφές μνήμες εκτός τσιπ επεξεργαστή.

Υψηλότερη συσχετιστικότητα:Στο θέμα αυτό έχουν εξαχθεί δυο πρακτικά συμπεράσματα. Πρώτον ότι η συνολοσυσχετιστική 8 δρόμων είναι τόσο αποτελεσματική στη μείωση αστοχιών όσο η πλήρως συσχετιστική. Δεύτερη παρατήρηση είναι ο 2:1 πρακτικός κανόνας κρυφής μνήμης, που λέει ότι η μια άμεσα αντιστοιχισμένη κρυφή μνήμη μεγέθους Ν έχει περίπου το ίδιο ρυθμό αστοχίας όπως μια συνολοσυσχετιστική κρυφή μνήμη 2 δρόμων μεγέθους Ν/2.

Πρόβλεψη δρόμων και ψευδοσυσχετιστικές κρυφές μνήμες:Μια άλλη προσέγγιση μειώνει τις αστοχίες σύγκρουσης και παρόλα αυτά διατηρεί την ταχύτητα ευστοχίας της άμεσα αντιστοιχισμένης κρυφής μνήμης. Στην πρόβλεψη δρόμων, επιπλέον bits διατηρούνται στην κρυφή μνήμη για να προβλέψουν τους δρόμους ή μπλοκ μέσα στο σύνολο για την επόμενη προσπέλαση κρυφής μνήμης. Αυτή η πρόβλεψη σημαίνει ότι ο πολυπλέκτης ξεκινάει νωρίς να επιλέξει το επιθυμητό μπλοκ και μόνο μια μονής ετικέτας σύγκριση πραγματοποιείται σε αυτόν τον κύκλο ρολογιού. Μια αστοχία έχει ως αποτέλεσμα να ελέγξουμε τα άλλα μπλοκ για ταίριασμα στους ακόλουθους κύκλους ρολογιού. Πέρα από την βελτίωση της απόδοσης, η πρόβλεψη δρόμου μειώνει την απαιτούμενη ισχύ, κάτι πολύ σημαντικό για ενσωματωμένες εφαρμογές.Μια σχετική προσέγγιση ονομάζεται ψευδοσυσχετιστική ή συσχετιστική στήλη. Οι προσπελάσεις προχωρούν ακριβώς όπως σε μια κρυφή μνήμη άμεσα αντιστοιχισμένη για μια ευστοχία. Σε μια αστοχία όμως πριν περάσει σε ένα χαμηλότερο επίπεδο της ιεραρχίας μνήμης, μια δεύτερη εγγραφή κρυφής μνήμης ελέγχεται αν ταιριάζει εκεί. Ένας απλός τρόπος είναι να αντιστραφεί το πιο σημαντικό bit του δείκτη για να βρούμε το άλλο μπλοκ στο ψευδοσύνολο.Οι ψευδοσυσχετιστικές κρυφές μνήμες τότε έχουν έναν γρήγορο και έναν αργό χρόνο ευστοχίας, μια κανονική ευστοχία και μια ψευδοευστοχία. Ένας κίνδυνος σε αυτήν την βελτιστοποίηση είναι αν πολλοί γρήγοροι χρόνοι ευστοχίας κρυφής μνήμης άμεσα αντιστοιχισμένης γίνονται αργοί χρόνοι ευστοχίας σε ψευδοσυσχετιστική κρυφή μνήμη. Η απόδοση τότε θα μειωνόταν από αυτήν τη βελτιστοποίηση. Άλλος ένας κίνδυνος είναι ότι η ποινή αστοχίας μπορεί να γίνει ελαφρώς μεγαλύτερη σε διάρκεια, προσθέτοντας τον χρόνο να ελεγχθεί μια άλλη εγγραφή της κρυφής μνήμης.

Βελτιστοποιήσεις compiler:Αυτή η βελτιστοποίηση είναι η μοναδική που δεν επιφέρει αλλαγές στο υλικό. Η βελτιστοποίηση αυτή γίνεται από την επανατακτοποίηση του κώδικα χωρίς να επηρεάζεται η ορθότητα.[MEM005] Βελτιστοποιήσεις τέτοιου είδους συμπεριλαμβάνουν την ευθυγράμμιση βασικών μπλοκ κρυφής μνήμης που μειώνει την πιθανότητα αστοχίας για

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

• Αναδιοργάνωση των procedures στην μνήμη για την μείωση των αστοχιών σύγκρουσης.[ΜΕΜ002]

• Merging Arrays: βελτίωση της spatial locality με έναν πίνακα δεδομένων αντί για δυο πίνακες.

• Loop interchange: Αλλαγή της σειράς φωλιάσματος των βρόχων για να προσπελαύνουμε τα δεδομένα με την ίδια σειρά όπως αποθηκεύονται στην μνήμη.[MEM003]

• Loop fusion: Συνδυασμός 2 ή περισσότερων ανεξάρτητων βρόχων που περιέχουν τους ίδιους βρόχους και κάποιες κοινές μεταβλητές.

• Blocking: Βελτίωση του temporal locality προσπελαύνοντας ένα τμήμα μόνο των δεδομένων επαναληπτικά αντί να διατρέχουμε ολόκληρες τις γραμμές ή τις στήλες.

Μειώνοντας την ποινή ή το ρυθμό αστοχίας μέσω παραλληλισμού

Μη μπλοκαρισμένες κρυφές μνήμες ( Non – blocking caches ή lockup – free caches):Αυτή η τεχνική μπορεί να υλοποιηθεί μόνο σε επεξεργαστές εκτός σειράς. Σε αυτούς τους επεξεργαστές η CPU δεν χρειάζεται να σταματήσει στην περίπτωση αστοχίας μνήμης. Μια μη μπλοκαρισμένη κρυφή μνήμη ή κρυφή μνήμη ελεύθερη κλειδώματος αυξάνει τα δυνητικά οφέλη ενός τέτοιου σχήματος επιτρέποντας στην κρυφή μνήμη δεδομένων να παρέχει ευστοχίες κρυφής μνήμης στην διάρκεια μιας αστοχίας. Αυτή η “ευστοχία υπό αστοχία” βελτιστοποίηση μειώνει την αποτελεσματική ποινή αστοχίας με το να είναι επιβοηθητική στην διάρκεια μιας αστοχίας αντί να αγνοεί της απαιτήσεις της CPU. Μια μικρή και περίπλοκη επιλογή είναι ότι η κρυφή μνήμη μπορεί να χαμηλώσει περαιτέρω την αποτελεσματική ποινή αστοχίας αν μπορέσει να επικαλύψει τις πολλαπλές αστοχίες, μια βελτιστοποίηση “ευστοχία υπό πολλαπλή αστοχία” ή “αστοχία υπό αστοχία”.Προανάκληση (prefetch) των εντολών και δεδομένων σε υλικό:Οι μη μπλολαρισμένες κρυφές μνήμες μειώνουν αποτελεσματικά την ποινή αστοχίας επικαλύπτοντας εκτέλεση με προσπέλαση μνήμης. Όμως για να έχει αξία χρειάζεται ένας επεξεργαστής με δυνατότητα εκτέλεσης εντολών εκτός σειράς. Μια άλλη προσέγγιση είναι να προανακαλεί στοιχεία πριν ζητηθούν από τον επεξεργαστή. Τόσο οι εντολές όσο και τα δεδομένα μπορούν να προανακληθούν είτε απευθείας μέσα στις κρυφές μνήμες είτε μέσα σε έναν εξωτερικό προσωρινό χώρο αποθήκευσης που μπορούν να προσπελαστούν γρηγορότερα από ότι η κύρια μνήμη.Η προανάκληση εντολών συχνά γίνεται σε υλικό έξω από τη κρυφή μνήμη. Τυπικά, ο επεξεργαστής ανακαλεί δυο μπλοκ σε περίπτωση αστοχίας: το απαιτούμενο μπλοκ και το αμέσως επόμενο μπλοκ. Το απαιτούμενο μπλοκ τοποθετήται στην κρυφή μνήμη εντολών όταν αυτό επιστρέφει και το προανακαλούμενο μπλοκ τοποθετήται μέσα στο προσωρινό χώρο αποθήκευσης ροής εντολών. Αν το απαιτούμενο μπλοκ είναι παρών στο προσωρινό χώρο αποθήκευσης ροής, η αρχική αίτηση κρυφής μνήμης ακυρώνεται, το μπλοκ

30

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

Προανάκληση ελεγχόμενη από compiler:Μια εναλλακτική λύση στη προανάκληση με υλικό είναι να εισάγει ο μεταφραστής εντολές προανάκλησης για να απαιτεί τα δεδομένα πριν αυτά χρειαστούν. Υπάρχουν δυο είδη προανάκλησης:

• Η προανάκληση καταχωρητή θα φορτώσει την τιμή σε έναν καταχωρητή

• Η προανάκληση κρυφής μνήμης φορτώνει δεδομένα μόνο στην κρυφή μνήμη και όχι στον καταχωρητή.

Οποιαδήποτε από τις δυο μπορεί να είναι σφάλλουσα ή μη σφάλλουσα. Αυτό σημαίνει ότι η διεύθυνση προκαλεί ή όχι μια εξαίρεση για σφάλματα εικονικών διευθύνσεων και παραβιάσεις προστασίας. Οι σφάλλουσες προανακλήσεις απλά μετατρέπονται σε μηδενική λειτουργία αν θα κατέληγαν κανονικά σε εξαίρεση.Η πιο αποτελεσματική προανάκληση είναι σημαντικά αόρατη σε ένα πρόγραμμα. Δεν αλλάζει τα περιεχόμενα των καταχωρητών και της μνήμης και δεν μπορεί να προκαλέσει σφάλματα εικονικής μνήμης. Η προανάκληση έχει νόημα μόνο αν ο επεξεργαστής μπορεί να προχωρήσει ενώ τα προανακαλούμενα δεδομένα ανακαλούνται. Αυτό σημαίνει ότι οι κρυφές μνήμες δεν σταματούν αλλά συνεχίζουν να παρέχουν εντολές και δεδομένα ενώ περιμένουν να επιστρέψουν τα προανακαλούμενα δεδομένα. Για αυτόν τον λόγο η κρυφή μνήμη σε αυτές τις υλοποιήσεις είναι συνήθως μη μπλοκαρισμένη.

Μείωση χρόνου ευστοχίας

Ο χρόνος ευστοχίας είναι πολύ σημαντικός γιατί επηρεάζει το ρυθμό ρολογιού του επεξεργαστή. Έτσι ένας γρήγορος χρόνος ευστοχίας πολλαπλασιάζεται σε σπουδαιότητα πέρα από το τύπο του μέσου χρόνου προσπέλασης μνήμης γιατί βοηθά στα πάντα.Μικρές και απλές κρυφές μνήμες:Μια χρονοβόρα ποσότητα μιας ευστοχίας της κρυφής μνήμης χρησιμοποιεί το τμήμα δείκτη της διεύθυνσης για να διαβάσει τη μνήμη ετικετών και μετά να τη συγκρίνει με τη διεύθυνση. Είναι σημαντικό η μνήμη να έχει μικρό μέγεθος τόσο για να χωράει στο ίδιο τσιπ με τον επεξεργαστή ώστε να αποφεύγεται ο χρόνος ποινής εξόδου από το τσιπ αλλά και γιατί γενικά στο υλικό ισχύει ότι το μικρότερο είναι και το γρηγορότερο. Πέρα από το μικρό μέγεθος η διατήρηση μιας απλής κρυφής μνήμης όπως η άμεσα αντιστοιχισμένη μειώνει τον χρόνο ευστοχίας.

Αποφυγή μετάφρασης της διεύθυνσης κατά την διάρκεια της εύρεσης με δείκτη τη κρυφή μνήμη (Virtual addressed caches):Η λογική να κάνουμε τη συνήθη περίπτωση γρήγορη προτείνει να χρησιμοποιηθούν εικονικές διευθύνσεις για την κρυφή μνήμη εφόσον οι ευστοχίες είναι συχνότερες από τις

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

Διοχετευμένη (pipelined) προσπέλαση μνήμης:Μια ακόμα τεχνική είναι η διοχέτευση της προσπέλασης της κρυφής μνήμης ώστε η αποτελεσματική καθυστέρηση της ευστοχίας της κρυφής μνήμης πρώτου επιπέδου να μπορεί να είναι πολλαπλοί κύκλοι ρολογιού δίνοντας γρήγορο χρόνο ρολογιού και αργές ευστοχίες.

Κρυφές μνήμες ίχνους:Μια πρόκληση στην προσπάθεια να βρεθεί παραλληλισμός επιπέδου εντολών είναι η παροχή αρκετών εντολών σε κάθε κύκλο χωρίς εξαρτήσεις. Μια λύση ονομάζεται κρυφή μνήμη ίχνους. Αντί να περιορίζουμε τις εντολές ενός μπλοκ στατικής μνήμης σε χωρική τοπικότητα, μια κρυφή μνήμη ίχνους βρίσκει μια δυναμική ακολουθίας εντολών συμπεριλαμβανόμενων ληφθέντων διακλαδώσεων για να φορτώσει σε ένα μποκ κρυφής μνήμης.Το μειονέκτημα των κρυφών μνημών ίχνους είναι ότι αποθηκεύουν τις ίδιες εντολές πολλαπλές φορές στην κρυφή μνήμη εντολών. Διακλαδώσεις υπό συνθήκη που κάνουν διαφορετικές επιλογές έχουν ως αποτέλεσμα τις ίδιες εντολές να είναι μέρος χωριστών ιχνών, που το καθένα καταλαμβάνει χώρο στην κρυφή μνήμη.

32

ΥΛΟΠΟΙΗΣΗ

Υλοποίηση εξομοιωτή

Χρησιμοποιώντας την αντικειμενοστραφής γλώσσα προγραμματισμού Java αναπτύχθηκε ένα πρόγραμμα το οποίο μπορεί να εξομοιώσει την συμπεριφορά ενός υπολογιστικού συστήματος συνδεδεμένο σε μια ιεραρχία μνημών. Η Java προτιμήθηκε γιατί είναι αντικειμενοστραφής γλώσσα προγραμματισμού και μας επέτρεψε σχετικά ευκολότερο σχεδιασμό μονάδων υλικού καθώς και καθιστά πιο εύκολη την δημιουργία ενός γραφικού περιβάλλοντος δίνοντας στο πρόγραμμα περισσότερη ευχρηστία. Το σύστημα χωρίζεται σε δυο βασικά μέρη: την υλοποίηση του επεξεργαστή και του σετ εντολών του και την υλοποίηση της ιεραρχίας μνήμης και των παραμέτρων της.

Αρχιτεκτονική επεξεργαστή

Η αρχιτεκτονική βάση της οποίας σχεδιάστηκε ο επεξεργαστής που εξομοιώνει το πρόγραμμα βασίζεται σε έναν επεξεργαστή απλής διασωλήνωσης (pipeline). Το pipeline αποτελείται από τέσσερα στάδια:

• Fetch ( Ανάκληση εντολής): Στο στάδιο αυτό ανακαλείται μια εντολή από την μνήμη ώστε να εκτελεστεί στο ν επεξεργαστή

• Decode (Αποκωδικοποίηση εντολής): Στο στάδιο αυτό η εντολή που ανακλήθηκε στο προηγούμενο κύκλο αποκωδικοποιήται και ετοιμάζεται για εκτέλεση. Ελέγχεται παράλληλα αν την στιγμή αυτή στο execute (το επόμενο στάδιο του pipeline) η εντολή που εκτελείται γράφει σε δεδομένα που χρησιμοποιεί η εντολή που αποκωδικοποιήται. Αν συμβαίνει κάτι τέτοιο σημαίνει ότι η εντολή που αποκωδικοποιήται πρέπει να ξανααποκωδικοποιηθεί στον επόμενο κύκλο ώστε να ανανεωθούν τα δεδομένα.[CA004]

• Execute (Εκτέλεση εντολής): Στο στάδιο αυτό η εντολή εκτελείται. Σε περίπτωση διακλάδωσης λαμβάνονται όλα τα απαραίτητα μέτρα ώστε τα προηγούμενα στάδια του pipeline να αδειάσουν και να μην εκτελεστούν περιττές πράξεις.

• Write Back (Εγγραφή δεδομένων): Σε περίπτωση που κατά το execute κάποια εντολή χρησιμοποιεί την μνήμη είτε για να αποθηκεύσει είτε για να γράψει δεδομένα η προσπέλαση αυτή λαμβάνει μέρος κατά το write back στάδιο.

Όλες οι περιπτώσεις προσπέλασης της μνήμης από τον επεξεργαστή συνοψίζονται στις ανάκληση εντολής που λαμβάνει μέρος στο fetch και εγγραφή / ανάγνωση δεδομένων που συμβαίνει κατά το write back.Το pipeline που χρησιμοποιείται είναι πολύ απλό σε σχέση με πιο σύγχρονες υλοποιήσεις. Για αυτόν τον λόγο στις μετρήσεις που παρουσιάζονται αργότερα η απόδοση του συστήματος είναι αρκετά χαμηλή. Ο πιο σημαντικός λόγος που συμβαίνει αυτό είναι ότι σε περίπτωση αστοχίας κάποιας μνήμης το pipeline αδρανοποιείται μέχρι να ανακτηθούν τα ζητούμενα δεδομένα από μια μνήμη χαμηλότερα στην ιεραρχία (stall). Τα υπόλοιπα χαρακτηριστικά του επεξεργαστή βασίστηκαν στην αρχιτεκτονική MIPS. Ο λόγος που προτιμήθηκε η αρχιτεκτονική MIPS είναι λόγω του απλού συνόλου εντολών που βοηθάνε πολύ στην φόρτωση και αποθήκευση στην μνήμη καθώς και τον μορφότυπο αυτών που βοηθάνε πολύ στην μοντελοποίηση αυτών. Οι μορφότυποι αυτοί χωρίζονται σε τρεις τύπους: Εντολές τύπου I, εντολές τύπου R και εντολές τύπου J. Οι πρώτες

χρησιμοποιούνται για τις εντολές με άμεσες τιμές καθώς και τις διακλαδώσεις υπό συνθήκη. Οι τύπου R πρόκειται για εντολές καταχωρητή – καταχωρητή. Τέλος οι εντολές τύπου J πραγματοποιούν άλματα με ή χωρίς σύνδεση. Η κάθε εντολή είναι των 32 bit.[CA005] Έτσι ο επεξεργαστής διαθέτει ένα register file 32 καταχωρητών οι οποίοι μπορούν να αποθηκεύσουν 32 bit δεδομένων ο καθένας. Η διευθυνσιοδότηση που χρησιμοποιήθηκε είναι μόνο άμεσης τιμής και μετατόπισης και το ελάχιστο μέγεθος προσπέλασης δεδομένων είναι 4 bytes. [CA002]Καθώς το πρόγραμμα δεν εξομοιώνει function units απλοποιήθηκε το σύνολο εντολών και δεν χρησιμοποιήθηκαν πράξεις κινητής υποδιαστολής. Η πρόσθεση μιας εντολής στο σύνολο εντολών του που υποστηρίζει το πρόγραμμα είναι μια αρκετά απλή αλλά χρονοβόρα διαδικασία και για αυτόν τον λόγο στο σύνολο εντολών προστέθηκαν μόνο οι εντολές που χρησιμοποιήθηκαν και στον αλγόριθμο της assembly που χρησιμοποιήθηκε για τις μετρήσεις.[CA003]

Ιεραρχία μνήμης

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

• Μέγεθος ενός block σε θέσεις (slots). Το μέγεθος ενός block είναι κοινό σε όλες τις μνήμες.

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

• Επιλογή δομής ιεραρχίας μνήμης. Οι διαθέσιμες επιλογές είναι μια L1 συνδεδεμένη με μια κύρια μνήμη, μια L1 συνδεδεμένη σε μια L2 και η L2 σε μια κύρια μνήμη και τέλος το πρώτο επίπεδο μνήμης χωρισμένο σε κρυφή μνήμη εντολών (instruction cache) και κρυφή μνήμη δεδομένων (data cache).

• Ανάλογα με το ποια δομή έχει επιλεχθεί μπορούν να παραμετροποιηθούν και τα επιμέρους χαρακτηριστικά της κάθε κρυφής μνήμης. Αυτά είναι το μέγεθος της μνήμης σε block, ο αλγόριθμος που χρησιμοποιήται ως πολιτική αντικατάστασης κάποιου block, το associativity της μνήμης και τέλος η πολιτική εγγραφής της μνήμης. Όσο αναφορά τους αλγορίθμους πολιτικής αντικατάστασης έχουν υλοποιηθεί οι πρώτα μέσα πρώτο έξω – First In First Out (FIFO) , τυχαία – Random και ελάχιστα προσφάτως χρησιμοποιημένο – Least Recently Used (LRU). Οι πολικές εγγραφής είναι οι write through και write back.

Εξομοίωση

Το πρόγραμμα μπορεί να ανοίγει ένα αρχείο κώδικα assembly. Από το αρχείο αυτό εξάγονται οι εντολές και οι ετικέτες που περιέχει και τοποθετούνται στην κύρια μνήμη στις ανάλογες θέσεις. Στην συνέχεια ξεκινά η εκτέλεση με τον program counter (έναν καταχωρητή που περιέχει την επόμενη προς εκτέλεση εντολή στην μνήμη) να δείχνει στην πρώτη εντολή.Όταν τελειώσει η εκτέλεση ο εξομοιωτής παρέχει πληροφορίες σχετικά με την απόδοση

34

της διάταξης που χρησιμοποιήθηκε και τα στατιστικά κάθε μνήμης. Συγκεκριμένα στα αποτελέσματα βλέπουμε πόσοι κύκλοι χρειάστηκαν για την εκτέλεση του αλγορίθμου, πόσες εντολές εκτελέστηκαν, την απόδοση εκφρασμένη σε κύκλους ανά εντολή, τον αριθμό των εγγραφών (stores) προσπελάσεων στην μνήμη (loads).Όσο αναφορά τις πληροφορίες για κάθε μνήμη, το πρόγραμμα μετράει τις προσπελάσεις (accesses) τις ευστοχίες (hits) και τις αστοχίες (misses). Επίσης στην περίπτωση που χρησιμοποιήται πολιτική εγγραφής write back το πρόγραμμα μετράει πόσες φορές χρειάστηκε να ενημερωθεί η χαμηλότερης ιεραρχίας μνήμης.

ΜΕΤΡΗΣΕΙΣ – ΣΥΜΠΕΡΑΣΜΑΤΑ

Για να πραγματοποιηθούν οι μετρήσεις χρησιμοποιήθηκε μικρό μέγεθος πίνακα καθώς και μικρά μεγέθη cache γιατί η αύξηση των μεγεθών αυτών προκαλούσε μεγάλη αύξηση στον χρόνο εξομοίωσης καθιστώντας τις μετρήσεις μη εφικτές. Όπως αναφέρθηκε και παραπάνω η ελάχιστη δυνατή ποσότητα ανάγνωσης δεδομένων θεωρήθηκε 4 bytes. Για να εξακριβωθεί η ορθή λειτουργία του προγράμματος υπολογίστηκαν οι προσπελάσεις μνήμης του αλγορίθμου ώστε να εξακριβωθούν με αυτές του προγράμματος. Έτσι για τον πολλαπλασιασμό πινάκων 10 x 10 θα χρειαστούν 300 stores για την αρχικοποίηση των πινάκων, 1000 loads για την ανάγνωση των στοιχείων του καθενός από τους δυο πίνακες και 100 stores για την αποθήκευση του αποτελέσματος στον τελικό πίνακα. Τα παραπάνω γίνονται κατανοητά από τον αλγόριθμο του πολλαπλασιασμού σε C:

36

#define MATRIX_DIMEN 10

int main(int argc, char *argv[]){ int A[MATRIX_DIMEN][MATRIX_DIMEN]; int B[MATRIX_DIMEN][MATRIX_DIMEN]; int C[MATRIX_DIMEN][MATRIX_DIMEN]; int i,j,k;

for (i=0;i<MATRIX_DIMEN;i++){ for (j=0;j<MATRIX_DIMEN;j++){ A[i][j] = i + j; B[i][j] = (i+1) * (j+1); C[i][j] = 0; } } for (i=0;i<MATRIX_DIMEN;i++){ for (j=0;j<MATRIX_DIMEN;j++){ for (k=0;k<MATRIX_DIMEN;k++){ C[i][j] = C[i][j] + (A[i][k]*B[k][j]); } } }}

Με MATRIX_DIMEN = 10, έχουμε στα 2 πρώτα for 10 x 10 = 100 προσπελάσεις για τον κάθε πίνακα. Συνολικά 300 stores. Στα 3 επόμενα for έχουμε 10 x 10 x10 = 1000 αναγνώσεις για τους πίνακες A και B και 10 x 10 αποθηκεύσεις για τον πίνακα C αφού επηρεάζεται μόνο από τα i και j που είναι στους εξωτερικούς βρόχους. Συμπερασματικά έχουμε:2000 loads και 400 stores.Επίσης είναι σαφές ότι για να γίνουν τα παραπάνω χρειάζονται 2400 access L1 μνήμη. Τέλος θεωρώντας ότι κάθε στοιχείο του πίνακα αντιστοιχεί σε 4 bytes, κάθε πίνακας χρειάζεται 10 x 10 x 4 = 400 bytes. Συνολικά χρειαζόμαστε 3 x 400 = 1200 bytes μνήμης.Χρησιμοποιώντας το πρόγραμμα αρχικά εξομοιώσαμε ένα σύστημα με μια L1 μνήμη με αδιάφορα χαρακτηριστικά μόνο για να δούμε αν ο αριθμός των loads και stores είναι σωστός. Πράγματι το πρόγραμμα μέτρησε 2000 loads και 401 stores. Το επιπλέον store οφείλεται σε μια επανάληψη του τελευταίου βρόχου μια επιπλέον φορά σφάλμα που οφείλεται στον τρόπο υλοποίησης του αλγορίθμου σε assembly.Στην συνέχεια για να απομονώσουμε τα accesses των δεδομένων από τις εντολών χρησιμοποιήθηκε μια διάταξη Instruction και Data Cache συνδεδεμένες σε μια L2. Τόσο η L2 όσο και η data cache ήταν αρκετά μεγάλες ώστε να χωρέσουν και τους 3 πίνακες ολόκληρους. Για την ακρίβεια οι ρυθμίσεις που χρησιμοποιήθηκαν ήταν:L2: 50 blocks x 8 slots x 4 bytes = 1600 bytesData cache: 50 blocks x 8 slots x 4 bytes = 1600 bytesInstruction Cache: 8 blocks x 8 slots x 4 bytes = 256 bytesΌλες οι μνήμες ήταν fully associative για να αποφευχθούν απρόβλεπτα misses.Τα αποτελέσματα ήταν:

Παρατηρούμε ότι στην data Cache έχουμε 2401 accesses όπως ήταν αναμενόμενο. Επίσης τα misses είναι 39 αφού η cache έχει πλάτος 8 θέσεων των 4bytes για να μεταφερθούν οι πίνακες χρειάζονται: 1200 bytes (μέγεθος πινάκων) / 32bytes (μέγεθος

L1 Instruction Cache:Misses :9Hits :33913Accesses :33922L1 Data Cache:Misses :39Hits :2362Accesses :2401L2 Shared Cache:Misses :48Hits :362Accesses :410

block) = 37.5 με στρογγυλοποίηση στον αμέσως μεγαλύτερο ακέραιο: 38. Το αποτέλεσμα είναι 39 επειδή τα δεδομένα δεν είναι απόλυτα ευθυγραμμισμένα με τα block της μνήμης.Επίσης τα misses της L2 cache ισούνται με το άθροισμα των misses των άλλων δυο αφού καμία cache δεν χρειάζεται να αντικαταστήσει δεδομένα.Θεωρώντας την μια διάσταση του πίνακα Ν μπορούμε να γενικεύσουμε τα παραπάνω:Loads: 2*N*N*N = 2*(N^3)Stores: 4*N*N = 4*(N^2)Data size: 3*N*N *4 bytes= 3*(N^2)*4 bytesΣτη συνέχεια έγιναν οι παρακάτω μετρήσεις:

Διάταξη L1 Shared Cache – L2 Shared Cache

Αλλαγή associativity

Με Ν = 16 έχουμε μέγεθος δεδομένων 3072 bytes. Στην διάταξη που θα χρησιμοποιήσουμε τα δεδομένα δεν θα χωράνε στην L2 cache. Με line size 8 και μέγεθος θέσης 4 bytes η L2 με 64 γραμμές πληροί αυτόν τον σκοπό (2048 bytes). Η L1 θα είναι 8 φορές μικρότερη με 8 γραμμές (256 bytes). Χρησιμοποιώντας ως μεταβλητή την associativity κάθε μνήμης αναμένουμε ότι καθώς θα αυξάνουν οι πιθανές θέσεις μιας διεύθυνσης στην μνήμη θα αυξηθεί και η απόδοση της διάταξης. Έτσι όταν οι μνήμες είναι direct mapped και κάθε διεύθυνση αντιστοιχεί σε μόνο ένα block, οι κύκλοι ανά εντολή (CPI) της διάταξης μεγιστοποιούνται ενώ αυξάνοντας το μέγεθος του κάθε set καταλήγουμε στην βέλτιστη απόδοση στην διάταξη με fully associative μνήμες. Οι μετρήσεις διαδοχικά ήταν: (L1 associativity | L2 Associativity): (direct mapped | direct mapped) , (direct mapped , 2) (2 | 4) , (4 | 8) , (4 | fully associative) , (fully associative | fully associative). Για τις μετρήσεις χρησιμοποιήθηκε πολιτική εγγραφής write back. Το αποτέλεσμα φαίνεται στο γράφημα 1 παρακάτω:

38

Λόγω του πολύ μικρού μεγέθους της L1 η διαφορά στην απόδοση λόγω της αύξησης του μεγέθους του set δεν είναι τόσο αισθητή. Οι περισσότερες προσπελάσεις στην μνήμη αυτή έχουν να κάνουν με φόρτωση εντολών. Η βελτίωση στην απόδοση οφείλεται στα λιγότερα conflict misses που έχουν να κάνουν με αντικατάσταση εντολών. Τα misses που οφείλονται στα δεδομένα σε όλες τις περιπτώσεις είναι παρόμοια αφού λόγω του μεγέθους της μνήμης ακόμα και σε μεγάλα μεγέθη set τα misses θα είναι αναπόφευκτα. Στο γράφημα 2 φαίνονται τα misses και τα hits της μνήμης L1.

Γράφημα 1: Απόδοση - Associativity με διάταξη L1 Shared, L2 Shared

dir -dir dir -2 2 – 4 4 – 8 4 – fully fully – fully2,2

2,3

2,4

2,5

2,6

2,7

2,8

2,9

3

Performance - Associativity

Associativity

Per

form

ance

(CP

I)

Γράφημα 2: L1 Shared Cache Misses / Hits - Associativity με διάταξη L1 Shared , L2 Shared

dir -dir dir -2 2 – 4 4 – 8 4 – fully fully – fully0,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05

1,40E+05

1,60E+05

L1 Shared Cache Misses/Hits - Associativity

HitsMisses

Associativity

Mis

ses

/ Hits

Αντίθετα με την L1 στην L2 μνήμη η επίδραση των set είναι μεγαλύτερη. Αυτό συμβαίνει λόγω των λιγότερων conflict misses που συμβαίνουν στην μνήμη. Αυτό έχει ως αποτέλεσμα την μείωση τόσο του αριθμού αστοχιών όσο και του αριθμού προσπελάσεων. Το αποτέλεσμα είναι αντιληπτό στο γράφημα 3.

Επαναλαμβάνοντας της παραπάνω μετρήσεις με αυξημένο το μέγεθος της L2 ώστε τα δεδομένα να χωράνε σε αυτήν αναμένουμε η επίδραση του associativity στην L2 να είναι αμελητέα. Το γράφημα 4 επιβεβαιώνει το γεγονός αυτό:

40

Γράφημα 3: L2 Shared Cache Misses Hits - Associativity με διάταξη L1 Shared Cache, L2 Shared Cache

dir -dir dir -2 2 – 4 4 – 8 4 – fully fully – fully0

2000

4000

6000

8000

10000

12000

14000

16000

L2 Shared Cache Misses/Hits - Associativity

HitsMisses

Associativity

Mis

ses

/ Hits

Γράφημα 4: Aπόδοση - Associativity με διάταξη L1 Shared Cache, L2 Shared Cache όταν τα δεδομένα χωράνε στην L2

dir -dir dir -2 2 – 4 4 – 8 4 – fully fully – fully1,74

1,76

1,78

1,8

1,82

1,84

1,86

1,88

1,9

1,92

Performance - Associativity

Associativity

Per

form

ance

(CP

I)

Αλλαγή μεγέθους μνήμης

Στην συνέχεια αλλάζοντας τα μεγέθη των L1 και L2 μετρήσαμε την απόδοση της διάταξης. Για τον σκοπό αυτό χρησιμοποιήσαμε Ν = 32 ώστε οι πίνακες να μην χωράνε στις μνήμες εκτός από τις τελευταίες τρεις μετρήσεις όπου τα δεδομένα χωράνε στην L2. Το μέγεθος των δεδομένων σε αυτήν την περίπτωση είναι 12288 bytes. Στις πρώτες τρεις μετρήσεις αυξήσαμε το μέγεθος της L1 ενώ κάθε τρεις μετρήσεις αυξήσαμε και το μέγεθος της L2. Διαδοχικά τα μεγέθη των μνημών (L1 | L2) ήταν: (256 | 4096) , (512 | 4096), (4096 | 4096), (256 | 8192) , (512 | 8192) , (4096 | 8192) , (256 | 16384) , (512 | 16384) , (4096 | 16384). Για τις μετρήσεις χρησιμοποιήθηκε πολιτική εγγραφών write through και associativity: (L1: 4 | L2: 8) Περιμένουμε η απόδοση να βελτιωθεί σε όλες τις περιπτώσεις με πιο έντονη βελτίωση στις περιπτώσεις που αυξάνεται το μέγεθος της L1. Στο γράφημα 5 έχουμε τα αποτελέσματα.

Στις περιπτώσεις (256 | 4096) , (256 | 8192) και (256 | 16384) που αυξάνεται μόνο η L2 ενώ η L1 παραμένει πολύ μικρή υπάρχει βελτίωση αλλά όχι τόσο αισθητή όσο όταν αυξάνεται το μέγεθος της L1. Η απόδοση της διάταξης είναι καλύτερη όταν τα μεγέθη είναι (4096 | 4096) (Μέγιστη L1, ελάχιστη L2) παρά όταν είναι (256 | 16384) (Ελάχιστη L1 , Μέγιστη L2) όπου τα δεδομένα χωράνε στην L2.Αναμενόμενο είναι επίσης καθώς αυξάνεται το μέγεθος της L1 να μειώνονται οι αστοχίες και οι προσπελάσεις της. Το ίδιο ισχύει για την L2 με τις αστοχίες να ελαχιστοποιούνται στην τελευταία περίπτωση όπου τα δεδομένα χωράνε στην μνήμη.

Γράφημα 5: Απόδοση - Μέγεθος L1 , L2

(256 | 4096)(512 | 4096)

(4096 | 4096)(256 | 8192)

(512 | 8192)(4096 | 8192)

(256 | 16384)(512 | 16384)

(4096 | 16384)

0

0,5

1

1,5

2

2,5

3

Performance - L1 | L2 Size

L1 | L2 Size (bytes)

Per

form

ance

(CP

I)

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

42

Γράφημα 6: L1 Shared Cache Misses / Hits - Μέγεθος L1 , L2

(256 | 4096)(512 | 4096)

(4096 | 4096)(256 | 8192)

(512 | 8192)(4096 | 8192)

(256 | 16384)(512 | 16384)

(4096 | 16384)

0,00E+00

2,00E+05

4,00E+05

6,00E+05

8,00E+05

1,00E+06

1,20E+06

1,40E+06

L1 Shared Cache Misses/Hits - L1 | L2 Size

HitsMisses

L1 | L2 Size (bytes)

Mis

ses

/ Hits

Γράφημα 7: L2 Shared Cache Misses / Hits - Μέγεθος L1 , L2

(256 | 4096)(512 | 4096)

(4096 | 4096)(256 | 8192)

(512 | 8192)(4096 | 8192)

(256 | 16384)(512 | 16384)

(4096 | 16384)

0,00E+00

1,00E+04

2,00E+04

3,00E+04

4,00E+04

5,00E+04

6,00E+04

7,00E+04

8,00E+04

9,00E+04

L2 Shared Cache Misses/Hits - L1 | L2 Size

HitsMisses

L1 | L2 Size (bytes)

Mis

ses

/ Hits

Αλλαγή μεγέθους μνήμης – Write Through

Στην επόμενη διάταξη αλλάξαμε τα μεγέθη των μνήμων L1 , L2 έτσι ώστε αρχικά τα δεδομένα να χωράνε και στις δυο μνήμες, στην συνέχεια μόνο στην L2 και τέλος να μην χωράνε σε καμία μνήμη. Χρησιμοποιήθηκε N = 16 δηλαδή μέγεθος δεδομένων 3072 bytes ενώ η πολιτική εγγραφής ήταν write through και το associativity των μνημών (L1: 4 | L2: 8). Αρχικά οι μνήμες ήταν (4096 | 8192) ώστε τα δεδομένα να χωράνε στην L1 στην συνέχεια (1024 | 4096) ώστε τα δεδομένα να χωράνε μόνο στην L2, και τέλος (256 | 1024) όπου οι πίνακες δεν χωράνε σε καμία κρυφή μνήμη. Είναι αναμενόμενο η απόδοση να επιδεινώνεται καθώς τα δεδομένα δεν χωράνε στις μνήμες. Αυτό γιατί όταν τα δεδομένα χωράνε στις μνήμες οι αστοχίες που ελαχιστοποιούνται για την κάθε μνήμη αφού δεν χρειάζεται να αντικαταστήσει δεδομένα. Η απόδοση της διάταξης κάθε φορά φαίνεται στο παρακάτω γράφημα:

Όπως ήδη αναφέρθηκε οι αστοχίες των μνημών και κατ' επέκταση και οι προσπελάσεις θα αυξάνονται καθώς τα δεδομένα δεν θα χωράνε σε αυτές. Τα αποτελέσματα αυτά επιβεβαιώνονται από τις μετρήσεις στα γραφήματα 9 , 10 παρακάτω.

Γράφημα 8: Απόδοση - Μέγεθος L1, L2 με N = 16 (μέγεθος δεδομένων 3072) και Write Through

(4096 | 8192) (1024 | 4096) (256 | 1024)0

0,5

1

1,5

2

2,5

3

3,5

Performance - L1 | L2 Size N = 16

L1 | L2 Size (bytes)

Per

form

ance

(CP

I)

44

Γράφημα 9: L1 Shared Cache Misses / Hits - Μέγεθος L1 , L2 με Ν = 16 (μέγεθος δεδομένων 3072) και Write Through

(4096 | 8192) (1024 | 4096) (256 | 1024)0,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05

1,40E+05

1,60E+05

1,80E+05

L1 Shared Cache Misses/Hits - L1 | L2 Size (N = 16)

HitsMisses

L1 | L2 Size (bytes)

Mis

ses

/ Hits

Γράφημα 10: L2 Shared Cache Misses / Hits - Μέγεθος L1 , L2 με Ν = 16 (μέγεθος δεδομένων 3072) και Write Through

(4096 | 8192) (1024 | 4096) (256 | 1024)0

2000

4000

6000

8000

10000

12000

14000

16000

L2 Shared Cache Misses/Hits - L1 | L2 Size (N = 16)

HitsMisses

L1 | L2 Size (bytes)

Mis

ses

/ Hits

Αλλαγή μεγέθους μνήμης – Write Back

Τις παραπάνω μετρήσεις τις επαναλάβαμε χρησιμοποιώντας πολιτική εγγραφής write back. Αναμενόμενο είναι να υπάρξουν λιγότερα hits στην L2 cache λόγω της απουσίας των εγγραφών που δεν χρειάζονται να γίνουν και μια μικρή βελτίωση της απόδοσης για τον ίδιο λόγο. Έτσι πήραμε τα αντίστοιχα γραφήματα που επιβεβαιώνουν τα παραπάνω:

Γράφημα 11: Απόδοση - Μέγεθος L1, L2 με N = 16 (μέγεθος δεδομένων 3072) και Write Back

(4096 | 8192) (1024 | 4096) (256 | 1024)0

0,5

1

1,5

2

2,5

3

3,5

Performance - L1 | L2 Size (N = 16)

L1 | L2 Size (bytes)

Per

form

ance

(CP

I)

Γράφημα 12: L1 Shared Cache Misses / Hits - Μέγεθος L1 , L2 με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back

(4096 | 8192) (1024 | 4096) (256 | 1024)0,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05

1,40E+05

1,60E+05

1,80E+05

L1 Shared Cache Misses/Hits -L1 | L2 Size (N = 16)

HitsMisses

L1 | L2 Size (bytes)

Mis

ses

/ Hits

Παρατηρούμε πράγματι ότι η απόδοση βελτιώθηκα κατά περίπου 0.02 κύκλους ανά εντολή ενώ στην L2 έχουν μειωθεί οι προσπελάσεις. Ειδικά στην πρώτη περίπτωση όπου τα δεδομένα χωράνε στην L1 είναι σαφές ότι έχουν αφαιρεθεί όλες οι εγγραφές που γινόντουσαν στην L2 κατά την εκτέλεση του αλγορίθμου.

Εξομοίωση bottleneck

Στην συνέχεια μελετήσαμε πιθανά bottlenecks σε διάφορα σημεία της ιεραρχείας. Για να εξομοιώσουμε ένα bottleneck τριπλασιάσαμε τον χρόνο επικοινωνίας των μνημών μεταξύ των οποίων υπάρχει το bottleneck. Οι μετρήσεις που έγιναν αφορούν αρχικά χωρίς κανένα bottleneck με χρόνους (σε κύκλους) 1 – 7 – 30. Στην συνέχεια ερευνήθηκε η ύπαρξη bottleneck μεταξύ register file και L1, μεταξύ L1 και L2 και τέλος μεταξύ L2 και main memory. Οι αντίστοιχοι χρόνοι είναι: (3 – 7 – 30) , (1 – 21 – 30) , (1 – 7 – 90).Όπως ήδη αναφέρθηκε η ιεραρχία μνημών χρησιμοποιήται ώστε να μπορούμε να έχουμε πρόσβαση σε ένα πολύ μεγάλο μέγεθος μνήμης με την ταχύτητα που έχουμε πρόσβαση σε μια μικρή μνήμη. Έτσι είναι αναμενόμενο ότι ένα bottleneck χαμηλά στην ιεραρχία δεν θα επιρρεάσει τόσο την απόδοση της διάταξης όσο ένα σε μια πιο υψηλή θέση. Πράγματι οι μετρήσεις επιβεβαίωσαν τα παραπάνω όπως φαίνεται και στο γράφημα 14.

46

Γράφημα 13: L2 Shared Cache Misses / Hits - Μέγεθος L1 , L2 με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back

(4096 | 8192) (1024 | 4096) (256 | 1024)0

2000

4000

6000

8000

10000

12000

14000

16000

L2 Shared Cache Misses/Hits - L1 | L2 Size (N = 16)

HitsMisses

L1 | L2 Size (bytes)

Mis

ses

/ Hits

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

Αλλαγή μεγέθους block με σταθερό μέγεθος μνήμης

Άλλη μια παράμετρος που έχει ενδιαφέρον είναι το σχήμα της cache. Για να μελετήσουμε αυτήν την παράμετρο κρατήσαμε σταθερό το συνολικό μέγεθος της κάθε cache και μεταβάλλαμε τόσο τον αριθμό γραμμών της όσο και το μέγεθος της κάθε γραμμής. Έτσι για μια L1 μεγέθους 256 bytes, θεωρώντας ότι κάθε θέση (slot) της μνήμης είναι 4 bytes σχεδιάστηκε διαδοχικά ως εξής: 2 slots x 32 blocks , 4 slots x 16 blocks 8 slots x 8 blocks και τέλος 16 slots x 4 blocks. Όταν το κάθε block της cache αποτελείται από λίγα slots οι προσπελάσεις και οι αστοχίες αυξάνουν καθώς η μνήμη μπορεί να φέρει λιγότερα δεδομένα με κάθε block. Από την άλλη αν το μέγεθος ενός block αποτελείται από πολλά slots παρά το ότι η μνήμη μπορεί να φέρνει περισσότερα δεδομένα με κάθε block οι αστοχίες θα αυξηθούν αφού όταν γίνεται μια αστοχία η cache θα διώχνει εξίσου μεγάλο αριθμό δεδομένων τα οποία μπορεί να χρειαστεί αργότερα. Είναι αναμενόμενο ότι μια ενδιάμεση κατάσταση θα έχει τη μέγιστη απόδοση. Χρησιμοποιώντας αρχικά μια διάταξη με μόνο μια μνήμη L1 έχουμε:

Γράφημα 14: Απόδοση - Bottlenecks (Χωρίς | RF – L1 | L1 – L2 | L2 - Main)

1 – 7 – 30 3 – 7 – 30 1 – 21 – 30 1 – 7 – 900

0,5

1

1,5

2

2,5

3

3,5

4

Performance - Bottlenecks

Latencies: L1 - L2 - Main (cycles)

Per

form

ance

(CP

I)

Πράγματι το γράφημα 15 επιβεβαιώνει τα παραπάνω έχοντας ένα ελάχιστο CPI για line size = 4 slots. Το ίδιο ελάχιστο εμφανίζεται και στο γράφημα των αστοχιών και προσπελάσεων αφού όπως αναφέρθηκε η βέλτιστη απόδοση θα βρισκόταν στην περίπτωση των ελάχιστων αστοχιών (γράφημα 16).

Η παραπάνω μέτρηση επαναλήφθηκε και επαληθεύθηκε για την περίπτωση της διάταξης L1 - L2. Τα αποτελέσματα που προέκυψαν παρουσιάζονται στα επόμενα γραφήματα.

48

Γράφημα 15: Απόδοση - Μέγεθος γραμμής με διάταξη L1 Shared

2 4 8 160

1

2

3

4

5

6

7

8

Performance - Line Size

Line Size (Memory Slots)

Per

form

ance

(CP

I)

Γράφημα 16: L1 Shared CacheMisses / Hits - Μέγεθος γραμμής με διάταξη L1 Shared

2 4 8 160,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05

1,40E+05

1,60E+05

1,80E+05

L1 Shared Cache Misses/Hits -Line Size

HitsMisses

Line Size (Memory Slots)

Mis

ses

/ Hits

Γράφημα 17: Απόδοση - Μέγεθος γραμμής με διάταξη L1 , L2 Shared

2 4 8 160

0,5

1

1,5

2

2,5

3

Performance - Line Size

Line Size (Memory Slots)

Per

form

ance

(CP

I)

Γράφημα 18: L1 Shared Cache Misses / Hits - Μέγεθος γραμμής με διάταξη L1, L2 Shared

2 4 8 160,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05

1,40E+05

1,60E+05

1,80E+05

L1 Shared Cache Misses/Hits -Line Size

HitsMisses

Line Size (Memory Slots)

Mis

ses

/ Hits

Στην περίπτωση της διάταξης L1 – L2 παρατηρούμε ότι ενώ τα διαγράμματα της απόδοσης και της μνήμης L1 παραμένουν ίδια όπως ήταν αναμενόμενο στην μνήμη L2 οι αστοχίες μειώνονται σταθερά καθώς αυξάνεται το μέγεθος γραμμής παρά το ότι συνολικά οι προσπελάσεις ακολουθούν το ίδιο μοτίβο. Αυτό συμβαίνει λόγω του συνολικού μεγέθους της μνήμης L2. Η μνήμη έχει μέγεθος 4096 bytes που είναι αρκετά για να χωρέσουν τα δεδομένα. Έτσι στην L2 δεν γίνονται άλλες αστοχίες από την στιγμή που τα δεδομένα μεταφερθούν σε αυτήν. Έτσι όσο πιο πολλά δεδομένα μπορεί να μεταφέρει η μνήμη ανά μπλοκ σε τόσο λιγότερες αστοχίες θα μπορέσει να τα φέρει όλα. Οι προσπελάσεις διατηρούν το την ίδια μορφή γιατί βασίζονται στην L1 μνήμη που τις προσκαλεί.

Διάταξη L1 Instruction Cache, L1 Data Cache – L2 Shared Cache

Αλλαγή associativity

Για την μέτρηση αυτή χρησιμοποιήθηκε επίσης Ν = 16 (3072 bytes δεδομένων) και μέγεθος μνημών: 2048 bytes για την L2 cache και 512 bytes για τις Instruction και Data caches. Οι τιμές του associativity που χρησιμοποιήθηκαν ήταν: (Instruction , Data | L2) (direct mapped | direct mapped) , (direct mapped | 2) , (2 | 4) , (4 | 8) , (4 | fully associative) , (fully associative | fully associative). Σε αυτήν την περίπτωση οι εντολές χωράνε εξολοκλήρου στην Instruction Cache με αποτέλεσμα να μην επηρεάζεται από τις ρυθμίσεις αφού όλες οι εντολές χωράνε μέσα στην μνήμη. Όμως οι data και L2 παρουσιάζουν την ίδια συμπεριφορά όπως στην περίπτωση L1 και L2. Συγκεκριμένα όταν το associativity αυξάνεται αυξάνεται και η απόδοση με μεγιστοποίηση της στην ρύθμιση (fully associative | fully associative). Πέρα από την απόδοση οι αστοχίες των δυο μνημών όσο και οι προσπελάσεις μειώνονται. Αυτό είναι αναμενόμενο αφού εφόσον μια διεύθυνση πλέον αποθηκεύεται σε παραπάνω από μια πιθανή θέση ο αλγόριθμος LRU που χρησιμοποιείται φροντίζει να μην απομακρυνθούν δεδομένα που μπορεί να χρειαστούν σε μελλοντικές

50

Γράφημα 19: L2 Shared Cache Misses / Hits - Μέγεθος γραμμής με διάταξη L1 , L2 Shared

2 4 8 160,00E+00

2,00E+03

4,00E+03

6,00E+03

8,00E+03

1,00E+04

1,20E+04

1,40E+04

1,60E+04

1,80E+04

2,00E+04

L2 Shared Cache Misses/Hits -Line Size

HitsMisses

Line Size (Memory Slots)

Mis

ses

/ Hits

πράξεις. Αυτά τα συμπεράσματα επιβεβαιώνονται και από τις μετρήσεις στα παρακάτω γραφήματα.

Γράφημα 20: Απόδοση - Associativity με διάταξη L1 Instruction , L1 Data – L2 Shared

dir -dir dir -2 2 – 4 4 – 8 4 – fully fully – fully1,6

1,7

1,8

1,9

2

2,1

2,2

Performance -Associativity

Associativity

Per

form

ance

(CP

I)

Γράφημα 21: L1 Instruction Cache Misses / Hits - Associativity με διάταξη L1 Instruction , Data – L2 Shared

dir -dir dir -2 2 – 4 4 – 8 2 – fully fully – fully0,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05

1,40E+05

L1 Instruction Cache Misses/Hits -Associativity

HitsMisses

Associativity

Mis

ses

/ Hits

Στις διατάξεις 20 , 21 ,22 φαίνεται ότι η μεγαλύτερη αύξηση στην απόδοση συμβαίνει όταν από η data cache αλλάζει από direct mapped σε 2. Αυτό είναι απόλυτα λογικό αφού ενώ στην επιλογή direct mapped μια διεύθυνση δεν έχει καμία επιλογή για την θέση στην οποία θα τοποθετηθεί στην περίπτωση που έχουμε 2 – way associativity η δεύτερη πιθανή θέση που μπορούμε να τοποθετήσουμε μια διεύθυνση αποτρέπει πολλά χρήσιμα δεδομένα από το να απομακρυνθούν από την data cache. Επίσης στην μετάβαση από (4 | 8) σε (4 | fully

52

Γράφημα 22: L1 Data Cache Misses / Hits - Associativity με διάταξη L1 Instruction, Data – L2 Shared

dir -dir dir -2 2 – 4 4 – 8 4 – fully fully – fully0,00E+00

2,00E+03

4,00E+03

6,00E+03

8,00E+03

1,00E+04

1,20E+04

1,40E+04

1,60E+04

L1 Data Cache Misses/Hits -Associativity

HitsMisses

Associativity

Mis

ses

/ Hits

Γράφημα 23: L2 Shared Cache Misses / Hits - Associativity με διάταξη L1 Instruction, Data – L2 Shared

dir -dir dir -2 2 – 4 4 – 8 4 – fully fully – fully0,00E+00

1,00E+03

2,00E+03

3,00E+03

4,00E+03

5,00E+03

6,00E+03

7,00E+03

8,00E+03

L2 Shared Cache Misses/Hits -Associativity

HitsMisses

Associativity

Mis

ses

/ Hits

associative) φαίνεται ότι από ένα σημείο και μετά το associativity της L2 έχει αμελητέα επίδραση στην απόδοση της διάταξης.

Αλλαγή μεγέθους μνήμης

Για να μελετηθεί η επίδραση του μεγέθους των μνημών όπως και στην περίπτωση διάταξης L1 – L2 χρησιμοποιήθηκε Ν = 32 με 12288 bytes δεδομένων. Οι μνήμες Instruction και Data είχαν ίδιο μέγεθος μεταξύ τους σε όλες τις μετρήσεις ενώ χρησιμοποιήθηκε 4 – way associativity. Για την L2 χρησιμοποιήθηκε 8 – way associativity. Τα μεγέθη των μνημών ήταν: (Instruction , Data | L2) : (256 | 4096) , (512 | 4096) , (4096 | 4096) , (256 | 8192) , (512 | 8192) , (4096 | 8192) , (256 | 16384) , (512 | 16384) , (4096 | 16384). Το μέγεθος ενός block ήταν 8 slots. Τα δεδομένα δεν χωράνε σε καμία μνήμη εκτός από την L2 στις τελευταίες 3 μετρήσεις. Είναι αναμενόμενο να δούμε ίδια συμπεριφορά της απόδοσης με την περίπτωση της διάταξης L1 – L2. Δηλαδή μεγάλη βελτίωση της απόδοσης όταν η data cache έχει μεγαλύτερο μέγεθος και μια μικρότερη αλλά αισθητή βελτίωση στην περίπτωση που η L2 αυξάνεται σε μέγεθος. Παράλληλα με την μείωση του CPI (βελτίωση της απόδοσης) αναμένεται να μειωθούν και οι αστοχίες αφού μεγαλώνοντας μια μνήμη έχουμε την δυνατότητα να έχουμε αποθηκευμένα περισσότερα δεδομένα χωρίς να χρειάζεται να διώξουμε άλλα τα οποία μπορεί να είναι χρήσιμα αργότερα. Τα συμπεράσματα αυτά επιβεβαιώνονται από τα γραφήματα 24, 26 ,27. Στο γράφημα 25 βλέπουμε ότι πράγματι η Instruction cache έχει σταθερή συμπεριφορά αφού οι εντολές χωράνε σε αυτήν σε όλες τις περιπτώσεις.

Γράφημα 24: Απόδοση - Μέγεθος μνημών L1 Instruction , L1 Data – L2 Shared

(256 | 4096)(512 | 4096)

(4096 | 4096)(256 | 8192)

(512 | 8192)(4096 | 8192)

(256 | 16384)(512 | 16384)

(4096 | 16384)

0

0,5

1

1,5

2

2,5

Performance - L1 Instruction , Data Cache | L2 Shared Cache Size

Instruction, Data | L2 Size (bytes)

Per

form

ance

(CP

I)

54

Γράφημα 25: L1 Instruction Cache Misses / Hits - Μεγέθη μνημών :L1 Instruction , L1 Data – L2 Shared

(256 | 4096)(512 | 4096)

(4096 | 4096)(256 | 8192)

(512 | 8192)(4096 | 8192)

(256 | 16384)(512 | 16384)

(4096 | 16384)

0,00E+00

2,00E+05

4,00E+05

6,00E+05

8,00E+05

1,00E+06

1,20E+06

L1 Instruction Cache Misses/Hits - L1 Instruction, Data | L2 Shared Cache Size

HitsMisses

Instruction, Data | L2 Size (bytes)

Mis

ses

/ Hits

Γράφημα 26: L1 Data Cache Misses / Hits - Μεγέθη μνημών L1 Instruction , L1 Data – L2 Shared

(256 | 4096)(512 | 4096)

(4096 | 4096)(256 | 8192)

(512 | 8192)(4096 | 8192)

(256 | 16384)(512 | 16384)

(4096 | 16384)

0,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05L1 Data Cache Misses/Hits - L1 Instruction, Data | L2 Shared Cache Size

HitsMisses

Instruction , Data | L2 Size (bytes)

Mis

ses

/ Hits

Συγκρίνοντας τα αποτελέσματα με αυτά της διάταξης L1 – L2 παρατηρούμε ότι ο διαχωρισμός των εντολών από τα δεδομένα αποφέρει μια μείωση του CPI και βελτίωση της απόδοσης. Επίσης είναι αξιοσημείωτο ότι η αλλαγή του μεγέθους της μνήμης έχει εντονότερη επίδραση στην απόδοση στην περίπτωση της διάταξης L1 – L2 παρά στην Instruction , Data – L2. Αυτό συμβαίνει επειδή η instruction cache στην δεύτερη περίπτωση δεν επιρρεάζει καθόλου την απόδοση του συστήματος. Όμως η μεγαλύτερη δραστηριότητα κατά την εκτέλεση του αλγορίθμου είναι στην instruction cache και αυτό φαίνεται από τις προσπελάσεις που συμβαίνουν σε αυτήν σε σχέση με αυτές τις data: 1.022.215 προσπελάσεις για τις εντολές και μόλις 69.633 για τα δεδομένα. Έτσι αν και η απόδοση βελτιώνεται λόγω τις μείωσης των αστοχιών για τα δεδομένα η επίδραση των εντολών παραμένει σταθερή σε αντίθεση με την περίπτωση της διάταξης L1 – L2 που βελτιώνεται μαζί με τα δεδομένα.Τέλος στο γράφημα 27 παρατηρούμε ότι μαζί με τις προσπελάσεις και τις αστοχίες μειώνονται και τα hits στην μνήμη L2. Αυτό συμβαίνει λόγο των λιγότερων αστοχιών στην data cache κάτι που αποτρέπει την χρήση της L2 cache.

Αλλαγή μεγέθους μνήμης – Write Through

Γράφημα 27: L2 Shared Cache Misses / Hits - Μεγέθη μνημών L1 Instuction , L1 Data – L2 Shared

(256 | 4096)(512 | 4096)

(4096 | 4096)(256 | 8192)

(512 | 8192)(4096 | 8192)

(256 | 16384)(512 | 16384)

(4096 | 16384)

0,00E+001,00E+042,00E+043,00E+044,00E+045,00E+046,00E+047,00E+048,00E+049,00E+041,00E+05

L2 Shared Cahce Misses/Hits - Instruction, Data | L2 Size

HitsMisses

Instruction, Data | L2 Size (bytes)

Mis

ses

/ Hits

Στη συνέχεια μελετάμε τις περιπτώσεις που οι πίνακες χωράνε στις μνήμες Data και L2. Χρησιμοποιούμε πάλι Ν = 16 και τα μεγέθη μνημών: (data | L2): (4096 | 8192) , (1024 | 4096) , (256 | 1024). Επειδή το μέγεθος των δεδομένων είναι 3072 bytes στην πρώτη περίπτωση χωράνε και στις δυο μνήμες στην δεύτερη μόνο στην L2 ενώ στην τελευταία δεν χωράνε πουθενά. Χρησιμοποιώντας πολιτική εγγραφής write through, 4 – way associativity και 8 – way associativity για τις data και L2 αντίστοιχα τα αποτελέσματα ήταν ως εξής:

56

Γράφημα 28: Απόδοση - Μέγεθος μνημών L 1 Data , L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και write through

(4096 | 8192) (1024 | 4096) (256 | 1024)0

0,5

1

1,5

2

2,5

Performance - L1 Data | L2 Shared Size (N = 16)

Instruction , Data | L2 Size (bytes)

Per

form

ance

(CP

I)

Γράφημα 29: L1 Instruction cache Misses / Hits - Μέγεθος μνημών L1 Data , L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και write through

(4096 | 8192) (1024 | 4096) (256 | 1024)0,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05

1,40E+05

L1 Instruction Cache Misses/Hits - L1 Data | L2 Shared Size (N = 16)

HitsMisses

Instruction , Data - L2 Size (bytes)

Mis

ses

/ Hits

Γράφημα 30: L1 Data Cache Misses / Hits - Μέγεθος μνημών L1 Data , L2 Shared με Ν =16 (μέγεθος δεδομένων 3072) και write through

(4096 | 8192) (1024 | 4096) (256 | 1024)0,00E+00

2,00E+03

4,00E+03

6,00E+03

8,00E+03

1,00E+04

1,20E+04

1,40E+04

1,60E+04

L1 Data Cache Misses/Hits - L1 Data | L2 Shared Cache Size (N = 16)

HitsMisses

Instruction , Data | L2 Size (bytes)

Mis

ses

/ Hits

Αναμέναμε πτώση της απόδοσης λόγω των επιπλέον προσπελάσεων και αστοχιών όταν οι πίνακες δεν χωράνε σε μια cache. Επίσης όταν οι πίνακες πλέον δεν χωράνε ούτε στην L2 η πτώση της απόδοσης κορυφώνεται αφού γίνονται πολλές προσπελάσεις στην κύρια μνήμη που είναι χρονοβόρες. Αυτά τα συμπεράσματα επιβεβαιώνονται από το γράφημα 28. Η instruction cache έχει το ίδιο μέγεθος και χαρακτηριστικά με την data cache πράγμα που σημαίνει ότι όλες οι εντολές χωράνε στην μνήμη σε όλες τις περιπτώσεις και η συμπεριφορά της instruction cache είναι σταθερή και δεν έχει καμία επίδραση στην απόδοση του συστήματος. Από την άλλη στις μνήμες data και L2 έχουμε αύξηση των αστοχιών και των προσπελάσεων με μεγιστοποίηση τους στην περίπτωση όπου τα δεδομένα δεν χωράνε ούτε στην L2. Αυτό συμβαίνει για τους ίδιους λόγους που αναφέρθηκαν πιο πάνω. Λόγω της αντικατάστασης δεδομένων για την εισαγωγή καινούριων χρήσιμα δεδομένα φεύγουν από τα υψηλά επίπεδα της ιεραρχίας της μνήμης και για να ανακτηθούν προκύπτουν επιπλέον αστοχίες στις μνήμες υψηλού επιπέδου και επιπλέον προσπελάσεις στις μνήμες χαμηλότερου επιπέδου. Τα συμπερίσματα αυτά φαίνονται στα διαγράμματα 30 και 31 για τις μνήμες data και L2 ενώ η σταθερή συμπεριφορά της instruction cache φαίνεται στο γράφημα 29.

Αλλαγή μεγέθους μνήμης – Write Back

Οι παραπάνω μετρήσεις επαναλήφθηκαν με μόνη αλλαγή στην πολιτική εγγραφής από write through σε write back. Όπως και στην περίπτωση της διάταξης L1 – L2 αναμένεται μια μικρή βελτίωση της απόδοσης λόγω της απουσίας των αχρείαστων εγγραφών στην L2. Για τον ίδιο λόγο η L2 θα παρουσιάσει λιγότερες ευστοχίες και λιγότερες προσπελάσεις. Τα αποτελέσματα είναι τα παρακάτω.

58

Γράφημα 31: L2 Shared Cache Misses / Hits - Μέγεθος μνημών L1 Data , L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και write through

(4096 | 8192) (1024 | 4096) (256 | 1024)0,00E+00

1,00E+03

2,00E+03

3,00E+03

4,00E+03

5,00E+03

6,00E+03

7,00E+03

8,00E+03

L2 Shared Misses/Hits - L1 Data | L2 Shared Cache Size (N = 16)

HitsMisses

Instruction , Data | L2 Size (bytes)

Mis

ses

/ Hits

Γράφημα 32: Απόδοση - Μέγεθος μνημών L1 Data , L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και write back

(4096 | 8192) (1024 | 4096) (256 | 1024)0

0,5

1

1,5

2

2,5

Performance - L1 Data | L2 Shared Cache Size (N = 16)

Instruction , Data | L2 Size (bytes)

Per

form

ance

(CP

I)

Γράφημα 33: L1 Instruction cache Misses / Hits - Μέγεθος μνημών L1 Data , L2 shared με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back

(4096 | 8192) (1024 | 4096) (256 | 1024)0,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05

1,40E+05

L1 Instruction Cache Misses/Hits - L1 Instruction , Data | L2 Shared Size (N = 16)

HitsMisses

Instruction , Data | L2 Size (bytes)

Mis

ses

/ Hits

Πράγματι οι υποθέσεις μας επιβεβαιώνονται από τα γραφήματα των μετρήσεων. Η απόδοση είναι ελάχιστα βελτιωμένη (περίπου κατά 0,02 κύκλους ανά εντολή) η data cache δεν επηρεάζεται από την αλλαγή της πολιτικής όπως είναι αναμενόμενο ενώ στο γράφημα 35 βλέπουμε ότι πράγματι ενώ οι αστοχίες της L2 μένουν αμετάβλητες οι προσπελάσεις και οι ευστοχίες μειώνονται. Ειδικά στην περίπτωση που τα δεδομένα χωράνε στην data cache εφόσον δεν χρειάζεται να γίνει καμία αντικατάσταση στην data cache στην L2 γίνονται τόσες ευστοχίες όσες χρειάζονται για να μεταφέρουν για πρώτη φορά τα δεδομένα στην data cache. Ουσιαστικά δεν συμβαίνει καμία εγγραφή στην L2.

60

Γράφημα 34: L1 Data Cache Misses / Hits - Μέγεθος μνημών L1 Data ,L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back

(4096 | 8192) (1024 | 4096) (256 | 1024)0,00E+00

2,00E+03

4,00E+03

6,00E+03

8,00E+03

1,00E+04

1,20E+04

1,40E+04

1,60E+04

L1 Data Cache Misses/Hits - L1 Instruction , Data | L2 Shared Size (N = 16)

HitsMisses

Instruction, Data | L2 Size (bytes)

Mis

ses

/ Hits

Γράφημα 35: L2 Shared Cache Misses / Hits - Μέγεθος μνημών L1 Data ,L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back

(4096 | 8192) (1024 | 4096) (256 | 1024)0,00E+00

1,00E+03

2,00E+03

3,00E+03

4,00E+03

5,00E+03

6,00E+03

7,00E+03

8,00E+03

L2 Shared Misses/Hits - L1 Instruction , Data | L2 Shared Cache Size (N = 16)

HitsMisses

Instruction , Data | L2 Size (bytes)

Mis

ses

/ Hits

Εξομοίωση bottleneck

Όπως και παραπάνω για να μελετήσουμε την περίπτωση κάποιου bottleneck μέσα στην ιεραρχία μνήμης αυξήσαμε το latency της ανάλογης μνήμης. Στην περίπτωση αυτή οι μνήμες instruction και data έχουν τους ίδιους χρόνους προσπέλασης. Έτσι οι χρόνοι που χρησιμοποιήθηκαν ήταν: (instruction , data – L2 – main): Χωρίς bottleneck: (1 – 7 – 30), bottleneck ανάμεσα σε register file και instruction , data caches: (3 – 7 – 30), ανάμεσα σε instruction, data και L2: (1 – 21 – 30) και τέλος ανάμεσα στην L2 και την κύρια μνήμη (1 – 7 – 90). Όπως είναι λογικό και σε αυτήν την περίπτωση η επίδραση μιας πιο αργής κύριας μνήμης πρέπει να είναι λιγότερη από αυτήν αργών μνημών εντολών και δεδομένων στο υψηλότερο επίπεδο. Αυτό γίνεται σαφές με τον διαχωρισμό των εντολών από τα δεδομένα. Όπως ήδη αναφέρθηκε η μεγαλύτερη δραστηριότητα του αλγορίθμου κατά την εκτέλεση έχει να κάνει με την instruction cache. Στην συγκεκριμένη περίπτωση οι προσπελάσεις στις μνήμες instruction και data είναι 132.784 και 10.483 αντίστοιχα ενώ της L2 που είναι σε χαμηλότερο επίπεδο είναι μόλις 2406. Οι προσπελάσεις της κύριας μνήμης είναι ακόμα λιγότερες οπότε ο τριπλασιασμός του χρόνου προσπέλασης των μνημών instruction και data θα έχει μεγαλύτερο αντίκτυπο στην απόδοση από αυτόν σε κάποια άλλη μνήμη.Το γράφημα των μετρήσεων επιβεβαιώνει τα παραπάνω.

Αλλαγή μεγέθους block με σταθερό μέγεθος μνήμης

Τέλος παρατηρούμε τις επιδράσεις του σχήματος γραμμής στην διάταξη Instruction ,Data – L2. Για τις μετρήσεις κρατήσαμε σταθερά το συνολικό μέγεθος των μνημών, 1024 για την L2 και 256 για τις instruction και data caches. Αναμένουμε όπως και στην περίπτωση της προηγούμενης διάταξης η μέγιστη απόδοση να παρατηρηθεί σε κάποια ενδιάμεση κατάσταση ενώ οι ακραίες καταστάσεις που ισοδυναμούν με πολύ φαρδιές ή πολύ στενές

Γράφημα 36: Απόδοση - Bottlenecks (Χωρίς | register file - instruction - data | instruction , data – L2 | L2 - main)

1 – 7 – 30 3 – 7 – 30 1 – 21 – 30 1 – 7 – 900

0,5

1

1,5

2

2,5

3

3,5

Performance - Bottleneck

Latencies (instruction , data - L2 - main) (cycles)

Per

form

ance

(CP

I)

μνήμες να έχουν μικρότερη απόδοση. Όπως αναφέρθηκε στην περίπτωση μιας φαρδιάς μνήμης τα δεδομένα έρχονται σε μεγάλη ποσότητα αλλά στην περίπτωση κάποιας αστοχίας αφαιρούνται εξίσου πολλά δεδομένα με αποτέλεσμα να χρειαστούν να ξαναφορτωθούν στην μνήμη, ενώ στην περίπτωση της στενής μνήμης τα δεδομένα για να φορτωθούν χρειάζονται πολλές προσπελάσεις στην χαμηλότερη μνήμη με αποτέλεσμα μείωση της απόδοσης. Τα συμπεράσματα επιβεβαιώνονται από τα γραφήματα που προέκυψαν από τις μετρήσεις.Παρατηρούμε πάλι ότι εφόσον τα οι εντολές χωράνε εξολοκλήρου στην instruction cache η συμπεριφορά της παραμένει αμετάβλητη. Οι data και L2 caches από την άλλη επηρεάζονται τόσο στις αστοχίες όσο και στις προσπελάσεις για τους λόγους που ήδη αναφέρθηκαν. Από τις διαδοχικές τιμές που δόθηκαν με data cache 4 slots x 16 blocks x 4 bytes και L2 cache 4 slots x 64 blocks x 4 bytes είχαμε την βέλτιστη απόδοση και ελαχιστοποίηση της χρήσης τόσο της L2 όσο και της data cache.

62

Γράφημα 37: Απόδοση - Μέγεθος γραμμής

2 4 8 160

0,5

1

1,5

2

2,5

3

Performance - Line Size

Line Size (Memory slots)

Per

form

ance

(CP

I)

Γράφημα 38: L1 Instruction Cache Misses / Hits - Μέγεθος γραμμής

2 4 8 160,00E+00

2,00E+04

4,00E+04

6,00E+04

8,00E+04

1,00E+05

1,20E+05

1,40E+05

L1 Instruction Cache Misses/Hits - Line Size

HitsMisses

Line Size (Memory Slots)

Mis

ses

/ Hits

Γράφημα 39: L1 Data Cache Misses / Hits - Μέγεθος γραμμής

2 4 8 160,00E+00

2,00E+03

4,00E+03

6,00E+03

8,00E+03

1,00E+04

1,20E+04

1,40E+04

1,60E+04

L1 Data Cache Misses/Hits - Line Size

HitsMisses

Line Size (Memory Slots)

Mis

ses

/ Hits

64

Γράφημα 40: L2 Shared Cache Misses / Hits - Μέγεθος γραμμής

2 4 8 160

1000

2000

3000

4000

5000

6000

7000

8000

9000

10000

L2 Shared Cache Misses/Hits - Line Size

HitsMisses

Line Size (Memory Slots)

Mis

ses

/ Hits

ΠΑΡΑΡΤΗΜΑΤΑ

Παράρτημα 1: Κώδικας Java του εξομοιωτήNewGui.javapackage cachesim_pkg;import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.io.*;import java.util.*;public class Newgui extends JFrame{

private static final long serialVersionUID = 2774707088237785373L;//***************************MEMORY OPTIONS****************************private JLabel l_memLineSize = new JLabel("Memory Line Size :");private JLabel l_memLines = new JLabel("Memory # of Blocks :");private JLabel l_struct = new JLabel("Cache Structure :");private JTextField t_memLineSize = new JTextField("8",6);private JTextField t_memLines = new JTextField("2000",6);private JButton b_memBlocks = new JButton("Set");private JButton b_memLines = new JButton("Set");private JComboBox struct_choice = new JComboBox();//***************************L1 CACHE OPTIONS**************************private JLabel l_L1lines = new JLabel("L1 Cache # of Blocks");private JLabel l_L1replace = new JLabel("L1 Replacement Policy");private JLabel l_L1setSize = new JLabel("L1 Associativity");private JLabel l_L1write = new JLabel("L1 Write Policy");private JTextField t_L1lines = new JTextField("8",6);private JTextField t_L1setSize = new JTextField("1",6);private JButton b_L1lines = new JButton("Set");private JButton b_L1setSize = new JButton("Set");private JComboBox c_L1replace = new JComboBox();private JComboBox c_L1write = new JComboBox();//***************************L2 CACHE OPTIONS**************************private JLabel l_L2lines = new JLabel("L2 Cache # of Blocks");private JLabel l_L2replace = new JLabel("L2 Replacement Policy");private JLabel l_L2setSize = new JLabel("L2 Associativity");private JLabel l_L2write = new JLabel("L2 Write Policy");private JTextField t_L2lines = new JTextField("8",6);private JTextField t_L2setSize = new JTextField("1",6);private JButton b_L2lines = new JButton("Set");private JButton b_L2setSize = new JButton("Set");private JComboBox c_L2replace = new JComboBox();private JComboBox c_L2write = new JComboBox();//***************************INSTRUCTION CACHE OPTIONS*****************private JLabel l_Ilines = new JLabel("I Cache # of Blocks");private JLabel l_Ireplace = new JLabel("I Replacement Policy");private JLabel l_IsetSize = new JLabel("I Associativity");private JLabel l_Iwrite = new JLabel("I Write Policy");private JTextField t_Ilines = new JTextField("8",6);private JTextField t_IsetSize = new JTextField("1",6);private JButton b_Ilines = new JButton("Set");private JButton b_IsetSize = new JButton("Set");private JComboBox c_Ireplace = new JComboBox();private JComboBox c_Iwrite = new JComboBox();//***************************DATA CACHE OPTIONS************************private JLabel l_Dlines = new JLabel("D Cache # of Blocks");private JLabel l_Dreplace = new JLabel("D Replacement Policy");private JLabel l_DsetSize = new JLabel("D Associativity");

private JLabel l_Dwrite = new JLabel("D Write Policy");private JTextField t_Dlines = new JTextField("8",6);private JTextField t_DsetSize = new JTextField("1",6);private JButton b_Dlines = new JButton("Set");private JButton b_DsetSize = new JButton("Set");private JComboBox c_Dreplace = new JComboBox();private JComboBox c_Dwrite = new JComboBox();//***************************CONTROL OPTIONS***************************private JButton start = new JButton("Start");private JButton open = new JButton("Open...");private JButton quit = new JButton("Quit");private JButton showFile = new JButton("Show File");private JButton reset = new JButton ("Reset");//***************************OUTPUT TEXTAREAS**************************private JTextArea log = new JTextArea(20,19);private JTextArea pipeline = new JTextArea(20,19);private JTextArea CL1 = new JTextArea(15,14);private JTextArea CL2 = new JTextArea(15,14);private JTextArea CL3 = new JTextArea(15,14);private JScrollPane scrollLog = new JScrollPane(log);private JScrollPane scrollPipe = new JScrollPane(pipeline);private JScrollPane scrollL1 = new JScrollPane(CL1);private JScrollPane scrollL2 = new JScrollPane(CL2);private JScrollPane scrollL3 = new JScrollPane(CL3);

//***************************FUNCTIONAL VARIABLES**********************public Options o ;public Init a;private Container content_pane = getContentPane ();private FileHandler fHandler = new FileHandler();private JTextArea fTextArea;private JScrollPane filescroll;private JFrame fileFrame;Newgui(){

super("Cache sim");ActionEventHandler handler = new ActionEventHandler();/****************************************************************** *************************MEMORY OPTIONS*************************** ******************************************************************/

struct_choice.addItem("1. L1"); struct_choice.addItem("2. L1 + L2"); struct_choice.addItem("3. I + D + L2");

JPanel pMem = new JPanel(); JPanel pMem1 = new JPanel(); JPanel pMem2 = new JPanel(); pMem1.setLayout(new FlowLayout(FlowLayout.LEFT)); pMem2.setLayout(new FlowLayout(FlowLayout.LEFT)); pMem1.add(l_memLineSize); pMem1.add(t_memLineSize); pMem1.add(b_memBlocks); pMem1.add(l_memLines); pMem1.add(t_memLines); pMem1.add(b_memLines); pMem2.add(l_struct);

66

pMem2.add(struct_choice); b_memBlocks.addActionListener(handler); b_memLines.addActionListener(handler); struct_choice.addActionListener(handler); pMem.setLayout(new BorderLayout()); pMem.setBorder(BorderFactory.createTitledBorder("Memory Options")); pMem.add(pMem1,BorderLayout.NORTH); pMem.add(pMem2,BorderLayout.SOUTH); /****************************************************************** ************************L1 CACHE OPTIONS************************** ******************************************************************/ c_L1replace.addItem("1.FIFO"); c_L1replace.addItem("2.Random"); c_L1replace.addItem("3.LRU"); c_L1write.addItem("1.WriteThrough"); c_L1write.addItem("2.WriteBack"); JPanel pCacheL1 = new JPanel(); JPanel pCacheL11 = new JPanel(); JPanel pCacheL12 = new JPanel(); pCacheL11.setLayout(new FlowLayout(FlowLayout.LEFT)); pCacheL12.setLayout(new FlowLayout(FlowLayout.LEFT)); pCacheL11.add(l_L1lines); pCacheL11.add(t_L1lines); pCacheL11.add(b_L1lines); pCacheL11.add(l_L1replace);

pCacheL11.add(c_L1replace);pCacheL12.add(l_L1setSize);pCacheL12.add(t_L1setSize);pCacheL12.add(b_L1setSize);pCacheL12.add(l_L1write);pCacheL12.add(c_L1write);

b_L1lines.addActionListener(handler);b_L1setSize.addActionListener(handler);c_L1replace.addActionListener(handler);c_L1write.addActionListener(handler);

pCacheL1.setLayout(new BorderLayout()); pCacheL1.setBorder(BorderFactory.createTitledBorder("L1 Cache

Options")); pCacheL1.add(pCacheL11,BorderLayout.NORTH); pCacheL1.add(pCacheL12,BorderLayout.SOUTH); /****************************************************************** ************************L2 CACHE OPTIONS************************** ******************************************************************/ c_L2replace.addItem("1.FIFO"); c_L2replace.addItem("2.Random"); c_L2replace.addItem("3.LRU"); c_L2write.addItem("1.WriteThrough"); c_L2write.addItem("2.WriteBack"); JPanel pCacheL2 = new JPanel(); JPanel pCacheL21 = new JPanel(); JPanel pCacheL22 = new JPanel(); pCacheL21.setLayout(new FlowLayout(FlowLayout.LEFT));

pCacheL22.setLayout(new FlowLayout(FlowLayout.LEFT)); pCacheL21.add(l_L2lines); pCacheL21.add(t_L2lines); pCacheL21.add(b_L2lines); pCacheL21.add(l_L2replace);

pCacheL21.add(c_L2replace);pCacheL22.add(l_L2setSize);pCacheL22.add(t_L2setSize);pCacheL22.add(b_L2setSize);pCacheL22.add(l_L2write);pCacheL22.add(c_L2write);

b_L2lines.addActionListener(handler);b_L2setSize.addActionListener(handler);c_L2replace.addActionListener(handler);c_L2write.addActionListener(handler);

pCacheL2.setLayout(new BorderLayout()); pCacheL2.setBorder(BorderFactory.createTitledBorder("L2 Cache

Options")); pCacheL2.add(pCacheL21,BorderLayout.NORTH); pCacheL2.add(pCacheL22,BorderLayout.SOUTH); /****************************************************************** ************************INSTRUCTION CACHE OPTIONS***************** ******************************************************************/ c_Ireplace.addItem("1.FIFO"); c_Ireplace.addItem("2.Random"); c_Ireplace.addItem("3.LRU"); c_Iwrite.addItem("1.WriteThrough"); c_Iwrite.addItem("2.WriteBack"); JPanel pCacheI = new JPanel(); JPanel pCacheI1 = new JPanel(); JPanel pCacheI2 = new JPanel(); pCacheI1.setLayout(new FlowLayout(FlowLayout.LEFT)); pCacheI2.setLayout(new FlowLayout(FlowLayout.LEFT)); pCacheI1.add(l_Ilines); pCacheI1.add(t_Ilines); pCacheI1.add(b_Ilines); pCacheI1.add(l_Ireplace);

pCacheI1.add(c_Ireplace);pCacheI2.add(l_IsetSize);pCacheI2.add(t_IsetSize);pCacheI2.add(b_IsetSize);pCacheI2.add(l_Iwrite);pCacheI2.add(c_Iwrite);

b_Ilines.addActionListener(handler);b_IsetSize.addActionListener(handler);c_Ireplace.addActionListener(handler);c_Iwrite.addActionListener(handler);

pCacheI.setLayout(new BorderLayout()); pCacheI.setBorder(BorderFactory.createTitledBorder("Instruction Cache

Options")); pCacheI.add(pCacheI1,BorderLayout.NORTH);

68

pCacheI.add(pCacheI2,BorderLayout.SOUTH); /****************************************************************** ************************DATA CACHE OPTIONS************************ ******************************************************************/ c_Dreplace.addItem("1.FIFO"); c_Dreplace.addItem("2.Random"); c_Dreplace.addItem("3.LRU"); c_Dwrite.addItem("1.WriteThrough"); c_Dwrite.addItem("2.WriteBack"); JPanel pCacheD = new JPanel(); JPanel pCacheD1 = new JPanel(); JPanel pCacheD2 = new JPanel(); pCacheD1.setLayout(new FlowLayout(FlowLayout.LEFT)); pCacheD2.setLayout(new FlowLayout(FlowLayout.LEFT)); pCacheD1.add(l_Dlines); pCacheD1.add(t_Dlines); pCacheD1.add(b_Dlines); pCacheD1.add(l_Dreplace);

pCacheD1.add(c_Dreplace);pCacheD2.add(l_DsetSize);pCacheD2.add(t_DsetSize);pCacheD2.add(b_DsetSize);pCacheD2.add(l_Dwrite);pCacheD2.add(c_Dwrite);

b_Dlines.addActionListener(handler);b_DsetSize.addActionListener(handler);c_Dreplace.addActionListener(handler);c_Dwrite.addActionListener(handler);

pCacheD.setLayout(new BorderLayout()); pCacheD.setBorder(BorderFactory.createTitledBorder("Data Cache

Options")); pCacheD.add(pCacheD1,BorderLayout.NORTH); pCacheD.add(pCacheD2,BorderLayout.SOUTH); /****************************************************************** ************************CONTROL BUTTONS*************************** ******************************************************************/ JPanel pCtrl = new JPanel(); pCtrl.setLayout(new FlowLayout(FlowLayout.LEFT)); pCtrl.add(start); pCtrl.add(open); pCtrl.add(showFile); pCtrl.add(reset); pCtrl.add(quit); start.addActionListener(handler); open.addActionListener(handler); showFile.addActionListener(handler); reset.addActionListener(handler); quit.addActionListener(handler); pCtrl.setBorder(BorderFactory.createTitledBorder("Control Options")); /****************************************************************** ************************LEFT PANEL********************************

******************************************************************/ JPanel left = new JPanel(); left.setLayout(new BoxLayout(left,BoxLayout.Y_AXIS)); left.add(pMem); left.add(pCacheL1); left.add(pCacheL2); left.add(pCacheI); left.add(pCacheD); left.add(pCtrl); /****************************************************************** ************************OUTPUT TEXTAREAS************************** ******************************************************************/ JPanel north = new JPanel(); JPanel south = new JPanel(); north.setLayout(new BoxLayout(north,BoxLayout.X_AXIS)); south.setLayout(new BoxLayout(south,BoxLayout.X_AXIS)); JPanel plog = new JPanel(); plog.add(scrollLog); plog.setBorder(BorderFactory.createTitledBorder("Log")); JPanel ppipeline = new JPanel(); ppipeline.add(scrollPipe); ppipeline.setBorder(BorderFactory.createTitledBorder("Pipeline Status")); JPanel pcl1 = new JPanel(); pcl1.add(scrollL1); pcl1.setBorder(BorderFactory.createTitledBorder("L1 / Instruction Cache")); JPanel pcl2 = new JPanel(); pcl2.add(scrollL2); pcl2.setBorder(BorderFactory.createTitledBorder("Data Cache")); JPanel pcl3 = new JPanel(); pcl3.add(scrollL3); pcl3.setBorder(BorderFactory.createTitledBorder("L2 Cache")); north.add(plog); north.add(ppipeline); south.add(pcl1); south.add(pcl2); south.add(pcl3); /****************************************************************** ************************RIGHT PANEL******************************* ******************************************************************/ JPanel right = new JPanel(); right.setLayout(new BoxLayout(right,BoxLayout.Y_AXIS)); right.add(north); right.add(south); /****************************************************************** ************************FILE FRAME********************************

70

******************************************************************/ fileFrame = new JFrame("File");

fileFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);fileFrame.setSize(300,500);fileFrame.setLocation(900,200);fTextArea = new JTextArea(300,500);fTextArea.setEditable(false);filescroll = new JScrollPane(fTextArea);fileFrame.add (filescroll, "Center");

/****************************************************************** ************************MAIN FRAME******************************** ******************************************************************/

this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);this.setSize(1100, 700);

this.setLocation(200, 200);this.setLayout(new GridLayout());

this.add(left); this.add(right); /****************************************************************** ************************INITIAL CONDITIONS************************ ******************************************************************/ log.setEditable(false); pipeline.setEditable(false); CL1.setEditable(false); CL2.setEditable(false); CL3.setEditable(false); t_L1setSize.setEditable(false); b_L1setSize.setEnabled(false); t_L2lines.setEditable(false); b_L2lines.setEnabled(false); t_L2setSize.setEditable(false); b_L2setSize.setEnabled(false); c_L2replace.setEnabled(false); c_L2write.setEnabled(false); t_Ilines.setEditable(false); b_Ilines.setEnabled(false); t_IsetSize.setEditable(false); b_IsetSize.setEnabled(false); c_Ireplace.setEnabled(false); c_Iwrite.setEnabled(false); t_Dlines.setEditable(false); b_Dlines.setEnabled(false); t_DsetSize.setEditable(false); b_DsetSize.setEnabled(false); c_Dreplace.setEnabled(false); c_Dwrite.setEnabled(false); showFile.setEnabled(false); o = new Options(log,pipeline,CL1,CL2,CL3);

a = new Init(o);}//Constructor end

public static void main (String[] args) throws IOException{

Newgui g = new Newgui();g.setVisible(true);

}//main end

/* * ActionEventHandler: Provides all the actions * that can be accessed through the GUI. */private class ActionEventHandler implements ActionListener{

/* * actionPerformed: Gets an input an event from * the GUI and executes proportionately */public void actionPerformed(ActionEvent e){

boolean status = false;/*******************CONTROL OPTIONS***************************/

if (e.getSource() == start){try{

a.start();}catch (Exception exc){o.update ("Start interrupted");}

}else if (e.getSource() == open){

status = openFile (); if (!status) JOptionPane.showMessageDialog (null, "Error opening file!", "File Open

Error",JOptionPane.ERROR_MESSAGE);}else if (e.getSource() == showFile) showFile();else if (e.getSource() == quit) System.exit(0);else if (e.getSource() == reset){

log.setText("");pipeline.setText("");CL1.setText("");CL2.setText("");CL3.setText("");o = new Options(log,pipeline,CL1,CL2,CL3);rst();

}

/*********************MEMORY OPTIONS****************************/

else if (e.getSource() == b_memLines){set(t_memLines,1);o.memLines = Integer.valueOf(t_memLines.getText());o.update("Memory # of blocks: " + o.memLines);

}else if (e.getSource() == b_memBlocks){

set(t_memLineSize,1);o.memLineSize =

Integer.valueOf(t_memLineSize.getText());o.update("Memory Line Size: " + o.memLineSize);

}else if (e.getSource() == struct_choice){

o.structure = Integer.valueOf(String.valueOf

(((JComboBox)e.getSource()).getSelectedItem()).charAt(0));o.structure = o.structure - 48;o.update("Structure: " +

72

((JComboBox)e.getSource()).getSelectedItem());switch (o.structure){case 1:

t_L1lines.setEditable(true);b_L1lines.setEnabled(true);c_L1replace.setEnabled(true);c_L1write.setEnabled(true);t_L2lines.setEditable(false);b_L2lines.setEnabled(false);c_L2replace.setEnabled(false);c_L2write.setEnabled(false);t_Ilines.setEditable(false);b_Ilines.setEnabled(false);c_Ireplace.setEnabled(false);c_Iwrite.setEnabled(false);t_Dlines.setEditable(false);b_Dlines.setEnabled(false);c_Dreplace.setEnabled(false);c_Dwrite.setEnabled(false);break;

case 2:t_L1lines.setEditable(true);b_L1lines.setEnabled(true);c_L1replace.setEnabled(true);c_L1write.setEnabled(true);t_L2lines.setEditable(true);b_L2lines.setEnabled(true);c_L2replace.setEnabled(true);c_L2write.setEnabled(true);t_Ilines.setEditable(false);b_Ilines.setEnabled(false);c_Ireplace.setEnabled(false);c_Iwrite.setEnabled(false);t_Dlines.setEditable(false);b_Dlines.setEnabled(false);c_Dreplace.setEnabled(false);c_Dwrite.setEnabled(false);break;

case 3:t_L1lines.setEditable(false);b_L1lines.setEnabled(false);c_L1replace.setEnabled(false);c_L1write.setEnabled(false);t_L2lines.setEditable(true);b_L2lines.setEnabled(true);c_L2replace.setEnabled(true);c_L2write.setEnabled(true);t_Ilines.setEditable(true);b_Ilines.setEnabled(true);c_Ireplace.setEnabled(true);c_Iwrite.setEnabled(true);t_Dlines.setEditable(true);b_Dlines.setEnabled(true);

c_Dreplace.setEnabled(true);c_Dwrite.setEnabled(true);break;

}}

/********************L1 CACHE OPTIONS***************************/

else if (e.getSource() == b_L1lines){set(t_L1lines,1);o.cL1Lines = Integer.valueOf(t_L1lines.getText());o.update("L1 Length: " + o.cL1Lines);if (o.cL1Lines != 0){

t_L1setSize.setEditable(true);b_L1setSize.setEnabled(true);

}else{

o.setSizeL1 = 0;t_L1setSize.setText("0");t_L1setSize.setEditable(false);b_L1setSize.setEnabled(false);

}}else if (e.getSource() == b_L1setSize){

setSetSize(o.cL1Lines,t_L1setSize);o.setSizeL1 = Integer.valueOf(t_L1setSize.getText());o.update("L1 Associativity: " + o.setSizeL1);

}else if (e.getSource() == c_L1replace){

o.reppolL1 = Integer.valueOf(String.valueOf

(((JComboBox)e.getSource()).getSelectedItem()).charAt(0));o.reppolL1 = o.reppolL1 - 48;o.update("L1 Replacement Policy: " +

((JComboBox)e.getSource()).getSelectedItem());}else if (e.getSource() == c_L1write){

o.write1 = Integer.valueOf(String.valueOf

(((JComboBox)e.getSource()).getSelectedItem()).charAt(0));o.write1 = o.write1 - 48;o.update("L1 Write Policy: " +

((JComboBox)e.getSource()).getSelectedItem());}

/***********************L2 CACHE OPTIONS****************/

else if (e.getSource() == b_L2lines){set(t_L2lines,1);o.cL2Lines = Integer.valueOf(t_L2lines.getText());o.update("L2 Cache: " + o.cL2Lines);if (o.cL2Lines != 0){

t_L2setSize.setEditable(true);b_L2setSize.setEnabled(true);

}else{

o.setSizeL2 = 0;t_L2setSize.setText("0");t_L2setSize.setEditable(false);b_L2setSize.setEnabled(false);

}

74

}else if (e.getSource() == b_L2setSize){

setSetSize(o.cL2Lines,t_L2setSize);o.setSizeL2 = Integer.valueOf(t_L2setSize.getText());o.update("L2 Associativity: " + o.setSizeL2);

}else if (e.getSource() == c_L2replace){

o.reppolL2 = Integer.valueOf(String.valueOf

(((JComboBox)e.getSource()).getSelectedItem()).charAt(0));o.reppolL2 = o.reppolL2 - 48;o.update("L2 Replacement Policy: " +

((JComboBox)e.getSource()).getSelectedItem());}else if (e.getSource() == c_L2write){

o.write2 = Integer.valueOf(String.valueOf

(((JComboBox)e.getSource()).getSelectedItem()).charAt(0));o.write2 = o.write2 - 48;o.update("L2 Write Policy: " +

((JComboBox)e.getSource()).getSelectedItem());}

/********************INSTRUCTION CACHE OPTIONS************************/

else if (e.getSource() == b_Ilines){set(t_Ilines,1);o.icLines = Integer.valueOf(t_Ilines.getText());o.update("ICache: " + o.icLines);if (o.icLines != 0){

t_IsetSize.setEditable(true);b_IsetSize.setEnabled(true);

}else{

o.setSizeIc = 0;t_IsetSize.setText("0");t_IsetSize.setEditable(false);b_IsetSize.setEnabled(false);

}}else if (e.getSource() == b_IsetSize){

setSetSize(o.icLines,t_IsetSize);o.setSizeIc = Integer.valueOf(t_IsetSize.getText());o.update("ICache Associativity: " + o.setSizeIc);

}else if (e.getSource() == c_Ireplace){

o.reppolIc = Integer.valueOf(String.valueOf

(((JComboBox)e.getSource()).getSelectedItem()).charAt(0));o.reppolIc = o.reppolIc - 48;o.update("ICache Replacement Policy: " +

((JComboBox)e.getSource()).getSelectedItem());}else if (e.getSource() == c_Iwrite){

o.writeIc = Integer.valueOf(String.valueOf

(((JComboBox)e.getSource()).getSelectedItem()).charAt(0));o.writeIc = o.writeIc - 48;o.update("ICache Write Policy: " +

((JComboBox)e.getSource()).getSelectedItem());}

/******************** DATA CACHE OPTIONS *********************/

else if (e.getSource() == b_Dlines){

set(t_Dlines,1);o.dcLines = Integer.valueOf(t_Dlines.getText());o.update("DCache: " + o.dcLines);if (o.dcLines != 0){

t_DsetSize.setEditable(true);b_DsetSize.setEnabled(true);

}else{

o.setSizeDc = 0;t_DsetSize.setText("0");t_DsetSize.setEditable(false);b_DsetSize.setEnabled(false);

}}else if (e.getSource() == b_DsetSize){

setSetSize(o.dcLines,t_DsetSize);o.setSizeDc = Integer.valueOf(t_DsetSize.getText());o.update("DCache Associativity: " + o.setSizeDc);

}else if (e.getSource() == c_Dreplace){

o.reppolDc = Integer.valueOf(String.valueOf

(((JComboBox)e.getSource()).getSelectedItem()).charAt(0));o.reppolDc = o.reppolDc - 48;o.update("DCache Replacement Policy: " +

((JComboBox)e.getSource()).getSelectedItem());}else if (e.getSource() == c_Dwrite){

o.writeDc = Integer.valueOf(String.valueOf

(((JComboBox)e.getSource()).getSelectedItem()).charAt(0));o.writeDc = o.writeDc - 48;o.update("DCache Write Policy: " +

((JComboBox)e.getSource()).getSelectedItem());}

}// actionPerformed end

/* * checkData: Prevents from entering non digit characters in a

JTextField. */private boolean checkData(JTextField t){

for (int i = 0; i < t.getText().length();i++){if (!Character.isDigit(t.getText().charAt(i))) return

false;}return true;

}//checkData end

/* * set: sets the adjusted value on the JTextField also checks for

non digits. */private void set(JTextField t,int x){

if (checkData(t))adjustValue(t,x);

else t.setText("0");}// set end

/* * adjustValue:Gets as input

76

* JTextField t (Given JTextField) * int x (trim number) * the result in the JTextField is the next lesser * number (than typed number) that qualifies (typed_number % x). */private void adjustValue(JTextField t, int x){

int n;n = Integer.valueOf(t.getText());n = n - (n % x);t.setText(String.valueOf(n));

}//adjustValue end

/* * setSetSize: like adjustValue but trimmed for association needs. */private void setSetSize (int n,JTextField t){

if (checkData(t)){int m = Integer.valueOf(t.getText());while (n % m != 0){

m--;}t.setText(String.valueOf(m));

}else t.setText("0");

}// setSetSize end

/* * openFile: Opens the given file. * Stores contents of file_string. * returns boolean if successful or not. */boolean openFile () { JFileChooser fc = new JFileChooser (); fc.setDialogTitle ("Open File");

fc.setFileSelectionMode ( JFileChooser.FILES_ONLY); fc.setCurrentDirectory (new File (".")); int result = fc.showOpenDialog (content_pane); if (result == JFileChooser.CANCEL_OPTION) { return true; } else if (result == JFileChooser.APPROVE_OPTION) { o.fFile = fc.getSelectedFile (); o.update("Selected file:" + o.fFile.getPath()); showFile.setEnabled(true); o.file_string = fHandler.readFile (o.fFile); if (o.file_string != null) fTextArea.setText (o.file_string); else return false; } else{ return false; } return true; }//openFile end

/* * showFile: Displays the contents of the opened file in a JTextField. */void showFile (){

if (fileFrame.isVisible()){

fileFrame.setVisible(false);showFile.setText("Show File");

}else {

fileFrame.setVisible(true);showFile.setText("Hide File");

}}//showFile end

/* * rst: resets the settings */void rst(){

o.initAddr = 0;o.memLineSize = 8;t_memLineSize.setText(String.valueOf(8));o.memLines = 2000;t_memLines.setText(String.valueOf(2000));o.cL1Lines = 8;t_L1lines.setText(String.valueOf(8));o.cL2Lines = 8;t_L2lines.setText(String.valueOf(8));o.icLines = 8;t_Ilines.setText(String.valueOf(8));o.dcLines = 8;t_Dlines.setText(String.valueOf(8));o.setSizeL1 = 1;t_L1setSize.setText(String.valueOf(1));o.setSizeL2 = 1;t_L2setSize.setText(String.valueOf(1));o.setSizeIc = 1;t_IsetSize.setText(String.valueOf(1));o.setSizeDc = 1;t_DsetSize.setText(String.valueOf(1));o.write1 = 1;o.write2 = 1;o.writeIc = 1;o.writeDc = 1;

}

}//ActionEventHandler end}//Gui end

class Options {/* * CLASS REFERENCE * Stores all the input parameters * Provides access to a JTextField * for feedback. */public Stringfilename;

public int initAddr = 0;public int memLineSize = 8;public int memLines = 2000;public int cL1Lines = 8;public int cL2Lines = 8;public int icLines = 8;public int dcLines = 8;

78

public int cacheLevel = 0;public int reppolL1 = 1;public int reppolL2 = 1;public int reppolIc = 1;public int reppolDc = 1;public int setSizeL1 = 1;public int setSizeL2 = 1;public int setSizeIc = 1;public int setSizeDc = 1;public int write1 = 1;public int write2 = 1;public int writeIc = 1;public int writeDc = 1;public int structure = 1;File fFile;String file_string;private JTextArea log;public JTextArea pipe;public JTextArea CL1;public JTextArea CL2;public JTextArea CL3;public JScrollPane scroll;public JFrame logFrame;Options(JTextArea log,JTextArea pipe,

JTextArea CL1, JTextArea CL2, JTextArea CL3){this.log = log;this.pipe = pipe;this.CL1 = CL1;this.CL2 = CL2;this.CL3 = CL3;

}//Constructor end

/* * update: Updates the JTextField */public void update (String s){

log.insert(s + "\n",log.getCaretPosition());}

}

class Init {Options o;Memory ram;Cache L1,L2,I,D;Pipeline p;FileCompiler compiler;ArrayList<Label> l;

Init (Options options){o = options;

} public void start() throws IOException,InterruptedException{ compiler = new FileCompiler(o,o.file_string); compiler.compile(); o.initAddr = compiler.findInitAddress(); compiler.instPrepare(); ram = new Memory(o); compiler.instToMemory(ram); l = compiler.getLabels(); createCache(); createPipeline(); p.play();

}//start end /* * createCache: According to the * options cache structure choice * creates the hierarchy of Cache. */ private void createCache(){ switch(o.structure){ case 1: L1 = new Cache(o,ram,o.cL1Lines,o.memLineSize, o.setSizeL1,o.reppolL1,o.write1,"L1",o.CL1); L2 = null; I = null; D = null; break; case 2: L2 = new Cache(o,ram,o.cL2Lines,o.memLineSize, o.setSizeL2,o.reppolL2,o.write2,"L2",o.CL3); L1 = new Cache(o,L2,o.cL1Lines,o.memLineSize, o.setSizeL1,o.reppolL1,o.write1,"L1",o.CL1); I = null; D = null; break; case 3: L1 = null; L2 = new Cache(o,ram,o.cL2Lines,o.memLineSize, o.setSizeL2,o.reppolL2,o.write2,"L2",o.CL3); I = new Cache(o,L2,o.icLines,o.memLineSize, o.setSizeIc,o.reppolIc,o.writeIc,"Instruction",o.CL1); D = new Cache(o,L2,o.dcLines,o.memLineSize, o.setSizeDc,o.reppolDc,o.writeDc,"Data",o.CL2); break; default: o.update("Failed creating Cache Structure invalid input"); break; } }//createCache end /* * createPipeline: * according to the Hierarchy of cache * creates the pipeline. */ void createPipeline(){ switch(o.structure){ case 1: p = new Pipeline(o,L1,l,o.pipe); break; case 2: p = new Pipeline(o,L1,l,o.pipe); break; case 3: p = new Pipeline(o,I,D,l,o.pipe); break; default: } }//createPipeline end}

80

FileCompiler.java:

package cachesim_pkg;import java.util.*;

public class FileCompiler {int iupdate,initAddr;public Options o;//private JTextArea a;private String data;private ArrayList<Label> labels;private ArrayList<Instruction> instructions;/* * Constructor: Needs a string that contains * the data from the opened file. Needs the options * for getting information about parameters and * providing feedback. */FileCompiler(Options options,String d){

o = options;data = d;//a = area;labels = new ArrayList<Label>();instructions = new ArrayList<Instruction>();

}//Constructor end

/* * compile: compiles the opened file * finds labels and instructions and * stores them in the two ArrayLists */void compile(){

String temp_string1,temp_string2,temp_string3;int i = 0,label_counter=0,inst_counter=0;o.update("#Compiling...");

while (i < data.length()){i = nextChar(i);if (i >= data.length())break;temp_string1 = storeString(i);i = iupdate;if (data.charAt(i) != '\n'){

i = nextChar(i);

if (i >= data.length())break;temp_string2 = storeString(i);i = iupdate;if (data.charAt(i) != '\n'){

i = nextChar(i);if (i >= data.length())break;temp_string3 = storeString(i);i = iupdate;temp_string1 = trimStringEnd(temp_string1);labels.add(new

Label(inst_counter,temp_string1));label_counter++;Instruction temp_Inst = new Instruction();temp_Inst.setName(temp_string2);temp_Inst.pos = inst_counter;inst_counter++;temp_Inst.arguments =

storeArgs(temp_string3);instructions.add(temp_Inst);

}else{Instruction temp_Inst = new Instruction();temp_Inst.setName(temp_string1);temp_Inst.pos = inst_counter;inst_counter++;temp_Inst.arguments =

storeArgs(temp_string2);instructions.add(temp_Inst);

}}else{

Instruction temp_Inst = new Instruction();temp_Inst.setName(temp_string1);temp_Inst.pos = inst_counter;inst_counter++;instructions.add(temp_Inst);

}}o.update("#Done!");

}//compile end/* * instPrepare: Finds the opcodes of the * instructions in the instructiontable * and updates the each instruction's opcode. */void instPrepare (){

InstructionTable instTable = new InstructionTable(o);Instruction temp_inst;o.update("#Initializing instruction opcodes...");for (int i = 0; i < instructions.size();i++){

temp_inst = instructions.get(i);temp_inst.opcode =

instTable.findInstOpcode(temp_inst.getName());temp_inst.initOperands();

}o.update("#Done!");

}//instPrepare end/* * findInitAddress: Searches through * instructions and finds org. Stores the * initial address. If org not found * it sets the initial address to 0. *

82

*/int findInitAddress(){

int initAddress = 0;o.update("#Locating initial address...");for (int i = 0; i < instructions.size();i++){

if (instructions.get(i).getName().matches("org")){initAddress =

Integer.valueOf(instructions.get(i).arguments.get(0));o.update("#Done!");initAddr = initAddress;instructions.remove(i);labelSync();break;

}}if (initAddress == 0){

o.update("#No initial address found!");o.update("#Setting initial address to 0...");o.update("#Done!");

}return initAddress;

}//findInitAddress end/* * labelSync: Synchronizes label and instruction positions * according to initial address. */void labelSync(){

o.update("#Syncronizing labels...");for (int i = 0;i < labels.size();i++){

labels.get(i).setPos(labels.get(i).getPos() + initAddr -1);}o.update("#Done!");o.update("#Synchronizing instructions...");for (int i = 0;i < instructions.size();i++){

instructions.get(i).pos = instructions.get(i).pos + initAddr -1;}o.update("#Done!");

}//labelSync end/* * instToMemory: copies the instructions * in the main memory */void instToMemory(Memory memory){

o.update("#Storing instruction into memory...");for (int i = 0;i < instructions.size();i++){

memory.store(instructions.get(i), initAddr + i);}o.update("#Done!");

}//instToMemory end/* * storeString: Starting from a character * and ending in ' ' , '\n' or '\t' returns * the string between. */String storeString(int n){

String temp;temp = String.valueOf(data.charAt(n));n++;if ((data.charAt(n) == ' ')||

(data.charAt(n) == '\t')||(data.charAt(n) == '\n')){

iupdate = n;return temp;

}while ( (data.charAt(n) != ' ')&&

(data.charAt(n) != '\t')&&(data.charAt(n) != '\n')){

temp = temp + data.charAt(n);n++;iupdate = n;if (n >= data.length())return temp;

}return temp;

}//storeString end/* * storeArgs: Stores the arguments of * an instructions in an ArrayList. Takes * as input a string and the output is an ArrayList * with strings the string found separated by a comma * in the given string. */ArrayList<String> storeArgs(String s){

String temp = "";ArrayList<String> ret = new ArrayList<String>();int i = 0;for (int n = 0; n < s.length();n++){

if (s.charAt(n)!= ','){temp = temp + s.charAt(n);

}else{ret.add(temp);i++;temp = String.valueOf(s.charAt(++n));

}}ret.add(temp);return ret;

}//storeArgs end/* * nextChar: Skips all the ' ', '\n', and '\t' * so that the pointer n "sees" a character. */int nextChar(int i){

while ( (data.charAt(i) == ' ')||(data.charAt(i) == '\t')||(data.charAt(i) == '\n')){i++; if(i >=

data.length())break;}return i;

}//nextChar end/* * getLabels: returns the ArrayList of * labels to be used later in the pipeline. * (for instruction execution) */ArrayList<Label> getLabels (){

return labels;}//getLabels end/* * display: used to display the found labels * and instructions with their arguments in a * JTextArea. Not used. */void display(){

//update("LABELS");for (int i = 0; i < labels.size();i++){

84

System.out.println(labels.get(i).getName());//update(labels.get(i).getName());

}//update("INSTRUCTIONS");for (int i = 0; i< instructions.size();i++){

System.out.println(instructions.get(i).getName());//update(instructions.get(i).name);String temp = "";for (int j= 0; j< instructions.get(i).arguments.size();j++){

temp = temp + instructions.get(i).arguments.get(j) + ",";

}System.out.println(temp);//update(temp);

}}//display end/* * update: Displays the given string in the * JTextArea. Not used. */public void update (String s){

//a.insert(s + "\n",a.getCaretPosition());}//update end/* * trimStringEnd: * removes last char from the given string * Used for storing labels properly * (without the ending ':'. */String trimStringEnd (String s){

String temp = ""; char c[] = s.toCharArray();

for (int i = 0;i < s.length() - 1;i++)temp = temp + c[i];

return temp; }}

FileHandler.java

package cachesim_pkg;import java.io.*;public class FileHandler {

public String readFile (File file) { StringBuffer fileBuffer; String fileString=null; String line;

try { FileReader in = new FileReader (file); BufferedReader dis = new BufferedReader (in); fileBuffer = new StringBuffer () ; while ((line = dis.readLine ()) != null) { fileBuffer.append (line + "\n"); } in.close (); fileString = fileBuffer.toString ();

} catch (IOException e ) { return null; } return fileString;}

public static boolean writeFile (File file, String dataString) { try { PrintWriter out = new PrintWriter (new BufferedWriter (new FileWriter (file))); out.print (dataString); out.flush (); out.close (); } catch (IOException e) { return false; } return true; }}

Instruction.java

package cachesim_pkg;import java.util.ArrayList;public class Instruction{

private String name;int pos,opcode;ArrayList<String> arguments;Operand [] operands;

Instruction(){name = null;opcode = 0;pos = 0;arguments = new ArrayList<String>();

}Instruction(int op,String name){

this.name = name;opcode = op;operands = new Operand[3];operands[0] = null;

}

String getName(){return name;

}int getOpcode(){

return opcode;}void setName(String s){

name = s;}void setOpcode(int n){

opcode = n;}

86

void setOperands(Operand[] ops){for (int i=0; i < ops.length;i++)

operands[i] = ops[i];}

Operand[] getOperands(){return operands;

}void initOperands(){

String temp_string;operands = new Operand [3];switch (opcode){case 1://add

for (int i = 0; i < arguments.size(); i++){temp_string = trimStringStart(arguments.get(i));operands[i] = new Operand(temp_string,0);}

break;case 2://bne

for (int i = 0; i < arguments.size() - 1; i++){temp_string = trimStringStart(arguments.get(i));operands[i] = new Operand(temp_string,0);}temp_string = arguments.get(2);operands[2] = new Operand(temp_string,1);

break;case 3://j

for (int i = 0; i < arguments.size(); i++){temp_string = arguments.get(i);operands[i] = new Operand(temp_string,1);}

break;case 4://jal

for (int i = 0; i < arguments.size(); i++){temp_string = arguments.get(i);operands[i] = new Operand(temp_string,1);}

break;case 5://lw

temp_string = trimStringStart(arguments.get(0));operands[0] = new Operand(temp_string,0);temp_string = memoryTrimValue(arguments.get(1));operands[1] = new Operand(temp_string,2);temp_string = memoryTrimRegister(arguments.get(1));operands[2] = new Operand(temp_string,0);

break;case 6://mflo

for (int i = 0; i < arguments.size(); i++){temp_string = trimStringStart(arguments.get(i));operands[i] = new Operand(temp_string,0);}

break;case 7://move

for (int i = 0; i < arguments.size(); i++){temp_string = trimStringStart(arguments.get(i));operands[i] = new Operand(temp_string,0);}

break;case 8://mult

for (int i = 0; i < arguments.size(); i++){temp_string = trimStringStart(arguments.get(i));

operands[i] = new Operand(temp_string,0);}break;case 9://nopbreak;case 10://sll

for (int i = 0; i < arguments.size() - 1; i++){temp_string = trimStringStart(arguments.get(i));operands[i] = new Operand(temp_string,0);}temp_string = arguments.get(2);operands[2] = new Operand(temp_string,3); break;

case 11://sltfor (int i = 0; i < arguments.size() - 1; i++){temp_string = trimStringStart(arguments.get(i));operands[i] = new Operand(temp_string,0);}temp_string = arguments.get(2);operands[2] = new Operand(temp_string,3);

break;case 12://sw

temp_string = trimStringStart(arguments.get(0));operands[0] = new Operand(temp_string,0);temp_string = memoryTrimValue(arguments.get(1));operands[1] = new Operand(temp_string,2);temp_string = memoryTrimRegister(arguments.get(1));operands[2] = new Operand(temp_string,0);

break;case 13:

temp_string = trimStringStart(arguments.get(0));operands[0] = new Operand(temp_string,0);temp_string = trimStringStart(arguments.get(1));operands[1] = new Operand(temp_string,0);temp_string = arguments.get(2);operands[2] = new Operand(temp_string,3);

break;case 14://addi

for (int i = 0; i < arguments.size() - 1; i++){temp_string = trimStringStart(arguments.get(i));operands[i] = new Operand(temp_string,0);}temp_string = arguments.get(2);operands[2] = new Operand(temp_string,3);

break;case 15://be

for (int i = 0; i < arguments.size() - 1; i++){temp_string = trimStringStart(arguments.get(i));operands[i] = new Operand(temp_string,0);}temp_string = arguments.get(2);operands[2] = new Operand(temp_string,1);

break;default:}

}/* * trimStringStart: * removes first char from string */String trimStringStart (String s){

String temp = ""; char c[] = s.toCharArray();

88

for (int i = 1;i <= s.length() - 1;i++)temp = temp + c[i];

return temp; }

/* * memorytrimeValue: * fetches the value from * value(register) */String memoryTrimValue(String s){

int n = 0;String temp = "";while (s.charAt(n) != '('){

temp = temp + s.charAt(n);n++;

}return temp;

}/* * memoryTrimRegister: * fetches the register from * value(register) */String memoryTrimRegister(String s){

int n = 0;String temp = "";while (s.charAt(n) != '('){

n++;}n++;while (s.charAt(n) != ')'){

temp = temp + s.charAt(n);n++;

}temp = trimStringStart(temp);return temp;

}}

class Operand{/*/ * CLASS REFERENCE */

private String data; public int info; // boolean isRegister = false,isLabel = false,isValueAddr = false,isValueImm = false; /*/ * Constructor used generally for all operands * Finds out whether the operand is referencing to memory address * or immediate data */ /* * info: * 0 : register * 1 : label * 2 : value (address) * 3 : value (immediate) */ Operand (String s,int info){ this.info = info; this.data = s; }

String getData(){ return data; } void setData (String value){ data = value; } /*/ * printAll : prints all operand's fields on the log * Used for debugging */ /* void printAll(Options o){ o.update( "Name: " + name); o.update("Data: " + data); if (address != 0) o.update("Address: " + address); else o.update("Address: No Address Stored"); o.update("========="); } */ /* * trimStringEnd: removes any non-digit characters from the end of a String * Used by the class constructor in the case of immediate data */ /* String trimStringEnd (String s){ String temp = ""; char c[] = s.toCharArray();

for (int i = 0;i < s.length() - 1;i++)temp = temp + c[i];

return temp; } */ /* * trimStringStart : removes any non-digit characters from he beginning of * a String. Used by the class constructor in the case of register table register */ /* String trimStringStart (String s){ String temp = ""; char c[] = s.toCharArray();

for (int i = 1;i <= s.length() - 1;i++)temp = temp + c[i];

return temp; } */ /* Operand (String s){ if (Character.isDigit(s.charAt(0))){ if (Character.isDigit(s.charAt(s.length()-1))){ name = -1; data = 0; address =Integer.valueOf(s); } else{ s = trimStringEnd(s); name = -2; data = Integer.valueOf(s); address = 0; } }

90

else{ s = trimStringStart(s); name = Integer.valueOf(s); data = 0; address = 0; } } */ }

class Label{private int pos;private String label;Label(int i,String name){

pos = i;label = name;

}void setPos(int i){

pos =i;}void setName(String name){

label = name;}int getPos(){

return pos;}String getName(){

return label;}

}InstructionTable.java

package cachesim_pkg;public class InstructionTable {

/*/ * CLASS REFERENCE * instTable : contains the instruction set that the * simulator supports * options : used to access data and print information * on the GUI */

private Instruction instTable[] = new Instruction[40]; public Options o;//Constructors /*/ * Constructor that creates a table containing * the opcodes for each instruction in the * instruction set. */ InstructionTable(Options options){ o = options; o.update("#Building instruction table..."); instTable[1] = new Instruction(1,"add"); instTable[2] = new Instruction(2,"bne"); instTable[3] = new Instruction(3,"j"); instTable[4] = new Instruction(4,"jal"); instTable[5] = new Instruction(5,"lw"); instTable[6] = new Instruction(6,"mflo"); instTable[7] = new Instruction(7,"move"); instTable[8] = new Instruction(8,"mult");

instTable[9] = new Instruction(9,"nop"); instTable[10] = new Instruction(10,"sll"); instTable[11] = new Instruction(11,"slt"); instTable[12] = new Instruction(12,"sw"); instTable[13] = new Instruction(13,"sub"); instTable[14] = new Instruction(14,"addi"); instTable[15] = new Instruction(15,"be"); instTable[16] = new Instruction(16,""); instTable[17] = new Instruction(17,""); instTable[18] = new Instruction(18,""); instTable[19] = new Instruction(19,""); instTable[20] = new Instruction(20,""); instTable[21] = new Instruction(21,""); instTable[22] = new Instruction(22,""); instTable[23] = new Instruction(23,""); instTable[24] = new Instruction(24,""); instTable[25] = new Instruction(25,""); instTable[26] = new Instruction(26,""); instTable[27] = new Instruction(27,""); instTable[28] = new Instruction(28,""); instTable[29] = new Instruction(29,""); instTable[30] = new Instruction(30,""); instTable[31] = new Instruction(31,"end"); instTable[32] = new Instruction(32,"org"); o.update("#Instruction Table Built"); }//Constructor end /*/ * findInstOpcode: searches through the instruction table * to find the opcode for the given instruction. Used to * find the opcodes of the instruction read from the code * file. */ int findInstOpcode(String name){ int opcode = 0; for (int i=1;i<=32;i++){ if (instTable[i].getName().matches(name)) opcode = instTable[i].getOpcode(); } return opcode; }//findInstOpcode end}//InstructionTable end

Memory.java

package cachesim_pkg;public class Memory {

/*/ * CLASS REFERENCE * Variables: * length : the number of lines * width : the number of blocks in each line * size : the number of available addresses * table : contains MemoryBlocks representing a memory * block (memory line) * options : used to access data and * print information on the GUI */private int length,width,size;

92

private int latency;private MemoryBlock [] table;private Options options;Memory(){

options = null;length = 0;width = 0;size = 0;table = null;

}//Constructor end

Memory (Options o){options = o;options.update("#Creating RAM...");length = options.memLines;width = options.memLineSize;size = length*width;latency = 30;table = new MemoryBlock[length];for (int i = 0;i < length;i++){

table[i] = new MemoryBlock(width);}

}//Constructor end

/*/ * store: stores an instruction in a MemoryVector * defined by the given address */public void store (Instruction inst,int address){

int offset,line;if (address > size){

options.update("Memory out of bounds. Address: " + address); }

else{line = address / width;offset = address % width;table[line].store(inst, offset);

}}//store end

/*/ * store: stores data in a MemoryVector * defined by the given address */public void store (int data,int address){

int offset,line;if (address > size){

options.update("Memory out of bounds. Address: " + address); }

else{line = address / width;offset = address %width;table[line].store(data,offset);

}}//store end

/*/ * access: returns the MemoryVector * defined by the given address */public MemoryVector access(int address){

MemoryVector vector;int offset,line;

if (address > size){options.update("Memory out of bounds. Address: " + address);vector = null;

}else{

line = address / width;offset = address % width;vector = table[line].access(offset);

}return vector;

}//access end

/*/ * accessBlock : returns the MemoryBlock * defined by the given address */public MemoryBlock accessBlock(int address) {

MemoryBlock block = new MemoryBlock(width);int line;if (address > size){

options.update("Memory out of bounds. Address: " + address);block = null;

}else{

line = address / width;copyBlock(block,table[line]);block = table[line];

}return block;

}//accessBlock end

public void storeBlock (int address, MemoryBlock block){int line;if (address > size){

options.update("Memory out of bounds. Address: " + address);}else{

line = address / width;copyBlock(table[line],block);

}}//storeBlock endpublic int getDelay(){

return latency;}

void copyBlock(MemoryBlock block1,MemoryBlock block2){for (int i =0; i < block1.vector.length; i++){

copyVector(block1.vector[i],block2.vector[i]);}

}

void copyVector(MemoryVector vector1,MemoryVector vector2){vector1.inst = vector2.inst;vector1.data = vector2.data;vector1.hasData = vector2.hasData;vector1.hasInst = vector2.hasInst;

}

}//Memory end

class MemoryBlock{

94

/*/ * CLASS REFERENCE * vector: contains MemoryVectors * representing 32bit of data (data block) * dirty : is used for write back and * write though in cache memory */MemoryVector[] vector;private boolean dirty;MemoryBlock (int d){

vector = new MemoryVector[d];for (int i = 0;i < d;i++){

vector[i] = new MemoryVector();}dirty = false;

}//Constructor end

void store (Instruction inst,int offset){vector[offset].setInst(inst);

}//store end

void store (int data,int offset){vector[offset].setData(data);

}//store end

MemoryVector access(int offset){return vector[offset];

}//access end

boolean isDirty(){return dirty;

}//isDirty end

void setDirty(boolean s){dirty = s;

}//setDirty end

}//MemoryBlock end

class MemoryVector{/*/ * CLASS REFERENCE * inst : contains the stored instruction * data : contains the stored data * hasData : defines whether the * MemoryVector has data or instruction stored */

Instruction inst;int data,tag;boolean hasData,hasInst;MemoryVector (){

inst = null;data = 0;hasData = false;hasInst = false;tag = -1;

}//Constructor end

void setInst(Instruction instruction){inst = instruction;hasData = false;hasInst = true;

}//setInst end

void setData(int d){data = d;hasData = true;hasInst = false;

}//setData end

Instruction getInst(){return inst;

}//getInst end

int getData(){return data;

}//getData end

boolean hasData(){return hasData;

}//hasData end

boolean hasInst(){return hasInst;

}//hasInst end

void setTag (int tag){this.tag = tag;

}//setTag endint getTag(){

return tag;}

}//MemoryVector end

Cache.java

package cachesim_pkg;import java.util.*;import javax.swing.*;public class Cache extends Memory{

/*/ * CLASS REFERENCE * length : the number of lines * width : the number of blocks in each line * size : the number of available addresses * assoc : the numbers of sets contained in the memory * setsizeV : the size of a single set in CacheVectors * setsizeB : the size of a single set in CacheBlocks * local : the algorithm used when removing a CacheBlock * write : defines the main memory update method when new * data are stored in cache, either write through or write

back * level : defines the hierarchy level of current cache memory * delay : in case of a miss, the delay to access lower level memory

96

* stats : contains the matrixes that keep the statistics * used to find the CacheBlock to be replaced when an

update * is needed, according to the replacing algorithm * table : contains CacheBlocks representing a cache * block (memory line) * options : used to access data and print information on the GUI * memory : the main memory linked with the cache */

private int length,width,size,setSize,setsizeV,setNum,local,write;private int latency,delay;private int index,hash,tag,line,offset;private int misses,hits,access,writebacks;private boolean found;private MemoryBlock[] table;private setStats stats;private Options options;private Memory memory;private String name;private JTextArea area;Cache (Options o,Memory m,int l,int w,

int associativity,int locality,int writePol,String s,JTextArea output){

options = o;name = s;options.update("#Creating " + name + " Cache...");length = l;width = w;size = length*width;setSize = associativity;setsizeV = setSize * width;setNum = length / setSize;local = locality;write = writePol;area = output;stats = new setStats(setNum,setSize,local);table = new MemoryBlock[length];memory = m;for (int i = 0;i < length;i++){

table[i] = new MemoryBlock(width);}if (name.matches("L2"))latency = 7;else latency = 1;o.update("#Done!");

}//Constructor end

@Overridepublic void store (int data, int address){

delay = 0;setCurrent(address);searchSet();

if (found){hits++;stats.updateLRU(index,line);memoryWrite(data,address);delay = latency;

}else {

memoryMiss(address);access++;hits++;searchSet();memoryWrite(data,address);

}update(area);

}//store end

@Overridepublic MemoryVector access (int address){

MemoryVector vector = new MemoryVector();delay = 0;setCurrent(address);searchSet();

if (this.name.matches("L1"));if (found){

hits++;stats.updateLRU(index,line);copyVector(vector,table[line].access(offset) );delay = latency;

}else {

memoryMiss(address);access++;hits++;searchSet();copyVector(vector,table[line].access(offset) );delay = latency + memory.getDelay();

}update(area);return vector;

}//access end @Overridepublic MemoryBlock accessBlock(int address) {

MemoryBlock block = new MemoryBlock(width);delay = 0;setCurrent(address);searchSet();

if (found){hits++;stats.updateLRU(index,line);copyBlock(block,table[line]);delay = latency;

}else {

memoryMiss(address);access++;hits++;searchSet();copyBlock(block,table[line]);delay = latency + memory.getDelay();

98

}update(area);return block;

}//accessBlock end

@Overridepublic void storeBlock (int address, MemoryBlock block){

delay = 0;setCurrent(address);searchSet();if (found){

hits++;stats.updateLRU(index,line);copyBlock(table[line],block);updateTags(address,line);delay = latency;

}else {

memoryMiss(address);access++;hits++;searchSet();copyBlock(table[line],block);updateTags(address,line);delay = latency + memory.getDelay();

}update(area);

}//storeBlock end

/*/ * searchSet : Searches through a set number for a tag. */private void searchSet (){

found = false;for (int address = setsizeV*index; address <

setsizeV*(index+1);address++){line = address / width;offset = address % width;if (table[line].access(offset).getTag() == tag){

found = true;break;

}}

}//searchSet end

/*/ * updateCache : used to replace a cache block with * another from memory in case of a cache miss * It creates the vectors' tags too. */public void updateCache (int address,int repLine){

MemoryBlock Mblock = new MemoryBlock(width);/*if (index == 0){

for (int i = 0; i < setSize;i++){System.out.print("Addresses@ " + i + ": ");if (table[i].access(0).hasData){

int addr = getReplaceAddr(i);for (int j = 0; j < width;j++){

System.out.print(addr + "\t");addr++;

}

}System.out.println("");

}}*/

Mblock = memory.accessBlock(address);copyBlock(table[repLine],Mblock);updateTags(address,repLine);

}//updateCache end

/*/ * updateMemory : used to replace a memory block with * another from cache in case of a write back. */private void updateMemory (int address,int repLine){

MemoryBlock Mblock = new MemoryBlock(width);copyBlock(Mblock,table[repLine]);memory.storeBlock(address, Mblock);

}//updateMemory end

private int calcTag(int address){int tag;tag = (address % setsizeV) + (address / size)*setsizeV;return tag;

}//calcTag end

@Overridepublic int getDelay(){

return delay;}

private void setCurrent(int address){access++;hash = address % size;index = hash / setsizeV;tag = calcTag(address);

}//setCurrent end

private int getReplaceAddr(int line){int address = 0,tag;int firsttag;boolean found = false;Integer[] firstAddr = new Integer[setSize];firstAddr[0] = setsizeV * index;for (int i = 1; i < setSize; i++){

firstAddr[i] = firstAddr [i-1] + width;}tag = table[line].access(0).getTag();

while (!found){for (int i = 0; i < setSize;i++){

firsttag = calcTag(firstAddr[i]);

if (firsttag == tag){found = true;address = firstAddr[i];break;

100

}firstAddr[i] = firstAddr[i] + size;

}}return address;

}//getReplaceAddr end

/* * memoryMiss: executes a memory miss function */private void memoryMiss(int address){

int repLine,repAddr;misses++;repLine = stats.update(index);repLine = repLine + (index*setSize);

if (table[repLine].isDirty()){repAddr = getReplaceAddr(repLine);updateMemory(repAddr,repLine);writebacks++;table[repLine].setDirty(false);

}updateCache(address,repLine);

}//memoryMiss end

/* * memoryWrite: executes a store function */private void memoryWrite(int data,int address){

table[line].store(data,offset);switch (write){case 1:

updateMemory(address,line);delay = latency + memory.getDelay();break;

case 2:table[line].setDirty(true);delay = latency;break;

}}//memoryWrite end

/* * updateTags: updates new block's tags */private void updateTags(int address,int line){

int tag,lineFirstAddr;lineFirstAddr = address - (address % width);for (int i = 0;i < width;i++){

tag = calcTag(lineFirstAddr);this.table[line].access(i).setTag(tag);lineFirstAddr++;

}}//updateTags end

/* * update: updates the cache JTextArea */private void update(JTextArea area){

String s;s = "Misses : " + misses + '\n' +

"Hits : " + hits + '\n' +

"Accesses : " + access + '\n' +"Writebacks : " + writebacks + '\n';

area.setText(s);}//update end

}//Cache end

class setStats{Integer staTable[][],LFUTable[][];boolean fillTable[];Random rand = new Random(47);int setsize,local;

setStats(int assoc,int setsizeB,int l){setsize = setsizeB;local = l;staTable = new Integer [assoc][setsize];LFUTable = new Integer [assoc][setsize];fillTable = new boolean [assoc];for (int i = 0; i < assoc; i++){

fillTable[i] = true;for (int j = 0;j < setsize;j++){

staTable[i][j] = 0;LFUTable[i][j] = 0;

}}

}//Constructor end

/*/ * update : Finds the line number of the cache that * should be updated from the main memory. If there * are still empty CacheBlocks they are the next to * update, if not the algorithm used to find next is * selected. It also updates the cache statistics for the * next update. */int update(int index){

int position = -1;if (fillTable[index]){

position = findPosition(index);for (int i = 0; i < setsize;i++){

if (staTable[index][i] != 0)staTable[index][i]++;

}staTable[index][position]++;updateLRUinside(index,position);updateFillTable(index);

}else{

switch(local){case 1:

position = maxPosition(index);staTable[index][position] = 0;for (int i = 0; i < setsize;i++){

staTable[index][i]++;}

break;case 2:

position = rand.nextInt(setsize);break;

102

case 3:position = maxLRUPosition(index);LFUTable[index][position] = 0;updateLRUinside(index,position);if (index == 0){

for (int i = 0; i < setsize; i ++){System.out.print(LFUTable[index][i] + "\t");

}System.out.println("");

}break;}

}return position;

}//update end

/*/ * findPosition : Finds a random empty position * in the given index */int findPosition(int index){

int position = 0;do{

position = rand.nextInt(setsize);}while (staTable[index][position] != 0);return position;

}//findPosition

/*/ * maxPosition : Finds the max value in the * column of the staTable that corresponds * to the given index. */int maxPosition(int index){

int max = 0,position = 0;for (int i =0; i < setsize;i++){

if (staTable[index][i]> max){max = staTable[index][i];position = i;

}}return position;

}//maxPosition end

/* * minPosition: Finds the min value in the * column of the LFUTable that corresponds * to the given index */int maxLRUPosition(int index){

int max = 0,position = 0;for (int i = 0; i < setsize; i++){

if (LFUTable[index][i] > max){max = LFUTable[index][i];position = i;

}}return position;

}

/*/ * updateFillTable : Checks if there are still * empty slots in the column of the staTable of

* the given index. If found the fillTable is marked * true else it is marked false. */void updateFillTable(int index){

fillTable[index] = false;for (int i =0; i < setsize;i++){

if (staTable[index][i] == 0){fillTable[index] = true;break;

}}

}//updateFillTable end

/* * updateLFU: updates the LFU table */void updateLRU(int index,int line){

line = line - setsize*index;for (int i = 0; i < setsize;i++){

LFUTable[index][i]++;}LFUTable[index][line]--;

}//updateLRU end

void updateLRUinside(int index,int line){for (int i = 0; i < setsize;i++){

LFUTable[index][i]++;}LFUTable[index][line]--;

}}//setStats end

Pipeline.java

package cachesim_pkg;import java.util.concurrent.*;import java.util.*;import javax.swing.*;public class Pipeline {

private Options o;private Cache c1,inst,data;private ArrayList<Label> l;

104

private boolean cache_mode;private Synchronizer sync;private Processor proc;private ExecutorService exec;private String feed,status;private int run_cycles,instructions;private float cpi;private JTextArea area;Pipeline(Options options,Cache cache,

ArrayList<Label> labels,JTextArea area){o = options;o.update("#Initializing pipeline...");c1 = cache;l = labels;cache_mode = false;o.update("#Done!");proc = new Processor(l);proc.initialize(o.initAddr);sync = new Synchronizer();exec = Executors.newCachedThreadPool();this.area = area;run_cycles = 0;instructions = 0;cpi = 0;

}//Constructor 1 end

Pipeline (Options options,Cache instructionC,Cache dataC,ArrayList<Label> labels,JTextArea area){

o = options;o.update("#Initializing pipeline...");inst = instructionC;data = dataC;l = labels;cache_mode = true;o.update("#Done!");proc = new Processor(l);proc.initialize(o.initAddr);sync = new Synchronizer();exec = Executors.newCachedThreadPool();this.area = area;run_cycles = 0;instructions = 0;cpi = 0;

}//Constructor 2 end

/* * play: starts the execution of the four pipeline threads. */public void play() throws InterruptedException{

o.update("#Starting execution...");exec.execute(new Fetch(sync));exec.execute(new Decode(sync));exec.execute(new Execute(sync));exec.execute(new WriteBack(sync));while(!proc.shutdown){

if (proc.shutdown){if ((proc.stall == 0) &&(proc.current_writeaddress == -1))exec.shutdown();

}}

}//play end

class Processor {private Register programCounter = new Register(0);private Register registerTable[] = new Register[33];private Register flo = new Register(0);private Register fhi = new Register(0);private Register rd = new Register(0);private Register rs = new Register(0);private Register rt = new Register(0);private Register prerd = new Register(0);private Register prers = new Register(0);private Register prert = new Register(0);private Register current_rd = new Register(0);private Register current_rs = new Register(0);private Register current_rt = new Register(0);private Register write_rd = new Register(0);private Register write_rs = new Register(0);private Register write_rt = new Register(0);private Instruction fetch_inst = new Instruction();private Instruction decode_inst = new Instruction(9,"nop");private Instruction preexecute_inst = new Instruction(9,"nop");private Instruction execute_inst = new Instruction(9,"nop");private int writeaddress = -1;private int current_writeaddress = -1;private ArrayList<Label> labels;private boolean shutdown = true;private boolean end = false;private int stall = 0;private int rawar = 0;private int stores = 0;private int loads = 0;Processor(ArrayList<Label> l){

o.update("#Creating processor...");labels = l;o.update("#Done!");

}//Constructor end

public void initialize(int initAddr){o.update("#Initializing processor...");programCounter.setValue(initAddr);for (int i = 0;i < 33; i++){

registerTable[i] = new Register(0);registerTable[i].setReference(-1);

}o.update("#Done!");

}//initialize end

/******************DECODE**********************************/

void decode (Instruction inst){switch (inst.getOpcode()){

case 1://add rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(registerTable[operandValue(1,inst)].getValue()); rs.setReference(operandValue(1,inst)); rt.setValue(registerTable[operandValue(2,inst)].getValue());

106

rt.setReference(operandValue(2,inst)); conflictStall(); break; case 2://bne rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(registerTable[operandValue(1,inst)].getValue()); rs.setReference(operandValue(1,inst)); rt.setValue(labelSearch(inst.operands[2].getData())); conflictStall(); break; case 3://j rt.setValue(labelSearch(inst.operands[0].getData())); conflictStall(); break; case 4://jal rd.setValue(programCounter.getValue() + 1); rt.setValue(labelSearch(inst.operands[0].getData())); conflictStall(); break; case 5://lw rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(operandValue(1,inst)); rt.setValue(registerTable[operandValue(2,inst)].getValue()); rt.setReference(operandValue(2,inst)); //System.out.println("lw decode: " + rd.getValue() + " " +

rs.getValue() + " " + rt.getValue()); conflictStall(); break; case 6://mflo rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); conflictStall(); break; case 7://move rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(registerTable[operandValue(1,inst)].getValue()); rs.setReference(operandValue(1,inst)); conflictStall(); break; case 8://mult rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(registerTable[operandValue(1,inst)].getValue()); rs.setReference(operandValue(1,inst)); conflictStall(); break; case 9: rd = new Register(0); rs = new Register(0); rt= new Register(0); break; case 10://sll rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(registerTable[operandValue(1,inst)].getValue()); rs.setReference(operandValue(1,inst)); rt.setValue(operandValue(2,inst)); conflictStall(); break; case 11://slt rd.setValue(registerTable[operandValue(0,inst)].getValue());

rd.setReference(operandValue(0,inst)); rs.setValue(registerTable[operandValue(1,inst)].getValue()); rs.setReference(operandValue(1,inst)); rt.setValue(operandValue(2,inst)); conflictStall(); case 12://sw rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(operandValue(1,inst)); rt.setValue(registerTable[operandValue(2,inst)].getValue()); rt.setReference(operandValue(2,inst)); //System.out.println("sw decode: " + rd.getValue() + " " +

rs.getValue() + " " + rt.getValue()); conflictStall(); break; case 13://sub rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(registerTable[operandValue(1,inst)].getValue()); rs.setReference(operandValue(1,inst)); rt.setValue(operandValue(2,inst)); conflictStall(); break; case 14://addi rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(registerTable[operandValue(1,inst)].getValue()); rs.setReference(operandValue(1,inst)); rt.setValue(operandValue(2,inst)); conflictStall(); break; case 15://be rd.setValue(registerTable[operandValue(0,inst)].getValue()); rd.setReference(operandValue(0,inst)); rs.setValue(registerTable[operandValue(1,inst)].getValue()); rs.setReference(operandValue(1,inst)); rt.setValue(labelSearch(inst.operands[2].getData())); conflictStall(); break; default:

}}//decode end

/*********************EXECUTE**************************************/

void execute(Instruction inst){String binary;switch (inst.getOpcode()){

case 1://add current_rd.setValue(current_rs.getValue() + current_rt.getValue());

registerTable[current_rd.getReference()].setValue(current_rd.getValue()); break; case 2://bne if (current_rd.getValue() != current_rs.getValue()){ prepareJump(); programCounter.setValue(current_rt.getValue()); } break; case 3://j

108

//if(current_rt.getValue()== 30)System.out.println("Jump L5"); prepareJump(); programCounter.setValue(current_rt.getValue()); break; case 4://jal registerTable[31].setValue(rd.getValue()); prepareJump(); programCounter.setValue(current_rt.getValue()); break; case 5://lw loads++; memoryAccess(2);

registerTable[current_rd.getReference()].setValue(current_rd.getValue()); break; case 6://mflo current_rd.setValue(flo.getValue());

registerTable[current_rd.getReference()].setValue(current_rd.getValue()); break; case 7://move current_rd.setValue(current_rs.getValue());

registerTable[current_rd.getReference()].setValue(current_rd.getValue()); break; case 8://mult int temp_mult = current_rd.getValue()*current_rs.getValue(); binary = Integer.toBinaryString(temp_mult); fhi.setValue(upperBinary(binary)); flo.setValue(lowerBinary(binary)); break; case 9://nop current_rd = new Register(0); current_rs = new Register(0); current_rt = new Register(0); break; case 10://sll int temp_shift = current_rs.getValue(); binary = Integer.toBinaryString(temp_shift); binary = shiftLeft(binary,rt.getValue()); current_rd.setValue(Integer.parseInt(binary,2)); registerTable[current_rd.getReference()] = current_rd; break; case 11://slt if (current_rs.getValue() < current_rt.getValue()) current_rd.setValue(1); else current_rd.setValue(0);

registerTable[current_rd.getReference()].setValue(current_rd.getValue()); break; case 12://sw stores++; copyRegister(write_rd, current_rd); copyRegister(write_rs, current_rs); copyRegister(write_rt, current_rt); writeaddress = write_rs.getValue() + write_rt.getValue(); break; case 13://sub current_rd.setValue(current_rs.getValue() -

current_rt.getValue());

registerTable[current_rd.getReference()].setValue(current_rd.getValue()); break;

case 14://addi current_rd.setValue(current_rs.getValue() +

current_rt.getValue());

registerTable[current_rd.getReference()].setValue(current_rd.getValue()); break; case 15://be if (current_rd.getValue() == current_rs.getValue()){ prepareJump(); programCounter.setValue(current_rt.getValue()); } break; case 31://end if ((stall == 0) &&(current_writeaddress == -1)) shutdown =

false; break;

}//printRegisterTable();

}//execute end

/************************PROCESSOR METHODS************************//* * labelSearch: returns the address a label is pointing to. */int labelSearch(String label){

int position = 0;for (int i = 0; i < labels.size();i++){

if (labels.get(i).getName().matches(label)){position = labels.get(i).getPos();

}}return position;

}// labelSearch end

/* * vectorData: returns a memoryvector's data if any */int vectorData(MemoryVector vector){

int temp_data = 0;if (vector.hasData()){

temp_data = vector.getData();}return temp_data;

}// vectorData

/* * vectorInst: returns a memoryvector's instruction if any */Instruction vectorInst(MemoryVector vector){

Instruction temp_inst = null;if (vector.hasInst()){

temp_inst = vector.getInst();}return temp_inst;

}//vectorInst

/* * lowerBinary: returns the lower 32 bit of a binary string. */int lowerBinary(String s){

String lower = "";

110

int result;for (int i = 0; i< 32; i++){

if (s.length() <= i)break; lower = lower + s.charAt(i); }

result = Integer.parseInt(lower,2);return result;

}//lowerBinary end

/* * upperBinary: returns the upper 32 bit of a binary string */int upperBinary(String s){

String upper = "";int result;if (s.length()<32) return 0;for (int i = 32; i < s.length(); i++){

upper = upper + s.charAt(i); }

result = Integer.parseInt(upper,2);return result;

}//upperBinary end

/* * shiftLeft: shift a binary String to the left 'times' times. */String shiftLeft(String s, int times){

String temp = "";for (int i = 0;i < times;i++){

temp = temp + '0';}for (int i = times; i < s.length();i++){

temp = temp + s.charAt(i - times);}return temp;

}//shiftLeft end

/* * prepareJump: prepares the pipeline's status for a jump */void prepareJump(){

rawar = 0;end = false;decode_inst = new Instruction(9,"nop");preexecute_inst = new Instruction(9,"nop");execute_inst = new Instruction(9,"nop");writeaddress = -1;current_writeaddress = -1;

}//prepareJump end

/* * copyRegister: copies the fields of a register into another */void copyRegister(Register r1,Register r2){

r1.setValue(r2.getValue());r1.setReference(r2.getReference());

}//copyRegister end

/* * copyInstruction: cpies the fields of an instruction into another */void copyInstruction (Instruction inst1, Instruction inst2){

inst1.setOpcode(inst2.getOpcode());inst1.setName(inst2.getName());

inst1.setOperands(inst2.getOperands());}// copyInstruction end

/* * operandValue: returns the integer value of an operand */int operandValue(int x,Instruction inst){

return Integer.valueOf(inst.operands[x].getData());}// operandValue end

/* * conflict: checks if there is a conflict between a register and the

current_rd */boolean conflict (Register r){

if (r.getReference() == -1) return false;if (r.getReference() == current_rd.getReference())

return true;else return false;

}//conflict end

/* * conflictStall: checks whether there is a RAW or WAR issue during decode */void conflictStall(){

if (conflict(rd)) rawar++; else if (conflict(rs)) rawar++; else if (conflict(rt)) rawar++;

}//conflictStall end

/* * memoryAccess: manages all pipeline memory interface */void memoryAccess(int choice){

int address;switch (choice){case 1: // Instruction Fetch

if (cache_mode){fetch_inst = vectorInst(inst.access(programCounter.getValue()));if ((inst.getDelay() - 1) != 0) stall = inst.getDelay()

- 1;}else{

fetch_inst = vectorInst(c1.access(programCounter.getValue()));//System.out.println(fetch_inst.getName());if (c1.getDelay() - 1 != 0) stall = c1.getDelay() - 1;

}break;

case 2: // Data readaddress = current_rs.getValue() + current_rt.getValue();

if (cache_mode){ current_rd.setValue(vectorData(data.access(address))); if (data.getDelay() - 1 != 0) stall = data.getDelay() - 1; } else{ current_rd.setValue(vectorData(c1.access(address))); if (c1.getDelay() - 1 != 0) stall = c1.getDelay() - 1; }

112

break;case 3: //Data Write

if (cache_mode){data.store(write_rd.getValue(),current_writeaddress);if (data.getDelay() - 1 != 0) stall = data.getDelay() -

1;}

else{ c1.store(write_rd.getValue(),current_writeaddress); if (c1.getDelay() - 1 != 0) stall = c1.getDelay() - 1; }

current_writeaddress = -1;break;

}}//memoryAccess end

/* * printRegisterTable: prints register table in the console for debugging */void printRegisterTable(){

for (int k = 0; k < 32; k++){System.out.print(k + ": " + registerTable[k].getValue() +

"\t");}System.out.println("");

}//printRegisterTable end

}//Processor end

/*****************************PIPELINE THREADS********************/

class Fetch implements Runnable{private Synchronizer sync;private int temp;Fetch (Synchronizer s){sync = s;}public void run(){

while (proc.shutdown){try{

sync.fetching();

run_cycles++;temp = proc.programCounter.getValue();feed ="Cycle: " + run_cycles + "\n";feed = feed + "PC: " + temp + "\n";status = run_cycles + " : " + proc.rawar + " / " +

temp + " / " + proc.stall + "\t";

if (proc.rawar != 0){if(proc.stall != 0) proc.stall --;proc.fetch_inst = new Instruction (9,"nop");temp--;proc.programCounter.setValue(temp);

}else if (proc.stall != 0){proc.stall--;proc.fetch_inst = new Instruction (9,"nop");

}else if(!proc.end){proc.memoryAccess(1);//System.out.print(proc.programCounter.getVa

lue() + "\t");//System.out.println(c1.access(6).hasData()

+ " " + c1.memory.access(6).hasData());

//c1.printCache();if (proc.fetch_inst.getOpcode() == 31)

proc.end = true;temp++;proc.programCounter.setValue(temp);

}else{proc.fetch_inst = new Instruction (31,"end");

}

status = status + "\t fetch::" + proc.fetch_inst.getName() + "\t decode::";

sync.enableDecode();sync.waitForFetch();

}catch (InterruptedException e){o.update("Fetch Interrupted");}

}}//run end

}//Fetch endclass Decode implements Runnable{

private Synchronizer sync;Decode (Synchronizer s){sync = s;}public void run(){

while (proc.shutdown){try{

sync.waitForDecode();sync.decoding();status = status + proc.decode_inst.getName() + "\t

exec::";proc.rawar = 0;

proc.decode(proc.decode_inst);

if (proc.rawar == 0){proc.copyInstruction(proc.preexecute_inst,

proc.decode_inst);proc.copyRegister(proc.prerd, proc.rd);proc.copyRegister(proc.prers, proc.rs);proc.copyRegister(proc.prert, proc.rt);proc.copyInstruction(proc.decode_inst,

proc.fetch_inst);}else{

proc.preexecute_inst = new Instruction(9,"nop");

proc.prerd = new Register(0);proc.prers = new Register(0);proc.prert = new Register(0);

}sync.enableExecute();

}catch(InterruptedException e){o.update("Decode interrupted");}

}}//run end

}//Decode endclass Execute implements Runnable{

private Synchronizer sync;Execute (Synchronizer s){sync = s;}public void run(){

while (proc.shutdown){

114

try{sync.waitForExecute();sync.executing();status = status + proc.execute_inst.getName() +

"\t write::";

proc.execute(proc.execute_inst);proc.copyInstruction(proc.execute_inst,

proc.preexecute_inst);proc.copyRegister(proc.current_rd, proc.prerd);proc.copyRegister(proc.current_rs, proc.prers);proc.copyRegister(proc.current_rt, proc.prert);

if (proc.execute_inst.getOpcode() != 9){instructions++;

}

feed = feed + "Instructions: " + instructions + "\n";

cpi = (float)run_cycles / (float)instructions;feed = feed + "CPI: " + cpi + "\n";

sync.enableWriteBack();}catch(InterruptedException e){o.update("Execute

interrupted");}}

}//run end}//Execute endclass WriteBack implements Runnable{

private Synchronizer sync;WriteBack (Synchronizer s){sync = s;}public void run(){

while (proc.shutdown){try{

sync.waitForWriteBack();sync.writingBack();status = status + proc.writeaddress + " / " +

proc.current_writeaddress;

if (proc.current_writeaddress != -1)proc.memoryAccess(3);

if (proc.writeaddress != -1){proc.current_writeaddress =

proc.writeaddress;proc.writeaddress = -1;

}

feed = feed + "Loads: " + proc.loads + "\n";feed = feed + "Stores: " + proc.stores + "\n";area.setText(feed);

//System.out.println(status);status = null;sync.enableFetch();

}catch(InterruptedException e) {o.update("WriteBack interrupted");}

}}//run end

}//WriteBack end}//Pipeline end

class Synchronizer {private boolean fetch = true;

private boolean decode = false;private boolean execute = false;private boolean writeBack = false;public synchronized void fetching(){

fetch = false;}//fetchingpublic synchronized void decoding(){

decode = false;}//decodingpublic synchronized void executing(){

execute = false;}//executingpublic synchronized void writingBack(){

writeBack = false;}//writingBackpublic synchronized void enableFetch(){

fetch = true;notifyAll();

}//enableFetchpublic synchronized void enableDecode(){

decode = true;notifyAll();

}//enableDecode endpublic synchronized void enableExecute(){

execute = true;notifyAll();

}// enableDecode endpublic synchronized void enableWriteBack(){

writeBack = true;notifyAll();

}//enableWriteBack endpublic synchronized void waitForFetch()throws InterruptedException{

while (fetch == false) wait();}//waitForFetch endpublic synchronized void waitForDecode()throws InterruptedException{

while (decode == false) wait();}//waitForDecode endpublic synchronized void waitForExecute()throws InterruptedException{

while (execute == false) wait();}//waitForExecute endpublic synchronized void waitForWriteBack()throws InterruptedException{

while (writeBack == false) wait();}//waitForWriteBack end

}//Synchronizer end

class Register{int data;int table_reference;Register(int n){

data = n;table_reference = -1;

}//Constructor end

void setValue(int n){data = n;

116

}//setValue endint getValue(){

return data;}//getValue endvoid setReference(int n){

table_reference = n;}//setReference endint getReference(){

return table_reference;}//getReference end

}//Register end

Παράρτημα 2: Αλγόριθμος πολλαπλασιασμού πινάκων σε C

#define MATRIX_DIMEN 10

int main(int argc, char *argv[]){ int A[MATRIX_DIMEN][MATRIX_DIMEN]; int B[MATRIX_DIMEN][MATRIX_DIMEN]; int C[MATRIX_DIMEN][MATRIX_DIMEN]; int i,j,k;

for (i=0;i<MATRIX_DIMEN;i++) { for (j=0;j<MATRIX_DIMEN;j++) { A[i][j] = i + j; B[i][j] = (i+1) * (j+1); C[i][j] = 0; } } for (i=0;i<MATRIX_DIMEN;i++) { for (j=0;j<MATRIX_DIMEN;j++) { for (k=0;k<MATRIX_DIMEN;k++) { C[i][j] = C[i][j] + (A[i][k]*B[k][j]); } } }}

Παράρτημα 3: Αλγόριθμοι πολλαπλασιασμού πινάκων σε assembly

16 x 16addi $15,$0,16addi $16,$0,257

add $3,$0,$16add $4,$3,$16add $5,$4,$16add $20,$5,$16

L1: add $8,$7,$6sw $8,0($3)addi $3,$3,1addi $6,$6,1bne $6,$15,L1addi $7,$7,1move $6,$0bne $7,$15,L1

L2: addi $29,$9,1addi $30,$10,1mult $29,$30mflo $11sw $11,0($4)addi $4,$4,1addi $9,$9,1bne $9,$15,L2addi $10,$10,1move $9,$0bne $10,$15,L2

L3: sw $0,0($5)addi $5,$5,1bne $5,$20,L3move $6,$0

L5: move $7,$0L4: move $8,$0L7: move $3,$0

move $4,$0move $5,$0add $3,$0,$16add $4,$3,$16add $5,$4,$16add $3,$3,$6mult $8,$15mflo $12add $3,$3,$12lw $9,0($3)add $4,$4,$8mult $7,$15mflo $12add $4,$4,$12lw $10,0($4)mult $9,$10mflo $12add $11,$11,$12addi $8,$8,1be $8,$15,L8

118

j L7L8: add $5,$5,$6

mult $7,$15mflo $12add $5,$5,$12sw $11,0($5)move $11,$0addi $7,$7,1be $7,$15,L9j L4

L9: addi $6,$6,1be $6,$15,L10j L5

L10: move $25,$0end

32 x 23

addi $15,$0,32addi $16,$0,1025add $3,$0,$16add $4,$3,$16add $5,$4,$16add $20,$5,$16

L1: add $8,$7,$6sw $8,0($3)addi $3,$3,1addi $6,$6,1bne $6,$15,L1addi $7,$7,1move $6,$0bne $7,$15,L1

L2: addi $29,$9,1addi $30,$10,1mult $29,$30mflo $11sw $11,0($4)addi $4,$4,1addi $9,$9,1bne $9,$15,L2addi $10,$10,1move $9,$0bne $10,$15,L2

L3: sw $0,0($5)addi $5,$5,1bne $5,$20,L3move $6,$0

L5: move $7,$0L4: move $8,$0L7: move $3,$0

move $4,$0move $5,$0add $3,$0,$16add $4,$3,$16add $5,$4,$16add $3,$3,$6mult $8,$15mflo $12add $3,$3,$12lw $9,0($3)add $4,$4,$8mult $7,$15mflo $12add $4,$4,$12lw $10,0($4)mult $9,$10mflo $12add $11,$11,$12addi $8,$8,1be $8,$15,L8j L7

L8: add $5,$5,$6mult $7,$15mflo $12add $5,$5,$12sw $11,0($5)move $11,$0addi $7,$7,1be $7,$15,L9j L4

L9: addi $6,$6,1be $6,$15,L10j L5

L10: move $25,$0end

120

Πίνακας σχημάτωνΣχήμα 1: Ιεραρχία Μνήμης................................................................................................................15Σχήμα 1: Τοποθέτηση μπλοκ σε κρυφή μνήμη..................................................................................19Σχήμα 2: Τμήματα μιας διεύθυνσης μιας συνολοσυσχετιστικής κρυφής μνήμης..............................21

Πίνακας γραφημάτωνΓράφημα 1: Απόδοση - Associativity με διάταξη L1 Shared, L2 Shared..........................................39Γράφημα 2: L1 Shared Cache Misses / Hits - Associativity με διάταξη L1 Shared , L2 Shared.......39Γράφημα 3: L2 Shared Cache Misses Hits - Associativity με διάταξη L1 Shared Cache, L2 Shared Cache..................................................................................................................................................40Γράφημα 4: Aπόδοση - Associativity με διάταξη L1 Shared Cache, L2 Shared Cache όταν τα δεδομένα χωράνε στην L2..................................................................................................................40Γράφημα 5: Απόδοση - Μέγεθος L1 , L2 ..........................................................................................41Γράφημα 6: L1 Shared Cache Misses / Hits - Μέγεθος L1 , L2........................................................42Γράφημα 7: L2 Shared Cache Misses / Hits - Μέγεθος L1 , L2.......................................................42Γράφημα 8: Απόδοση - Μέγεθος L1, L2 με N = 16 (μέγεθος δεδομένων 3072) και Write Through 43Γράφημα 9: L1 Shared Cache Misses / Hits - Μέγεθος L1 , L2 με Ν = 16 (μέγεθος δεδομένων 3072) και Write Through....................................................................................................................44Γράφημα 10: L2 Shared Cache Misses / Hits - Μέγεθος L1 , L2 με Ν = 16 (μέγεθος δεδομένων 3072) και Write Through....................................................................................................................44Γράφημα 11: Απόδοση - Μέγεθος L1, L2 με N = 16 (μέγεθος δεδομένων 3072) και Write Back....45Γράφημα 12: L1 Shared Cache Misses / Hits - Μέγεθος L1 , L2 με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back..........................................................................................................................45Γράφημα 13: L2 Shared Cache Misses / Hits - Μέγεθος L1 , L2 με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back..........................................................................................................................46Γράφημα 14: Απόδοση - Bottlenecks (Χωρίς | RF – L1 | L1 – L2 | L2 - Main)................................47Γράφημα 15: Απόδοση - Μέγεθος γραμμής με διάταξη L1 Shared...................................................48Γράφημα 16: L1 Shared CacheMisses / Hits - Μέγεθος γραμμής με διάταξη L1 Shared.................48Γράφημα 17: Απόδοση - Μέγεθος γραμμής με διάταξη L1 , L2 Shared..........................................49Γράφημα 18: L1 Shared Cache Misses / Hits - Μέγεθος γραμμής με διάταξη L1, L2 Shared..........49Γράφημα 19: L2 Shared Cache Misses / Hits - Μέγεθος γραμμής με διάταξη L1 , L2 Shared.........50Γράφημα 20: Απόδοση - Associativity με διάταξη L1 Instruction , L1 Data – L2 Shared................51Γράφημα 21: L1 Instruction Cache Misses / Hits - Associativity με διάταξη L1 Instruction , Data – L2 Shared............................................................................................................................................51Γράφημα 22: L1 Data Cache Misses / Hits - Associativity με διάταξη L1 Instruction, Data – L2 Shared.................................................................................................................................................52Γράφημα 23: L2 Shared Cache Misses / Hits - Associativity με διάταξη L1 Instruction, Data – L2 Shared.................................................................................................................................................52Γράφημα 24: Απόδοση - Μέγεθος μνημών L1 Instruction , L1 Data – L2 Shared............................53Γράφημα 25: L1 Instruction Cache Misses / Hits - Μεγέθη μνημών :L1 Instruction , L1 Data – L2 Shared.................................................................................................................................................54Γράφημα 26: L1 Data Cache Misses / Hits - Μεγέθη μνημών L1 Instruction , L1 Data – L2 Shared............................................................................................................................................................54Γράφημα 27: L2 Shared Cache Misses / Hits - Μεγέθη μνημών L1 Instuction , L1 Data – L2 Shared............................................................................................................................................................55Γράφημα 28: Απόδοση - Μέγεθος μνημών L 1 Data , L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και write through......................................................................................................................56Γράφημα 29: L1 Instruction cache Misses / Hits - Μέγεθος μνημών L1 Data , L2 Shared με Ν = 16

(μέγεθος δεδομένων 3072) και write through....................................................................................57Γράφημα 30: L1 Data Cache Misses / Hits - Μέγεθος μνημών L1 Data , L2 Shared με Ν =16 (μέγεθος δεδομένων 3072) και write through....................................................................................57Γράφημα 31: L2 Shared Cache Misses / Hits - Μέγεθος μνημών L1 Data , L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και write through....................................................................................58Γράφημα 32: Απόδοση - Μέγεθος μνημών L1 Data , L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και write back...........................................................................................................................59Γράφημα 33: L1 Instruction cache Misses / Hits - Μέγεθος μνημών L1 Data , L2 shared με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back........................................................................................59Γράφημα 34: L1 Data Cache Misses / Hits - Μέγεθος μνημών L1 Data ,L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back........................................................................................60Γράφημα 35: L2 Shared Cache Misses / Hits - Μέγεθος μνημών L1 Data ,L2 Shared με Ν = 16 (μέγεθος δεδομένων 3072) και Write Back........................................................................................60Γράφημα 36: Απόδοση - Bottlenecks (Χωρίς | register file - instruction - data | instruction , data – L2 | L2 - main)....................................................................................................................................61Γράφημα 37: Απόδοση - Μέγεθος γραμμής.......................................................................................62Γράφημα 38: L1 Instruction Cache Misses / Hits - Μέγεθος γραμμής..............................................63Γράφημα 39: L1 Data Cache Misses / Hits - Μέγεθος γραμμής........................................................63Γράφημα 40: L2 Shared Cache Misses / Hits - Μέγεθος γραμμής....................................................64

ΒιβλιογραφίαCA001: John L. Hennesy , David A. Patterson, Computer Architecture, 2003MEM001: Lebeck , Sorin , Roth , Hill , Wood , Sohi , Smith , Vijaykumar , Lipasti, Storage Hierarchy I: Caches, 2004MEM005: Mahmut Kandemir , J. Ramanujam , Alok Choudhary, Improving Cache Locality by a Combination of Loop and Data Transformations, 1999ΜΕΜ002: Dirk Grunwald , Benjamin Zorn , Robert Henderson, Improving the Cache Locality of Memory Allocation, 1993MEM003: Micjel E. Wolf , Monica S. Lam, A Data Locality Oprimizing Algorithm, 1991CA004: James E. Smith , Gurindar S. Sohi, The Microarchitecture of Superscalar Processors, 1995CA005: Sylvain Girbal , Daniel Gracia Perez , Gilles Mouchard , Olivier Temam, Building a DLX simulator from scratch with UNISIM, 2007CA002: D. Serpanos, MIPS: Σύνολο εντολών, γλώσασ μηχανής & μεθοδολογία σχεδίασης, CA003: Doug Burger , Todd M. Austin, The Simplescalar Tool Set, version 2.0, 1997

122


Recommended