Ανάπτυξη αλγόριθμου Closest Pair με CUDA API

Μέγεθος: px
Εμφάνιση ξεκινά από τη σελίδα:

Download "Ανάπτυξη αλγόριθμου Closest Pair με CUDA API"

Transcript

1 ΕΘΝΙΚΟ ΚΑΙ ΚΑΠΟΔΙΣΤΡΙΑΚΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΤΜΗΜΑ ΦΥΣΙΚΗΣ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ & ΤΗΛΕΠΙΚΟΙΝΩΝΙΩΝ ΜΕΤΑΠΤΥΧΙΑΚΟ ΔΙΠΛΩΜΑ ΕΙΔΙΚΕΥΣΗΣ ΗΛΕΚΤΡΟΝΙΚΟY ΑΥΤΟΜΑΤΙΣΜΟY ΔΙΠΛΩΜΑΤΙΚΗ ΕΡΓΑΣΙΑ Ανάπτυξη αλγόριθμου Closest Pair με CUDA API Φοιτητής: Παπαγεωργάκης Γεώργιος Επιβλέπων: Κοτρώνης Ιωάννης, Επ.Καθηγητής Αθήνα, Σεπτέμβρης 2014

2 ΔΙΠΛΩΜΑΤΙΚΗ ΕΡΓΑΣΙΑ Ανάπτυξη αλγόριθμου Closest Pair με CUDA API Φοιτητής: Παπαγεωργάκης Γεώργιος Επιβλέπων: Κοτρώνης Ιωάννης, Επ.Καθηγητής Εγκρίθηκε από την τριμελή εξεταστική επιτροπή την 15η Οκτώβρης Κοτρώνης Ιωάννης Κ. Επ.Καθηγητής Αν.Καθηγητής Αθήνα, Σεπτέμβρης

3 Περίληψη Είναι αποδεδειγμένο ότι η χρήση των μονάδων επεξεργασίας γραφικών, σε συνδυασμό με την κεντρική μονάδα επεξεργασίας, μπορεί να βελτιώσει κατά κόρον την απόδοση εντατικών υπολογιστικών εφαρμογών. Η κατάλληλη χρήση των GPU είναι ακόμα ένα ανοιχτό ερευνητικό θέμα. Ένα από τα προβλήματα είναι η κατάλληλη ρύθμιση παραμέτρων εκτέλεσης του συστήματος σε GPU και σε αυτό επικεντρώνεται αυτή η εργασία. Δηλαδή για ένα πρόγραμμα που δέχεται διαφορετικές παραμέτρους αναζητείται η καλύτερη τιμή για το πλήθος των νημάτων ανά μπλοκ και το πλήθος των μπλοκ στο πλέγμα, που θα οδηγήσει στο μικρότερο χρόνο εκτέλεσης. Αντικείμενο της παρούσας εργασίας είναι ο σχεδιασμός και η υλοποίηση γνωστού προβλήματος της υπολογιστικής γεωμετρίας δισδιάστατου πλησιέστερου ζεύγουςclosest pair, χρησιμοποιώντας τη μονάδα επεξεργασίας γραφικών (GPU) της NVidia με το CUDA API. Πιο συγκεκριμένα, μια πρώτη ανάλυση αφορά την απλή προσέγγιση στο πρόβλημα, δηλαδή με έλεγχο για κάθε δυνατό ζεύγος σημείων, τετραγωνικής ασυμπτωτικής πολυπλοκότητας. Στην δεύτερη και ταχύτερη μέθοδο αλγόριθμου αναλύουμε την προσέγγιση του διαίρει και βασίλευε. Λαμβάνοντας υπόψη ένα σύνολο σημείων στο επίπεδο S, η προσέγγισή μας θα είναι να χωριστεί το σύνολο σε δύο περίπου ίσα μέρη (S1 και S2) για τις οποίες ήδη έχουμε τις λύσεις, και στη συνέχεια, να συγχωνεύσει τα μισά σε γραμμικό χρόνο για να έχουμε τελικά ασυμπτωτική πολυπλοκότητα O(nlogn). Ωστόσο, η πραγματική λύση απέχει πολύ από το προφανές. Είναι πιθανό ότι το επιθυμητό ζεύγος θα μπορούσε να έχει ένα σημείο στο S1 και ένα στο S2, αλλά αυτό δεν μας αναγκάζει να ελέγξουμε όλα τα πιθανά ζεύγη σημείων. Η προσέγγιση που παρουσιάζεται στην παρούσα εργασία είναι bottom-up. Ο αλγόριθμος closest pair είναι ένα βασικό πρόβλημα με πληθώρα εφαρμογών αλλά και με πολύ σημαντικό ρόλο κλειδί σε πολλούς αλγόριθμους. Η μελέτη του και η παραλληλοποίηση του με χρήση GPU μπορεί να μειώσει σημαντικά τον χρόνο εκτέλεσης του αλγορίθμου, προσφέροντας έτσι ιδανική λύση για real-time συστήματα αλλά και για εφαρμογές όπου παίζει σημαντικό ρόλο ο χρόνος υπολογισμού μεγάλου όγκου δεδομένων. Οι δυσκολίες που αντιμετωπίστηκαν σχετίζονται κατά κόρον με τον αλγόριθμο διαίρει & βασίλευε στον εντοπισμό των υποψήφιων πιο κοντινών ζευγαριών που υπολογίστηκαν σε μια από τις αναδρομικές κλήσεις, ή σε υποψήφιο ζεύγος σημείων με ένα σημείο στην δεξιά δέσμη και το άλλο στην αριστερή. Επιπλέον, υπήρξαν τεχνικής φύσης δυσκολίες όσο αφορά την βέλτιστη χρήση της συσκευής GPU οι οποίες αναλύονται παράλληλα με τα αποτελέσματα μετρήσεων. 2

4 Ευχαριστίες Πριν την παρουσίαση της παρούσας εργασίας, αισθάνομαι την υποχρέωση να ευχαριστήσω τον υπεύθυνο ακαδημαϊκό που είχε την ευθύνη για την επίβλεψη αυτής της εργασίας, τον καθηγητή κύριο Ιωάννη Κοτρώνη του τμήματος πληροφορικής του Πανεπιστημίου Αθηνών, για την καθοδήγηση και την βοήθεια που μου πρόσφερε. Οι συμβουλές που πήρα ήταν πολύ χρήσιμες και καθοριστικές για την ολοκλήρωση της εργασίας αυτής. Πέραν της ολοκλήρωσης της διπλωματικής εργασίας, κατάφερα να εμβαθύνω στην θεωρία του παράλληλου υπολογισμού, ενός θέματος πολύ σημαντικού στον τομέα της πληροφορικής στις μέρες μας. Θα ήθελα επίσης να ευχαριστήσω το διδακτορικό φοιτητή Ηλία Κωνσταντινίδη, που με βοήθησε σε κάποιες δυσκολίες που αντιμετώπισα κατά τη διάρκεια της εργασίας μου. 3

5 Περιεχόμενα Εισαγωγή Αρχιτεκτονική CUDA και Προγραμματιστικό Μοντέλο Αρχιτεκτονική υλικού Αρχιτεκτονική SIMT Multithreading υλικού Προγραμματιστικό μοντέλο Δομή ενός προγράμματος CUDA Οργάνωση των CUDA threads Οργάνωση της CUDA Memory (global, constant, shared) Pointers στην CUDA Άλλοι ειδικοί τύποι μνήμης Ατομικές Συναρτήσεις Asynchronous Concurrent Execution Concurrent εκτέλεση μεταξύ host-device Επικάλυψη της μεταφοράς δεδομένων και Εκτέλεσης Kernel Ταυτόχρονη (Concurrent) εκτέλεση των Kernel Ταυτόχρονες μεταφορές δεδομένων Ροή δεδομένων - Streams Events Συγχρονισμένες κλήσεις Ενοποιημένο Virtual Address Space Κεφάλαιο 1 Επιδόσεις και περιορισμοί Χαρακτηριστικά συστήματος μετρήσεων Κεντρική Μονάδα Επεξεργασίας Κάρτα γραφικών Επιτάχυνση (Speedup) και αποτελεσματικότητα (Efficiency) Structure of Arrays και Array of Structures στην cuda Mapping των block-threads Εκτίμηση, Παραλληλοποίηση, Βελτιστοποίηση, Ανάπτυξη (APOD)

6 Κεφάλαιο 2 Αλγόριθμος Closest Pair Brute Force Closest Pair Brute Force Closest Pair Brute Force για CPU Τεχνική Caching για blocked algorithms Αποτελέσματα μετρήσεων με τεχνική Caching Παραλληλοποίηση Closest Pair Brute Force για GPU σε CUDA Μέγεθος σημείων (points) και απαιτήσεις διάταξης Μια πρώτη υλοποίηση σε GPU Αποτελέσματα μετρήσεων Κεφάλαιο 3 Αλγόριθμος Closest Pair Divide & Conquer Closest Pair Divide & Conquer Ορθότητα αλγόριθμου Closest Pair Divide & Conquer για CPU Αποτελέσματα μετρήσεων Παραλληλοποίηση Closest Pair Divide & Conquer για GPU σε CUDA CUDA Closest Pair Divide & Conquer bitonicsort kernel bruteforce kernel mindistreduction kernel findverticalstrip kernel comparestrippoints kernel Αποτελέσματα μετρήσεων Ανασκόπηση αποτελεσμάτων Κεφάλαιο 4 Ανακεφαλαίωση Συμπεράσματα Παράρτημα Βιβλιογραφία - Αναφορές

7 Εισαγωγή Αρχιτεκτονική CUDA και Προγραμματιστικό Μοντέλο. 1 Αρχιτεκτονική υλικού Η NVIDIA GPU αρχιτεκτονική είναι δομημένη γύρω από μια κλιμακούμενη σειρά πολύνημάτινης ροής πολυεπεξεργαστών- Multithreaded Streaming Multiprocessors (SMs). Όταν ένα πρόγραμμα CUDA επικαλείται έναν πυρήνα πλέγματος (kernel grid) από τον εξυπηρετητή (host CPU), τα μπλοκ του πλέγματος απαριθμούνται και διανέμονται στους πολυεπεξεργαστές με διαθέσιμη χωρητικότητα εκτέλεσης. Τα νήματα ενός μπλοκ εκτελούνται ταυτόχρονα σε έναν πολυεπεξεργαστή, και πολλαπλά μπλοκ νημάτων μπορούν να εκτελεστούν ταυτόχρονα σε έναν πολυεπεξεργαστή. Όταν τερματίσει ένα μπλοκ νημάτων, τα νέα μπλοκ νημάτων εκτελούνται στους ελεύθερους πλέον πολυεπεξεργαστές. Παράδειγμα της ροής επεξεργασίας CUDA: 1. Αντιγραφή δεδομένων από την κύρια μνήμη στην GPU memory. 2. Η CPU αναθέτει την process στην GPU. 3. Εκτέλεση στην GPU παράλληλα στον κάθε πυρήνα της. 4. Αντιγραφή του αποτελέσματος από την GPU memory στην κύρια memory Σχήμα 1.1 Ροή επεξεργασίας cuda Ο πολυεπεξεργαστής έχει σχεδιαστεί για να εκτελεί εκατοντάδες νήματα ταυτόχρονα. Για να διαχειριστεί ένα τόσο μεγάλο ποσό νημάτων, χρησιμοποιεί μια μοναδική αρχιτεκτονική που ονομάζεται SIMT (Single-Instruction, Multiple-Thread) που περιγράφεται παρακάτω. Οι εντολές περνάνε με τεχνική pipeline για τη μόχλευση του παραλληλισμού σε επίπεδο εντολών (instruction-level parallelism) μέσα σε ένα ενιαίο νήμα, καθώς και σε νημάτινο επίπεδο παραλληλισμού (thread-level parallelism) εκτενώς μέσω ταυτόχρονου πολύ-νηματοειδής υλικού (multithreading hardware). Σε αντίθεση με τους πυρήνες μιας CPU, οι εντολές εκδίδονται σε σειρά ενώ δεν υπάρχει πρόβλεψη διακλάδωσης ή εκτέλεσης. Τέλος, η αρχιτεκτονική της NVIDIA GPU χρησιμοποιεί littleendian αναπαράσταση. 6

8 1.1 Αρχιτεκτονική SIMT O πολυεπεξεργαστής δημιουργεί, διαχειρίζεται τα χρονοδιαγράμματα και εκτελεί νήματα σε ομάδες των 32 παράλληλων νημάτων που ονομάζονται στημόνια (warp). Το κάθε νήμα που συνθέτει ένα στημόνι ξεκινάει ταυτόχρονα με τα υπόλοιπα στην ίδια διεύθυνση προγράμματος αλλά έχουν τον δικό τους μετρητή διεύθυνσης εντολών (Instruction Address Counter), κατάστασης καταχωρητών (Register State) και συνεπώς, είναι ελεύθερα να διακλαδωθούν (branch) και να εκτελεστούν ανεξάρτητα. Ο όρος στημόνι προέρχεται από την ύφανση, την πρώτη παράλληλη τεχνολογία νημάτων. Μισό στημόνι είναι είτε το πρώτο ή το δεύτερο μισό του στημονιού. Το ένα τέταρτο του στημονιού είναι είτε το πρώτο, δεύτερο, τρίτο ή τέταρτο κομμάτι του στημονιού. Όταν ένας πολυεπεξεργαστής εκτελεί ένα ή περισσότερα μπλοκ νημάτων, τα χωρίζει σε στημόνια (warps) όπου έκαστο χρονοπρογραμματίζεται από τον warp-scheduler για να εκτελεστεί. Ο τρόπος όπου ένα μπλοκ χωρίζεται σε στημόνια είναι μονόδρομος. Κάθε στημόνι περιέχει νήματα διαδοχικών, μονοτονικά αυξανόμενου thread ID με το πρώτο στημόνι να περιέχει το νήμα 0. Η ιεραρχία Νημάτων, όπως θα δούμε παρακάτω, περιγράφει τον τρόπο με τον οποίο τα νήματα αντιστοιχίζονται σε δείκτες στο μπλοκ. Ένα στημόνι εκτελεί μια κοινή εντολή ανά μονάδα χρόνου, έτσι επιτυγχάνεται πλήρης αποτελεσματικότητα όταν όλα τα 32 νήματα του στημονιού συμφωνούν στην πορεία εκτέλεσής τους. Αν τα νήματα στο στημόνι αποκλίνουν μέσω δεδομένων που εξαρτώνται από όρους διακλάδωσης, το στημόνι εκτελεί σειριακά κάθε μονοπάτι, απενεργοποιώντας τα νήματα που δεν είναι σε αυτό το μονοπάτι, και όταν όλα τα μονοπάτια ολοκληρωθούν, τα νήματα επανασυγκλίνουν στο ίδιο μονοπάτι εκτέλεσης. Η απόκλιση διακλάδωσης (branch divergence) εμφανίζεται μόνο σε ένα στημόνι. Διαφορετικά στημόνια εκτελούνται ανεξάρτητα από το αν εκτελούν κοινά ή διαφορετικά μονοπάτια κώδικα. Η SIMT αρχιτεκτονική είναι παρόμοια με την SIMD (Single Instruction, Multiple Data) στον τρόπο οργάνωσης διανυσμάτων στ ότι μία απλή εντολή ελέγχει πολλαπλά στοιχεία επεξεργασίας. Μια βασική διαφορά είναι ότι η SIMD οργάνωση διανυσμάτων εκθέτει το πλάτος της SIMD στο λογισμικό, ενώ οι SIMT εντολές προσδιορίζουν την εκτέλεση και την διακλάδωσης ενός μόνο νήματος. Σε αντίθεση με την SIMD διανυσματική μηχανή, η SIMT επιτρέπει στους προγραμματιστές να γράψουν παράλληλο κώδικα σε νημάτινο επίπεδο για ανεξάρτητα, κλιμακωτά νήματα, καθώς και κώδικα παράλληλων δεδομένων για συντονισμένα νήματα. Για σκοπούς ορθότητας, ο προγραμματιστής μπορεί να αγνοήσει ουσιαστικά την SIMT συμπεριφορά. Ωστόσο, σημαντικές βελτιώσεις στην απόδοση μπορεί να πραγματοποιηθούν με την ιδέα ότι ο κώδικας σπάνια απαιτεί απόκλιση των νημάτων στο στημόνι. Στην πράξη, αυτό είναι ανάλογο με το ρόλο των γραμμών λανθάνουσας μνήμης (cache) σε παραδοσιακό κώδικα: Το μέγεθος της λανθάνουσας μνήμης της γραμμής μπορεί να αγνοηθεί κατά το 7

9 σχεδιασμό για την ορθότητα του προγράμματος, αλλά πρέπει να λαμβάνεται υπόψη στην δομή του κώδικα κατά το σχεδιασμό για μεγιστοποίηση της απόδοσης. Οι διανυσματικές αρχιτεκτονικές, από την άλλη πλευρά, απαιτούν από το λογισμικό να συνενώνουν τα φορτία (coalesced loads) σε διανύσματα και να διαχειρίζονται την απόκλιση χειροκίνητα. Τα νήματα (threads) ενός στημονιού (warp) που βρίσκονται στο τρέχον μονοπάτι εκτέλεσης του στημονιού ονομάζονται ενεργά νήματα (active threads), ενώ τα νήματα που δεν είναι στο ίδιο μονοπάτι είναι ανενεργά (inactive threads). Τα νήματα μπορούν να είναι ανενεργά, επειδή έχουν αποχωρήσει νωρίτερα από τα άλλα νήματα του ίδιου στημονιού, είτε επειδή είναι σε διαφορετικό μονοπάτι διακλάδωσης από εκείνα που ήδη εκτελούνται από το στημόνι, είτε επειδή είναι τα τελευταία νήματα του μπλοκ των οποίων ο αριθμός των νημάτων δεν είναι πολλαπλάσιο του μεγέθους του στημονιού (warp size). Εάν μια μη ατομική (non-atomic) εντολή που εκτελείται από ένα στημόνι γράφει στην ίδια θέση στην παγκόσμια (global) ή κοινόχρηστη (shared) μνήμη για περισσότερα από ένα από τα νήματα του στημονιού, ο αριθμός των συνεχών εγγραφών που συμβαίνουν στην εν λόγω θέση μεταβάλλεται ανάλογα με την υπολογιστική ικανότητα της συσκευής (π.χ. Compute Capability 1.x, 2.x, και 3.x). Το νήμα που εκτελεί την τελική εγγραφή είναι απροσδιόριστο. 1.2 Multithreading υλικού Το πλαίσιο εκτέλεσης (program counters, registers, κλπ.) για κάθε στημόνι που επεξεργάζεται από έναν πολυεπεξεργαστή διατηρείται on-chip για όλη τη διάρκεια ζωής του στημονιού. Ως εκ τούτου, η μετάβαση από το ένα πλαίσιο εκτέλεσης σε άλλο δεν έχει κόστος, και κάθε φορά που εκτελείται νέα εντολή, ο χρονοπρογραμματιστής των στημονιών (warp scheduler) επιλέγει στημόνι το οποίο έχει νήματα έτοιμα να εκτελέσουν την επόμενη εντολή του (τα active threads του warp) και μεταφέρει τις εντολές σε αυτά τα νήματα. Πιο συγκεκριμένα, κάθε πολυεπεξεργαστής (multiprocessor) έχει ένα σύνολο από 32-bit καταχωρητές που κατανέμονται μεταξύ των στημονιών, και μια παράλληλη κρυφή μνήμη δεδομένων (parallel data cache) ή κοινόχρηστη μνήμη (shared memory) που κατανέμεται μεταξύ των μπλοκ. Ο αριθμός των μπλοκ και των στημονιών που μπορούν να διαμένουν και να υποβάλλονται σε επεξεργασία πάνω στον πολυεπεξεργαστή για έναν συγκεκριμένο πυρήνα (kernel) εξαρτάται από την ποσότητα των καταχωρητών και την κοινόχρηστη μνήμη που χρησιμοποιεί ο πυρήνας καθώς επίσης και απ το ποσό που πραγματικά διαθέτει ο πολυεπεξεργαστής. 8

10 Υπάρχει επίσης ένας μέγιστος αριθμός των μπλοκ και ένας μέγιστος αριθμός στημονιών ανά πολυεπεξεργαστή. Τα όρια αυτά, καθώς και το ποσό των καταχωρητών και κοινόχρηστη μνήμης που διατίθενται στον πολυεπεξεργαστή είναι συνάρτηση της υπολογιστικής ικανότητας (Compute Capability) της συσκευής. Εάν δεν υπάρχουν αρκετοί καταχωρητές ή διαθέσιμη κοινόχρηστη μνήμη ανά πολυεπεξεργαστή για την επεξεργασία τουλάχιστον ενός μπλοκ, ο πυρήνας θα αποτύχει να εκτελεστεί. Ο συνολικός αριθμός των στημονιών σε ένα μπλοκ υπολογίζεται ως εξής: ceil ( T, 1) W size T είναι ο αριθμός των νημάτων ανά μπλοκ. W size είναι το μέγεθος του warp, συνήθως 32. ceil(x, y) ίσο με x με άνω στρογγυλοποίηση σε πλησιέστερο πολλαπλάσιο του y. Η αρχιτεκτονική CUDA αποτελείται από διάφορα στοιχεία (πράσινα κουτάκια): 1. Παράλληλες υπολογιστικές μηχανές των NVIDIA GPU. 2. Υποστήριξη σε OS επίπεδο πυρήνα για προετοιμασία υλικού, διαμόρφωση κλπ. 3. Οδήγηση για χρήστη (User-mode), η οποία παρέχει API σε επίπεδο συσκευή για προγραμματιστές. 4. Σύνολο εντολών PTX αρχιτεκτονικής (ISA) για τους παράλληλους πυρήνες και συναρτήσεων (functions). Σχήμα 1.2 Ροή επεξεργασίας cuda 9

11 2 Προγραμματιστικό μοντέλο 2.1 Δομή ενός προγράμματος CUDA Η δομή ενός προγράμματος CUDA αντικατοπτρίζει την συνύπαρξη ενός εξυπηρετητή (host CPU) και ενός ή περισσοτέρων συσκευών (devices GPUs) στον υπολογιστή. Κάθε αρχείο προέλευσης CUDA μπορεί να έχει κώδικα σαν μίγμα και των δύο, εξυπηρετητή και συσκευής. Από προεπιλογή, κάθε παραδοσιακό πρόγραμμα C είναι ένα πρόγραμμα CUDA που περιέχει μόνο εξυπηρετητή κώδικα. Ο προγραμματιστής μπορεί να προσθέσει συναρτήσεις συσκευής (device functions) και τα δεδομένα σε οποιοδήποτε πηγαίο αρχείο. Οι δηλώσεις δεδομένων της συσκευής επισημαίνονται σαφώς με ειδικές λέξεις-κλειδιά CUDA. Αυτές είναι συνήθως συναρτήσεις που εμφανίζουν μια πλούσια ποσότητα δεδομένων παραλληλισμού. Ο κώδικας μπορεί να μεταγλωττιστεί μόνο από συμβατικό μεταγλωττιστή (compiler), συνήθως τον nvidia NVCC. Όπως φαίνεται στο σχήμα 1.3 ο NVIDIA μεταγλωττιστής επεξεργάζεται το πρόγραμμα χρησιμοποιώντας τις λέξεις κλειδιά για να ξεχωρίσει τον κώδικα εξυπηρετητή από τον κώδικα συσκευής. Ο κώδικας της συσκευής GPU, γνωστός ως πυρήνας (kernel) και οι αντίστοιχες δομές δεδομένων (data structures), μεταγλωττίζονται περαιτέρω από ένα μέρος του NVCC κατά την εκτέλεση (runtime) και έτσι εκτελείται στην μηχανή GPU. Σε περίπτωση που δεν υπάρχει συσκευή, ο πυρήνας μπορεί να εκτελεστεί στον CPU με χρήση του εργαλείου MCUDA. Σχήμα 1.3 Γενική διαδικασία compile για πρόγραμμα σε CUDA Η εκτέλεση του προγράμματος ξεκινάει από τον εξυπηρετητή CPU. Όταν εκτελεστεί ο πυρήνας δημιουργείται ένα πλέγμα (grid) από νήματα όπως φαίνεται στο σχήμα 1.4. Όταν τερματίσει ο πυρήνας τερματίζει επίσης και το αντίστοιχο πλέγμα ενώ το πρόγραμμα συνεχίζει την εκτέλεση του στον εξυπηρετητή CPU. Το σχήμα είναι απλοποιημένο χωρίς αλληλοεπικαλύψεις εκτέλεσης μεταξύ εξυπηρετητή και συσκευής. 10

12 Στον ετερογενή (heterogeneous) προγραμματισμό μπορεί να γίνει ορθή χρήση της αλληλοεπικάλυψης, ιδιότητα η οποία θεωρείται πολύ σημαντική. Σχήμα 1.4 Εκτέλεση kernel Η εκτέλεση πυρήνα τυπικά δημιουργεί έναν μεγάλο αριθμό νημάτων τα οποία εκμεταλλεύονται την παραλληλία δεδομένων (data parallelism). Για παράδειγμα, στον πολλαπλασιασμό πινάκων το κάθε νήμα μπορεί να υπολογίζει ένα στοιχείο του διανύσματος εξόδου. Σε αυτή την περίπτωση, ο αριθμός των νημάτων που θα δημιουργηθούν είναι ανάλογος του μήκους του διανύσματος. Ο προγραμματιστής μπορεί να υποθέσει με ασφάλεια ότι η δημιουργία των νημάτων στην συσκευή (device) παίρνει μικρό αριθμό κύκλων μηχανής λόγω της αρχιτεκτονικής του υλικού. Αυτό έρχεται σε αντίθεση με την παραδοσιακή χρήση νημάτων στον CPU όπου η δημιουργία τους απαιτεί μερικές χιλιάδες κύκλους μηχανής. Ένας πυρήνας ορίζεται χρησιμοποιώντας την δήλωση προσδιορισμού global και ο αριθμός των CUDA νημάτων που εκτελούνται για συγκεκριμένη κλήση του πυρήνα προσδιορίζεται με τη χρήση μιας νέας σύνταξης διαμόρφωσης εκτέλεσης (execution configuration syntax) δηλαδή το συμβολισμό <<<... >>>. Σε κάθε νήμα που εκτελεί τον πυρήνα δίνεται ένα μοναδικό αναγνωριστικό νήματος και είναι προσβάσιμο εντός του πυρήνα, μέσω της ενσωματωμένης μεταβλητής (built-in variable) threadidx. Το ακόλουθο δείγμα κώδικα προσθέτει δύο διανύσματα Α και Β, Ν μεγέθους, και αποθηκεύει το αποτέλεσμα στο διάνυσμα C: // Kernel definition global void VecAdd(float* A, float* B, float* C){ int i = threadidx.x; C[i] = A[i] + B[i]; } int main(){... // Kernel invocation with N threads VecAdd <<<1, N>>> (A, B, C);... } 11

13 2.2 Οργάνωση των CUDA threads. Όλα τα νήματα CUDA σε ένα πλέγμα εκτελούν την ίδια συνάρτηση του πυρήνα και στηρίζονται σε συντεταγμένες για να διακρίνονται μεταξύ τους και να μπορούν να προσδιορίζουν το αντίστοιχο τμήμα των δεδομένων για την επεξεργασία. Τα νήματα οργανώνονται σε μια ιεραρχία δύο επιπέδων: ένα πλέγμα (Grid) αποτελείται από ένα ή περισσότερα μπλοκ και κάθε μπλοκ με τη σειρά του αποτελείται από ένα ή περισσότερα νήματα. Όλα τα νήματα στο μπλοκ έχουν κοινό δείκτη μπλοκ, με πρόσβαση μέσω της μεταβλητής blockidx. Κάθε νήμα έχει επίσης ένα δείκτη νήματος (thread index), με πρόσβαση μέσω της μεταβλητής threadidx. Για έναν προγραμματιστή CUDA, το blockidx και threadidx εμφανίζονται ως built-in, προ-αρχικοποιημένες μεταβλητές που μπορούν να βρεθούν μέσα σε συναρτήσεις πυρήνα. Όταν ένα νήμα εκτελεί μια συνάρτηση πυρήνα, οι αναφορές στο blockidx και threadidx μεταβλητές επιστρέφουν τις συντεταγμένες του νήματος. Οι παράμετροι διαμόρφωσης εκτέλεσης σε μια δήλωση εκτέλεσης πυρήνα καθορίζουν τις διαστάσεις του πλέγματος και τις διαστάσεις του κάθε μπλοκ. Οι διαστάσεις αυτές είναι διαθέσιμες ως προκαθορισμένες και ενσωματωμένες μεταβλητές στις συναρτήσεις του πυρήνα ως griddim και blockdim. Σχήμα 1.5 Πλέγμα (grid) με νημάτινα μπλοκ (thread blocks) Σε γενικές γραμμές, ένα πλέγμα είναι ένας 3D πίνακας από μπλοκ και κάθε μπλοκ είναι ένας 3D πίνακας νημάτων. Ο προγραμματιστής μπορεί να επιλέξει να χρησιμοποιήσει λιγότερες διαστάσεις καθορίζοντας τις αχρησιμοποίητες διαστάσεις σε 1. Η ακριβής οργάνωση ενός πλέγματος καθορίζεται από τις παραμέτρους διαμόρφωσης εκτέλεσης (execution configuration parameters) δηλαδή με <<< και >>> της δήλωσης πυρήνα. Η πρώτη παράμετρος καθορίζει τις διαστάσεις του πλέγματος σε αριθμό των μπλοκ. Η δεύτερη καθορίζει τις διαστάσεις του κάθε μπλοκ σε αριθμό νημάτων. Κάθε τέτοια παράμετρος είναι τύπου dim3, η οποία είναι μια C δομή (struct) με τρία πεδία unsigned integer, x, y και z και αντιστοιχούν στις τρεις διαστάσεις. 12

14 Για 1D ή 2D πλέγμα και μπλοκ, τα μη χρησιμοποιηθέντα πεδία διαστάσεων θα πρέπει να οριστούν σε 1 για λόγους σαφήνειας. Για παράδειγμα, ο παρακάτω κώδικας εξυπηρετητή (host) μπορεί να χρησιμοποιηθεί για να ξεκινήσει την συνάρτηση vectaddkernel() του πυρήνα και να δημιουργήσει ένα 1D πλέγμα που αποτελείται από 128 μπλοκ, καθένα από τα οποία αποτελείται από 32 νήματα. dim3 dimblock(128,1,1); dim3 dimgrid(32,1,1); vectaddkernel <<<dimgrid, dimblock>>>(...); Τα dimblock και dimgrid είναι μεταβλητές κώδικα του εξυπηρετητή (host) που ορίζεται από τον προγραμματιστή. Αυτές οι μεταβλητές μπορούν να έχουν ονόματα εφ' όσον είναι τύπου dim3 και η κλίση του πυρήνα χρησιμοποιεί τα αντίστοιχα ονόματα. Οι διαστάσεις του πλέγματος και του μπλοκ μπορεί να υπολογιστούν και από άλλες μεταβλητές. Αυτό επιτρέπει ο αριθμός των μπλοκ να ποικίλει ανάλογα με το μέγεθος των διανυσμάτων, έτσι ώστε το πλέγμα θα έχει αρκετά νήματα για να καλύψει όλα τα διανυσματικά στοιχεία. Η τιμή της μεταβλητής n κατά την κλίση του πυρήνα θα καθορίσει τη διάσταση του πλέγματος. Εάν το n είναι ίσο με 1000, το πλέγμα θα αποτελείται από τέσσερα τετράγωνα. Εάν n είναι ίσο με 4000, το πλέγμα θα έχει 16blocks. Σε κάθε περίπτωση, θα υπάρχουν αρκετά νήματα για να καλύψει όλα τα διανυσματικά στοιχεία. Μόλις κληθεί η vectaddkernel(), οι διαστάσεις του δικτύου και του μπλοκ θα παραμείνουν ίδια μέχρι το συνολικό πλέγμα να τελειώνει την εκτέλεση του. Για λόγους ευκολίας, η CUDA C παρέχει μια ειδική συντόμευση για την κλίση ενός πυρήνα 1D πλέγματος και μπλοκ. Αντί να χρησιμοποιήσουμε dim3 μεταβλητές, μπορεί κανείς να χρησιμοποιήσει τις αριθμητικές εκφράσεις για να καθορίσει την διαμόρφωση των 1D πλεγμάτων και μπλοκ. Στην περίπτωση αυτή, ο μεταγλωττιστής (compiler) CUDA C παίρνει απλά την αριθμητική έκφραση της διάστασης x και υποθέτει ότι η y και z διαστάσεις είναι 1. Στην CUDA C, οι επιτρεπόμενες τιμές των griddim.x, griddim.y, και griddim.z κυμαίνονται από 1 έως Τα μπλοκ οργανώνονται σε 3D πίνακες νημάτων. Δύο διαστάσεων μπλοκ μπορεί να δημιουργηθούν από τον καθορισμό του z σε 1. Μονοδιάστατο μπλοκ μπορεί να δημιουργηθεί από τον καθορισμό του y και z σε 1, όπως και στο παράδειγμα vectaddkernel. Όπως προαναφέρθηκε, όλα τα μπλοκ σε ένα πλέγμα έχουν τις ίδιες διαστάσεις. Ο αριθμός των νημάτων σε κάθε διάσταση του μπλοκ καθορίζεται από τη δεύτερη παράμετρο κατά την κλίση του πυρήνα. 13

15 Σχήμα 1.6 Πολυδιάστατο παράδειγμα οργάνωσης του πλέγματος CUDA grid Το πλέγμα μπορεί να έχει μεγαλύτερη διάσταση από τα μπλοκ του και το αντίστροφο. Για παράδειγμα, το σχήμα 1.6 δείχνει ένα μικρό παράδειγμα ενός 2D grid (2, 2, 1) που αποτελείται από 3D μπλοκ (4, 2, 2). Το πλέγμα μπορεί να δημιουργηθεί με τον παρακάτω κώδικα στον εξυπηρετητή (host): dim3 dimblock(2,2,1); dim3 dimgrid(4,2,2); vectaddkernel <<<dimgrid, dimblock>>>(...); Το πλέγμα αποτελείται από τέσσερα μπλοκ οργανωμένα σε έναν πίνακα 2 x 2. Κάθε μπλοκ σημειώνεται με (blockidx.y, blockidx.x). Για παράδειγμα, το block (1,0) έχει blockidx.y = 1 και blockidx.x = 0. Η διάταξη των ετικετών είναι τέτοια ώστε η υψηλότερη διάσταση να έρχεται πρώτη. Αυτή είναι η αντίστροφη της σειράς που χρησιμοποιείται στις παραμέτρους διαμόρφωσης, όπου η χαμηλότερη διάσταση έρχεται πρώτη. Αυτή η ιδιαιτερότητα λειτουργεί καλύτερα όταν απεικονίζουμε την χαρτογράφηση των συντεταγμένων των νημάτων σε δεδομένα όσον αφορά την πρόσβαση σε πολυδιάστατους πίνακες. Επίσης, κάθε threadidx αποτελείται επίσης από τρία πεδία: την x συντεταγμένη threadid.x, την y συντεταγμένη threadidx.y, και την z συντεταγμένη threadidx.z. 14

16 Χαρτογράφηση νημάτων για πολυδιάστατα δεδομένα Η επιλογή για 1D, 2D, ή 3D οργάνωση νημάτων είναι συνήθως με βάση τη φύση των δεδομένων. Για παράδειγμα, οι εικόνες είναι ένας 2D πίνακας εικονοστοιχείων. Είναι συχνά βολικό να χρησιμοποιείτε 2D πλέγμα που αποτελείται από 2D μπλοκ για να επεξεργαστεί τα εικονοστοιχεία (pixels). Το σχήμα 1.7 δείχνει μια τέτοια διάταξη για την επεξεργασία μιας εικόνας 76x62 (οριζόντια x, κάθετη y). Ας υποθέσουμε ότι έχουμε αποφασίσει να χρησιμοποιήσει ένα 16x16 μπλοκ, με 16 νήματα στην κατεύθυνση x και 16 νήματα στην κατεύθυνση y. Θα χρειαστεί πέντε τετράγωνα κατά τη διεύθυνση x και τέσσερα τετράγωνα στην κατεύθυνση y, οδηγώντας σε 5 x 4 = 20 μπλοκ, όπως φαίνεται στο σχήμα. Η σκιασμένη περιοχή απεικονίζει τα νήματα που καλύπτουν τα εικονοστοιχεία. Σημειώστε ότι έχουμε τέσσερα επιπλέον νήματα στην κατεύθυνση x και δύο επιπλέον νήματα στην κατεύθυνση y. Δηλαδή, θα δημιουργήσει 80 x 64 νήματα για την επεξεργασία 76 x 62 εικονοστοιχείων. Σχήμα 1.7 Χαρτογράφηση εικόνας σε νήματα Αυτό είναι παρόμοιο με την κατάσταση όπου ένα διάνυσμα 1000 στοιχείων επεξεργάζεται από την 1D vecaddkernel στο Σχήμα 1.8 χρησιμοποιώντας τέσσερα 256 μπλοκ νημάτων. Θυμηθείτε ότι μια εντολή if είναι αναγκαία για να αποφευχθούν τα επιπλέον 24 νήματα από την έναρξη ισχύος τους. Κατ' αναλογία, θα πρέπει να περιμένουμε ότι ο πυρήνας εκτέλεσης επεξεργασίας εικόνας έχει δηλώσεις για να ελέγχει εάν οι δείκτες νημάτων threadidx.x και threadidx.y εμπίπτουν στο έγκυρο εύρος των εικονοστοιχείων. Ας υποθέσουμε ότι ο κώδικας του εξυπηρετητή χρησιμοποιεί μια μεταβλητή ακεραίου n για να παρακολουθεί τον αριθμό των εικονοστοιχείων στην κατεύθυνση x, και μια άλλη μεταβλητή ακεραίου m για να παρακολουθεί τον αριθμό των εικονοστοιχείων στην y κατεύθυνση. Θα υποθέσουμε ότι η εικόνα εισόδου δεδομένων έχει αντιγραφεί στη μνήμη της συσκευής και μπορεί να προσεγγιστεί μέσω μιας μεταβλητής δείκτη d_pin. 15

17 Σχήμα 1.8 Indexing των block και thread Η εικόνα εξόδου έχει ανατεθεί στην μνήμη της συσκευής και μπορεί να προσεγγιστεί μέσω μιας μεταβλητή δείκτη d_pout. Ο ακόλουθος κώδικας υποδοχής μπορεί να χρησιμοποιηθεί για να ξεκινήσει έναν 2D πυρήνα για την επεξεργασία της εικόνας: dim3 dimblock (ceil(n/16.0), ceil(m/16.0), 1); dim3 dimgrid(16,16,1); vectaddkernel <<<dimgrid, dimblock>>>(d_pin, d_pout, n, m); Σε αυτό το παράδειγμα, υποθέτουμε για απλότητα ότι οι διαστάσεις των μπλοκ καθορίζονται σε 16 x 16. Οι διαστάσεις του πλέγματος, από την άλλη πλευρά, εξαρτώνται από τις διαστάσεις της εικόνας. Για να επεξεργαστείτε μια x (3M εικονοστοιχεία) εικόνα, θα δημιουργήσουμε μπλοκ, 150 κατά τη διεύθυνση x και 94 κατά τη διεύθυνση y. Ιδανικά, θα θέλαμε να έχουμε πρόσβαση σε 2D πίνακα μέσω του d_pin, όπου ένα στοιχείο στην j γραμμή και τη στήλη i μπορεί να προσεγγιστεί ως d_pin [j] [i]. Ωστόσο, το πρότυπο ANSI C, με βάση τις οποίες αναπτύχθηκε η CUDA C, προϋποθέτει ότι ο αριθμός των στηλών στο d_pin είναι γνωστός κατά τη μεταγλώττιση. Δυστυχώς, αυτή η πληροφορία δεν είναι γνωστή κατά το χρόνο μεταγλώττισης για δυναμικά κατανεμημένους πίνακες (dynamically allocated arrays). Όντως, ένας από τους λόγους για τους οποίους κάποιος χρησιμοποιεί δυναμικά κατανεμημένους πίνακες είναι να επιτρέψει το μέγεθος και οι διαστάσεις των πινάκων να ποικίλλουν ανάλογα με το μέγεθος των δεδομένων κατά το χρόνο εκτέλεσης. Συνεπώς, οι πληροφορίες σχετικά με τον αριθμό των στηλών σε μια δυναμικά κατανεμημένο 2D πίνακα δεν είναι γνωστό κατά τη μεταγλώττιση εκ του σχεδιασμού. Ως αποτέλεσμα, οι προγραμματιστές πρέπει να γραμμικοποιήσουν, ή αλλιώς "flatten" έναν δυναμικά κατανεμημένο 2D πίνακα σε ένα ισοδύναμο 1D πίνακα στην τρέχουσα CUDA C. Το νεότερο πρότυπο C99 επιτρέπει την πολυδιάστατη σύνταξη για δυναμικά κατανεμημένους πίνακες. Είναι πιθανό ότι οι μελλοντικές εκδόσεις της CUDA C μπορεί να υποστηρίξουν πολυδιάστατη σύνταξη για δυναμικά κατανεμημένους πίνακες. Η σημερινή έκδοση CUDA C μεταγλωττιστής (compiler) αφήνει το εν λόγω έργο στους προγραμματιστές λόγω έλλειψης πληροφορίας για τις διαστάσεις. 16

18 Υπάρχουν τουλάχιστον δύο τρόποι που μπορεί κανείς να γραμμικοποιήσει έναν 2D πίνακα. Ο ένας είναι να τοποθετήσουμε όλα τα στοιχεία της ίδιας γραμμής σε διαδοχικές θέσεις. Οι γραμμές τοποθετούνται στη συνέχεια η μία μετά την άλλη μέσα στον χώρο μνήμης. Η διάταξη αυτή, που ονομάζεται row-major layout, απεικονίζεται στο σχήμα 1.9. Για να αυξήσουμε την αναγνωσιμότητα, θα χρησιμοποιήσουμε Μ i,j για να υποδηλώσει ένα στοιχείο M στην γραμμή j και το στήλη i. Η Μ i,j είναι ισοδύναμο με την έκφραση C M [j] [i], αλλά πιο ευανάγνωστη. Σχήμα 1.9 Row-major layout σε πίνακα στην C Το σχήμα 1.9 δείχνει ένα παράδειγμα όπου ένας πίνακα Μ 4x4 γραμμικοποιείται σε 1D πίνακα 16-στοιχείων, με όλα τα στοιχεία της πρώτης γραμμής 0, ακολουθούμενη από τα τέσσερα στοιχεία της γραμμής 1, κ.λπ. Ως εκ τούτου, ο δείκτης του ισοδύναμου 1D πίνακα για το στοιχείο M στην γραμμή j και στήλη i είναι j x 4 + i. Ο όρος j x 4 παραλείπει όλα τα στοιχεία των σειρών πριν από την γραμμή j. Ο όρος i επιλέγει στη συνέχεια το σωστό στοιχείο στο τμήμα της γραμμής j. Για παράδειγμα, ο δείκτης 1D για το Μ 2,1 είναι 2 x = 9. Αυτό απεικονίζεται στο σχήμα 1.9, όπου Μ 9 είναι το ισοδύναμο 1D του Μ 2,1. Αυτός είναι ο τρόπος που με τον οποίο οι C μεταγλωττιστές (compilers) γραμμικοποιούν τους 2D πίνακες. Συγχρονισμός και διαφανής επεκτασιμότητα Η CUDA επιτρέπει τα νήματα στο ίδιο μπλοκ να συντονίσουν τις δραστηριότητες τους χρησιμοποιώντας μια συνάρτηση συγχρονισμού φράγματος (barrier) την syncthreads (). Όταν μια συνάρτηση πυρήνα καλεί την syncthreads (), όλα τα νήματα σε ένα μπλοκ θα περιμένουν μέχρι το κάθε νήμα στο μπλοκ να φτάσει στη ίδια θέση. Αυτό εξασφαλίζει ότι όλα τα νήματα σε ένα μπλοκ έχουν ολοκληρώσει μια φάση της εκτέλεσής του πυρήνα πριν οποιαδήποτε από αυτά περάσει στην επόμενη φάση. 17

19 Ο συγχρονισμός με φράγμα είναι μια απλή και δημοφιλής μέθοδος συντονισμού παράλληλων δραστηριοτήτων. Στην πραγματική ζωή, χρησιμοποιούμε συχνά συγχρονισμό με barrier για τον συντονισμό παράλληλων δραστηριοτήτων πολλών ατόμων. Το σχήμα 1.9 απεικονίζει την εκτέλεση του συγχρονισμού φραγμού. Υπάρχουν Ν νήματα στο μπλοκ. Μερικά από τα νήματα φθάνουν στην κατάσταση συγχρονισμού φραγμού νωρίς και κάποια από αυτά πολύ αργότερα. Τα νήματα που φτάνουν το φράγμα νωρίς θα περιμένουν για τα υπόλοιπα που θα φθάσουν αργότερα. Όταν το τελευταίο νήμα φτάνει στο φράγμα, όλα μαζί μπορούν να συνεχίσουν την εκτέλεση. Στην CUDA, μια syncthreads() δήλωση πρέπει να εκτελεστεί από όλα τα νήματα σε ένα μπλοκ. Όταν μια syncthread() δήλωση τοποθετείται σε μια if statement θα εκτελέσουν είτε όλα τα νήματα στο μπλοκ την ίδια διαδρομή που περιλαμβάνει τα syncthreads() είτε κανένα από αυτά. Για μια if-then-else, αν κάθε μονοπάτι έχει μια syncthreads() δήλωση, είτε όλα τα θέματα σε ένα μπλοκ εκτελούν τις syncthreads() στο μονοπάτι του then statement ή όλα εκτελούν το άλλο μονοπάτι του else statement. Οι δύο syncthreads() δηλώσεις είναι στην ουσία διαφορετικά σημεία συγχρονισμού φράγματος. Αν ένα νήμα σε ένα μπλοκ εκτελεί το μονοπάτι της then και στη συνέχεια ένα άλλο νήμα εκτελεί το μονοπάτι του else, τα νήματα θα περιμένουν σε διαφορετικά σημεία συγχρονισμού. Έτσι θα καταλήξουν να περιμένουν για πάντα. Είναι ευθύνη των προγραμματιστών να γράφουν κώδικα ώστε να πληρούνται οι απαιτήσεις αυτές. Σχήμα 1.9 Χρόνος εκτέλεσης και συγχρονισμός με barrier Η ικανότητα για συγχρονισμό επιβάλλει επίσης περιορισμούς εκτέλεσης σε νήματα μέσα σε ένα μπλοκ. Αυτά τα νήματα θα πρέπει να εκτελούνται με στενή χρονική εγγύτητα μεταξύ τους για να αποφευχθεί υπερβολικά μεγάλος χρόνος αναμονής. Όντως, κάποιος πρέπει να βεβαιωθεί ότι όλα τα νήματα που εμπλέκονται στο συγχρονισμό φράγματος έχουν πρόσβαση στους αναγκαίους πόρους ώστε τελικά να φτάσει στο σημείο συγχρονισμού. Σε αντίθετη περίπτωση, ένα νήμα που δεν έφτασε ποτέ στο σημείο 18

20 συγχρονισμού φράγματος μπορεί να προκαλέσει όλα τα άλλα να περιμένουν για πάντα. Τα συστήματα CUDA runtime πληρούν αυτόν τον περιορισμό με την ανάθεση εκτέλεσης των πόρων σε όλα τα νήματα σε ένα μπλοκ ως μονάδα. Ένα μπλοκ μπορεί να αρχίσει την εκτέλεση μόνο όταν το runtime σύστημα έχει εξασφαλίσει όλες τις πηγές που χρειάζονται για όλα τα νήματα στο μπλοκ για την ολοκλήρωση της εκτέλεσης. Όταν ένα νήμα από ένα μπλοκ αποδίδεται σε έναν πόρο εκτέλεσης, όλα τα άλλα νήματα στο ίδιο μπλοκ επίσης εκχωρούνται στον ίδιο πόρο. Αυτό εξασφαλίζει τη χρονική εγγύτητα για όλα τα νήματα σε ένα μπλοκ και αποτρέπει την υπερβολική ή αορίστου χρόνου αναμονή κατά το συγχρονισμό φράγματος. Αυτό μας οδηγεί σε ένα σημαντικό trade-off στο σχεδιασμό συγχρονισμού του CUDA φράγματος (barrier). Με το να μην επιτρέπει στα νήματα σε διαφορετικά μπλοκ να εκτελούν συγχρονισμό φράγματος μεταξύ τους, το runtime σύστημα CUDA μπορεί να εκτελέσει τα μπλοκ σε οποιαδήποτε σειρά σε σχέση με τα υπόλοιπα, αφού κανένα από αυτά δεν χρειάζεται να περιμένει τα υπόλοιπα. Αυτή η ευελιξία επιτρέπει κλιμακούμενες υλοποιήσεις όπως φαίνεται στο Σχήμα 1.10, όπου ο χρόνος προχωρά από την κορυφή Σχήμα 1.10 Έλλειψη συγχρονισμού μεταξύ των μπλοκ επιτρέπει κλιμάκωση στα προγράμματα προς τα κάτω. Σε ένα σύστημα χαμηλού κόστους με λίγους μόνο πόρους εκτέλεσης, μπορεί κανείς να εκτελέσει ένα μικρό αριθμό μπλοκ ταυτόχρονα π.χ. δύο μπλοκ εκτέλεσης σε μια στιγμή όπως εμφανίζονται στην αριστερή πλευρά του Σχήματος Σε μια ακριβή (high-end) εφαρμογή με περισσότερους πόρους εκτέλεσης, μπορεί κανείς να εκτελέσει ένα μεγάλο αριθμό από μπλοκ ταυτόχρονα π.χ. τέσσερα μπλοκ εκτέλεσης σε μια στιγμή. Η ικανότητα να εκτελεί τον ίδιο κώδικα της εφαρμογής σε ένα ευρύ φάσμα ταχυτήτων επιτρέπει την παραγωγή ενός μεγάλου εύρους φάσματος εφαρμογών ανάλογα με το κόστος, τη δύναμη και τις απαιτήσεις επιδόσεων σε συγκεκριμένα τμήματα της αγοράς. 19

21 Προγραμματισμός νημάτων και ανοχή καθυστέρησης Ο προγραμματισμός νημάτων είναι αυστηρά μια ιδέα και κατά συνέπεια θα πρέπει να συζητηθεί στο πλαίσιο της συγκεκριμένες υλοποιήσεις υλικού. Στις περισσότερες εφαρμογές μέχρι σήμερα, όταν ένα μπλοκ έχει εκχωρηθεί σε ένα SM, διαιρείται περαιτέρω σε μονάδες των 32 νημάτων δηλαδή τα στημόνια. Το μέγεθος των στημονιών είναι συγκεκριμένο για κάθε εφαρμογή και δεν αποτελούν μέρος των προδιαγραφών του CUDA. Ωστόσο, είναι χρήσιμα για την κατανόηση και τη βελτιστοποίηση της απόδοσης των εφαρμογών CUDA. Το μέγεθος των στημονιών είναι μια ιδιότητα της εκάστοτε συσκευής CUDA, το οποίο βρίσκεται στο πεδίο της μεταβλητής dev_prop.warpsize (dev_prop). Το στημόνια είναι μονάδα προγραμματισμού των νημάτων στις SMs. Το σχήμα 1.11 δείχνει την κατανομή των μπλοκ σε στημόνια σε μια εφαρμογή. Κάθε στημόνι αποτελείται από 32 νήματα διαδοχικών τιμών threadidx: τα νήματα 0-31 για το πρώτο στημόνι, το δεύτερο στημόνι, και ούτω καθ εξής. Σε αυτό το παράδειγμα, υπάρχουν τρία μπλοκ block1, block2 και block3, όλα σε μια SM. Κάθε ένα από τα τρία μπλοκ διαιρείται περαιτέρω σε στημόνια για προγραμματιστικούς λόγους. Σχήμα 1.11 Επιμερισμός των block σε warps για thread scheduling Μπορούμε να υπολογίσουμε τον αριθμό των στημονιών που κατοικούν στον SM για ένα δεδομένο μέγεθος μπλοκ και ένα δεδομένο αριθμό από μπλοκ που αποδίδεται σε κάθε SM. Για παράδειγμα, εάν κάθε μπλοκ έχει 256 νήματα, μπορούμε να υπολογίσουμε ότι κάθε μπλοκ έχει 256 = 8 warps. Με τρία μπλοκ σε κάθε SM, έχουμε 8 x 3 = 24 στημόνια 32 σε κάθε SM. 20

22 Η κάθε SM έχει σχεδιαστεί για να εκτελεί όλα τα νήματα σε ένα στημόνι βάσει το SIMD μοντέλο. Σε οποιαδήποτε χρονική στιγμή, μια εντολή γίνεται fetched και εκτελείται για όλα τα νήματα του στημονιού. Τα νήματα θα εκτελέσουν την ίδια εντολή για διαφορετικά τμήματα των δεδομένων. Ως αποτέλεσμα, όλα τα νήματα στο στημόνι θα έχουν πάντα την ίδια χρονική στιγμή εκτέλεσης. Όταν μια εντολή εκτελείται από τα νήματα σε ένα στημόνι θα πρέπει να περιμένουμε το αποτέλεσμα μιας ήδη εκτελούμενης λειτουργίας με μεγάλη καθυστέρηση, και έτσι το στημόνι δεν έχει επιλεγεί για εκτέλεση. Ένα άλλο στημόνι που δεν είναι πλέον σε αναμονή για τα αποτελέσματα θα επιλεγεί για την εκτέλεση. Εάν περισσότερα από ένα στημόνια είναι έτοιμα για εκτέλεση, χρησιμοποιείται ένας μηχανισμός προτεραιότητας και επιλέγει ένα για εκτέλεση. Αυτός ο μηχανισμός της πλήρωσης του χρόνου καθυστέρησης των εργασιών με εργασία από άλλα νήματα συχνά καλείται ανοχή καθυστέρησης ή απόκρυψη καθυστέρησης (latency tolerance or latency hiding). Ο προγραμματισμός των στημονιών (warp) χρησιμοποιείται επίσης για την ανοχή άλλων τύπων λειτουργιών, όπως pipelined floating-point αριθμητική και εντολές διακλάδωσης. Με αρκετά στημόνια, το υλικό πιθανότατα εντοπίζει ένα στημόνι να εκτελεστεί σε οποιοδήποτε χρονικό σημείο, καθιστώντας έτσι δυνατή την πλήρη χρήση του υλικού παρόλη την εκτέλεση μακρόχρονων λειτουργιών. Η επιλογή των έτοιμων στημονιών για την εκτέλεση δεν εισάγει χρόνο αδράνειας στο χρονοδιάγραμμα εκτέλεσης, η οποία αναφέρεται ως μηδενική επιβάρυνση προγραμματισμού νημάτων (zero-overhead thread scheduling). Με τον προγραμματισμό στημονιών, το μεγάλο χρονικό διάστημα αναμονής των εντολών του είναι «κρυμμένο» εκτελώντας εντολές άλλων στημονιών. Αυτή η ικανότητα στην ανοχή μεγάλων χρονικών καθυστερήσεων είναι ο κύριος λόγος για τον οποίο οι GPU δεν αφιερώνουν τόσο μεγάλη επιφάνεια του τσιπ σε λανθάνουσα μνήμη (cache) και μηχανισμούς πρόβλεψης διακλάδωσης, όπως η CPU. Ως αποτέλεσμα, οι GPU αφιερώνουν περισσότερη περιοχή του τσιπ σε πόρους διαθέσιμους για εντολές κινητής υποδιαστολής (floating-point). 2.3 Οργάνωση της CUDA Memory (global, constant, shared). Η CUDA υποστηρίζει διάφορους τύπους μνήμης που μπορούν να χρησιμοποιηθούν από τους προγραμματιστές ώστε να επιτευχθεί ένας υψηλός συντελεστής CGMA και έτσι μια υψηλή ταχύτητα εκτέλεσης πυρήνες τους. Έστω ο παρακάτω κώδικας: Σε κάθε επανάληψη του βρόχου, εκτελούνται δύο προσπελάσεις της παγκόσμιας μνήμης μία για πολλαπλασιασμό κινητής υποδιαστολής και μία πρόσθεση κινητής υποδιαστολής. Μια πρόσβαση στη παγκόσμια μνήμη φέρνει ένα στοιχείο του d_m [ ] και μια ακόμα φέρνει ένα στοιχείο του d_n [ ]. Μία λειτουργία κινητής υποδιαστολής πολλαπλασιάζει τον d_m [ ] με τον d_n [ ] και το αποτέλεσμα συσσωρεύεται στην μεταβλητή Pvalue. Έτσι, η αναλογία της κινητής υποδιαστολής υπολογισμού για την λειτουργία προσπέλασης της παγκόσμιας μνήμης είναι 1:1, ή 1.0. O συντελεστής (CGMA Compute 21

23 to Global Memory Access), ορίζεται ως ο αριθμός των υπολογισμών κινητής υποδιαστολής για κάθε πρόσβαση στην παγκόσμια μνήμη μέσα σε μια περιοχή ενός προγράμματος CUDA και παίζει σημαντικό ρόλο στην απόδοση των προγραμμάτων. Στο κάτω μέρος του σχήματος 1.12, βλέπουμε την παγκόσμια και constant μνήμη. Αυτοί οι τύποι μνημών μπορούν να γράφουν (W) και να διαβάζουν (R) από τον εξυπηρετητή με την κλήση API συναρτήσεων. Η constant μνήμη υποστηρίζει μικρή καθυστέρηση, υψηλό εύρος ζώνης, πρόσβαση μόνο για ανάγνωση από τη συσκευή, όταν όλα τα νήματα έχουν ταυτόχρονη πρόσβαση στην ίδια θέση. Ο κώδικας του Device μπορεί να: R/W ανά-thread registers R/W ανά-thread local memory R/W ανά-block shared memory R/W ανά-grid global memory Read only ανά-grid constant memory Ο κώδικας για τον Host μπορεί να: Μεταφέρει data προς/από ανά-grid global και constant memories Σχήμα 1.12 Γενική όψη μοντέλου μνήμης CUDA Οι καταχωρητές και η κοινόχρηστη μνήμη είναι on-chip μνήμες. Οι μεταβλητές που βρίσκονται σε αυτούς τους τύπους μνήμης μπορούν να προσπελαθούν με πολύ μεγάλη ταχύτητα με ένα εξαιρετικά παράλληλο τρόπο. Οι καταχωρητές κατανέμονται σε επιμέρους νήματα και κάθε νήμα μπορεί να έχει πρόσβαση μόνο στους δικούς του καταχωρητές. Μια συνάρτηση πυρήνα (kernel) τυπικά χρησιμοποιεί καταχωρητές για να αποθηκεύσει μεταβλητές με συχνή προσπέλαση και είναι ιδιωτικές (private) για κάθε νήμα. Η κοινόχρηστη μνήμη κατανέμεται στα μπλοκ νημάτων όλα τα νήματα σε ένα μπλοκ μπορεί να έχουν πρόσβαση σε μεταβλητές που βρίσκονται στις κοινές θέσεις μνήμης που διατίθενται για το μπλοκ. Η κοινόχρηστη μνήμη είναι ένα αποτελεσματικό μέσο για συνεργασία μεταξύ των νημάτων με την ανταλλαγή δεδομένων των εισόδων τους και τα ενδιάμεσα αποτελέσματα των εργασιών τους. Δηλώνοντας τους τύπους μνήμης CUDA, ένας προγραμματιστής υπαγορεύει στην ουσία την ορατότητα (visibility) και την ταχύτητα της μεταβλητής μέσα στο πρόγραμμα. Το μοντέλο προγραμματισμού της παγκόσμιας (global) μνήμης σε CUDA ακολουθεί το κλασσικό μοντέλο του von Neumann. Η επεξεργαστική μονάδα στο σχήμα 1.13 αντιστοιχεί στο όριο των επεξεργαστών που συνήθως βλέπουμε σήμερα. Η παγκόσμιας μνήμη είναι off-chip και υλοποιείται με τεχνολογία DRAM, η οποία συνεπάγει μεγάλες καθυστερήσεις πρόσβασης και χαμηλό εύρος ζώνης. Οι καταχωρητές αντιστοιχούν στο 22

24 register file του μοντέλου von Neumann και είναι στο die του επεξεργαστή, συνεπάγεται δηλαδή πολύ μικρή καθυστέρηση πρόσβασης και δραστικά μεγαλύτερο Σχήμα 1.13 Μνήμη και καταχωρητές στο μοντέλο von Neumann εύρος ζώνης πρόσβασης. Σε μία συσκευή, το εύρος ζώνης πρόσβασης του αρχείου καταχωρητών (register file) είναι παραπάνω περίπου δύο τάξεις μεγέθους απ ότι της παγκόσμιας μνήμης. Επιπλέον, κάθε φορά που μια μεταβλητή έχει αποθηκευτεί σε έναν καταχωρητή, η πρόσβαση σε αυτήν δεν καταναλώνει πλέον off-chip εύρος ζώνης της global μνήμης. Αυτό αντικατοπτρίζεται ως μια αύξηση του συντελεστή CGMA. Η πρόσβαση στους καταχωρητές περιλαμβάνει λιγότερες οδηγίες από την πρόσβαση στην παγκόσμιας (global) μνήμη. Στο σχήμα 1.13, ο επεξεργαστής χρησιμοποιεί τον PC για να φέρει (fetch) εντολές από τη μνήμη στον IR. Στη συνέχεια χρησιμοποιούνται τα bits της (fetched) εντολής για να ελέγχει τις δραστηριότητες του υπολογιστή (instruction execution). Ο αριθμός των εντολών που μπορεί να ληφθεί και να εκτελούνται σε κάθε κύκλο ρολογιού είναι περιορισμένος. Ως εκ τούτου, όσο περισσότερες εντολές πρέπει να εκτελεστούν σε ένα πρόγραμμα, τόσο περισσότερο χρόνο θα χρειαστεί για να εκτελεστεί το πρόγραμμα. Οι αριθμητικές εντολές σε πιο σύγχρονους επεξεργαστές έχουν ενσωματωμένους builtin τελεστές καταχωρητών (register operands). Για παράδειγμα, μια τυπική πρόσθεση κινούμενης υποδιαστολής είναι της μορφής fadd r1, r2, r3 όπου r2 και r3 είναι οι αριθμοί καταχωρητών που προσδιορίζουν τη θέση στο αρχείο καταχωρητών, όπου βρίσκονται οι τιμές των τελεστών εισόδου. Η θέση για την αποθήκευση του αποτελέσματος καθορίζεται από το r1. Ως εκ τούτου, όταν ένας τελεστής εντολής βρίσκεται σε καταχωρητή, όταν γίνεται ο υπολογισμός δεν χρειάζεται καμία πρόσθετη εντολή για να κάνει το τελεστή διαθέσιμο στην αριθμητική και λογική μονάδα (ALU). 23

25 Από την άλλη πλευρά, αν μια τιμή τελεστή είναι στην παγκόσμια (global) μνήμη, πρέπει να εκτελεστεί η διαδικασία φόρτωσης από την μνήμη ώστε να κάνει το τελεστή διαθέσιμο στην ALU. Για παράδειγμα, εάν ο πρώτος τελεστής μιας πράξης πρόσθεσης κινητής υποδιαστολής είναι στην παγκόσμια (global) μνήμη ενός τυπικού υπολογιστή, οι εντολές που εμπλέκονται θα είναι: load r2, r4, offset fadd r1, r2, r3 όπου η εντολή φόρτωσης προσθέτει μια τιμή μετατόπισης (offset) στο περιεχόμενα του r4 για να δημιουργήσει την διεύθυνση του τελεστή. Στην συνέχεια γίνεται πρόσβαση στην παγκόσμιας (global) μνήμη και τοποθετεί την τιμή στον καταχωρητή r2. Η εντολή fadd εκτελεί την πρόσθεση κινητής υποδιαστολής χρησιμοποιώντας τις τιμές των r2 και r3 και τοποθετεί το αποτέλεσμα στον r1. Δεδομένου ότι ο επεξεργαστής μπορεί να φέρει και να εκτελέσει έναν περιορισμένο αριθμό εντολών ανά κύκλο ρολογιού, η έκδοση με ένα επιπλέον φορτίο θα λάβει πιθανόν περισσότερο χρόνο για την επεξεργασία. Σε σύγχρονους υπολογιστές, η ενέργεια που καταναλώνεται για την πρόσβαση σε μια τιμή από το αρχείο καταχωρητών είναι τουλάχιστον μία τάξη μεγέθους μικρότερη από ό, τι η πρόσβαση σε μια τιμή από την παγκόσμια μνήμη. O αριθμός των καταχωρητών που διατίθενται σε κάθε νήμα είναι αρκετά περιορισμένη στις σημερινές GPUs. Το σχήμα 1.14 δείχνει την κοινόχρηστη μνήμη και τους καταχωρητές σε μια συσκευή CUDA. Η κοινόχρηστη μνήμη έχει σχεδιαστεί ως μέρος του χώρου μνήμης και βρίσκεται στο chip του επεξεργαστή. Όταν ο επεξεργαστής προσπελαύνει τα δεδομένα στην κοινόχρηστη μνήμη, χρειάζεται να εκτελέσει μια λειτουργία φόρτωσης (load) της μνήμης, όπως και για την πρόσβαση στα δεδομένα της παγκόσμιας (global) μνήμης. Ωστόσο, επειδή η κοινόχρηστη μνήμη είναι on-chip, μπορεί να προσεγγιστεί με πολύ χαμηλότερο καθυστέρηση (latency) και πολύ μεγαλύτερο εύρος ζώνης από την παγκόσμια μνήμη. Λόγω της ανάγκης να εκτελέσει μια λειτουργία του φορτώματος, η κοινόχρηστη μνήμη έχει μεγαλύτερη καθυστέρηση και χαμηλότερο εύρος ζώνης από τους καταχωρητές. Σχήμα 1.14 Shared μνήμη και καταχωρητές σε συσκευή CUDA 24

26 Μια σημαντική διαφορά μεταξύ της κοινόχρηστης μνήμης των καταχωρητών είναι ότι οι μεταβλητές που βρίσκονται στην κοινόχρηστη μνήμη είναι προσβάσιμες από όλα τα νήματα στο ίδιο μπλοκ. Η κοινόχρηστη μνήμη έχει σχεδιαστεί για να υποστηρίζει αποτελεσματικά, υψηλό εύρος ζώνης για ανταλλαγή δεδομένων μεταξύ των νημάτων σε ένα μπλοκ. Όπως φαίνεται στο Σχήμα 1.14, η συσκευή SM CUDA συνήθως χρησιμοποιεί πολλές μονάδες επεξεργαστών SP, για να επιτρέψει σε πολλαπλά νήματα ταυτόχρονη επεξεργασία. Τα νήματα σε ένα μπλοκ μπορούν να απλωθούν σε όλες αυτές τις μονάδες επεξεργασίας. Ως εκ τούτου, η εφαρμογή της κοινόχρηστης μνήμης σε αυτές τις συσκευές CUDA συνήθως σχεδιάζονται έτσι ώστε να επιτρέπουν σε πολλές υπολογιστικές μονάδες να έχουν ταυτόχρονη πρόσβαση στο περιεχόμενό της για αποτελεσματική ανταλλαγή δεδομένων μεταξύ των νημάτων σε ένα μπλοκ. Ο πίνακας παρουσιάζει τη σύνταξη CUDA για τη δήλωση των μεταβλητών με διάφορα είδη μνήμης της συσκευής. Κάθε τέτοια δήλωση δίνει επίσης την αντίστοιχη εμβέλεια (scope) και διάρκεια ζωής (lifetime). Η εμβέλεια προσδιορίζει το εύρος των θεμάτων που μπορούν να έχουν πρόσβαση σε μια μεταβλητή: με ένα νήμα μόνο, με όλα τα Δήλωση Μεταβλητών Μνήμη Εμβέλεια Χρόνος ζωής Αυτόματες μεταβλητές εκτός από πίνακες Register Thread Kernel Αυτόματες μεταβλητές πίνακα Local Thread Kernel device shared int SharedVar; Shared Block Kernel device int GlobalVar; Global Grid Application device constant int ConstVar; Constant Grid Application Πίνακας 1.1 Εμβέλεια μεταβλητών για είδη μνήμης νήματα ενός μπλοκ, είτε από όλα τα νήματα όλων των πλεγμάτων. Αν η εμβέλεια μιας μεταβλητής είναι ένα και μοναδικό νήμα, μια ιδιωτική έκδοση της μεταβλητής θα δημιουργηθεί για κάθε νήμα κάθε νήμα μπορεί να έχει πρόσβαση μόνο στην ιδιωτική έκδοση της μεταβλητής. Για παράδειγμα, εάν ένας πυρήνας δηλώνει μια μεταβλητή, της οποίας η εμβέλεια είναι ένα νήμα, και εκτελείται στην συνέχεια για ένα εκατομμύριο νήματα, η μεταβλητή θα δημιουργηθεί επίσης ένα εκατομμύριο φορές έτσι ώστε κάθε νήμα να χρησιμοποιεί τη δική του εκδοχή της μεταβλητής. 2.4 Pointers στην CUDA Στην CUDA, οι δείκτες χρησιμοποιούνται για να δείχνουν τα αντικείμενα δεδομένων στην παγκόσμια (global) μνήμη. Υπάρχουν δύο χαρακτηριστικοί τρόποι με τους οποίους global void MatrixMulKernel(float* d_m, float* d_n, float* d_p) {... Pvalue += d_m[...]*d_n[...]; d_p[...] = Pvalue; } } 25

27 η χρήση δείκτη τίθεται στον πυρήνα και στις συναρτήσεις της συσκευής. Πρώτον, εάν ένα αντικείμενο έχει κατανεμηθεί (allocated) από μια συνάρτηση εξυπηρετητή, ο δείκτης στο αντικείμενο αρχικοποιείται από την cudamalloc() και μπορεί να περάσει στην συνάρτηση κλίσης του πυρήνα ως παράμετρος, για παράδειγμα οι παράμετροι d_m, d_n και d_p στον παρακάτω κώδικα. Ο δεύτερος τύπος είναι να αναθέσει τη διεύθυνση μιας μεταβλητής που δηλώθηκε στην παγκόσμια (global) μνήμη σε έναν δείκτη μεταβλητής. Για παράδειγμα, η δήλωση σε μια συνάρτηση του πυρήνα, αναθέτει τη διεύθυνση του GlobalVar σε ένα αυτόματο δείκτη μεταβλητής ptr. Τέλος δεν επιτρέπεται να χρησιμοποιείται η διεύθυνση μιας συνάρτησης device στον κώδικα του εξυπηρετητή. {float* ptr = &GlobalVar;} Μια κλήση πυρήνα μοιάζει και συμπεριφέρεται ακριβώς όπως και κάθε κλήση της συνάρτησης στο πρότυπο C. Το σύστημα εκτέλεσης (runtime) φροντίζει οποιαδήποτε πολυπλοκότητα που περιέχει το γεγονός ότι αυτοί οι παράμετροι πρέπει να περαστούν από τον εξυπηρετητή στη συσκευή. Η πιο ενδιαφέρουσα προσθήκη είναι η κατανομή της μνήμης cudamalloc(). Η κλίση συμπεριφέρεται παρόμοια με το πρότυπο κλήσης malloc() στην C, αλλά ενημερώνει το CUDA runtime ώστε να κατανέμει τη μνήμη στην συσκευή. cudamalloc((void **) &dev_c, sizeof(int)) Η πρώτη παράμετρος είναι ένας δείκτης προς τον δείκτη που θα θέλαμε να κρατήσουμε τη νέα διεύθυνση κατανομής της μνήμης, και η δεύτερη παράμετρος είναι το μέγεθος της κατανομής που θέλουμε. Ο δείκτης μνήμης δεν είναι η τιμή επιστροφής της συνάρτησης, αυτό είναι ταυτόσημη συμπεριφορά της συνάρτησης malloc (), μέχρι και το void*. Αυτό αναδεικνύει ένα λεπτό αλλά σημαντικό σημείο. Μεγάλο μέρος της απλότητας και της ισχύος της CUDA C προέρχεται από την ικανότητα να καθιστά ασαφή τα όρια μεταξύ του κώδικα εξυπηρετητή και του κώδικα της συσκευής. Ωστόσο, είναι ευθύνη του προγραμματιστή να μην κάνει dereference τον δείκτη που επιστρέφεται από την cudamalloc() σε κώδικα που εκτελείται στον εξυπηρετητή. Ο κώδικας εξυπηρετητή μπορεί να χρησιμοποιεί αυτόν τον δείκτη, να εκτελέσει αριθμητικές πράξεις σε αυτό, ή ακόμα και να τον κάνει cast σε διαφορετικό τύπο, αλλά δεν μπορεί να χρησιμοποιηθεί για να διαβάσει ή να γράψει από τη μνήμη. Δυστυχώς, ο μεταγλωττιστής (compiler) δεν μπορεί να προστατεύσει από το λάθος αυτό. Επιτρέπει τα dereferences των δεικτών συσκευής μέσα από τον κώδικα του εξυπηρετητή επειδή μοιάζουν με οποιονδήποτε άλλο δείκτη στην εφαρμογή. 26

28 Μπορούμε να συνοψίσουμε τους περιορισμούς στη χρήση της συσκευής δείκτη ως εξής: Μπορούμε να περάσουμε δείκτες που κατανέμονται με cudamalloc () σε συναρτήσεις που εκτελούνται στη συσκευή GPU. Μπορούμε να χρησιμοποιήσουμε δείκτες που κατανέμονται με cudamalloc () για διάβασμα ή εγγραφή μνήμης από κώδικα που εκτελείτε στη συσκευή GPU. Μπορούμε να περάσουμε δείκτες που κατανέμονται με cudamalloc () σε συναρτήσεις που εκτελούνται στον εξυπηρετητή (host). Δεν μπορούμε να χρησιμοποιήσουμε δείκτες που κατανέμονται με cudamalloc () για διάβασμα ή εγγραφή μνήμης από κώδικα που εκτελείται στον εξυπηρετητή (host). Χρησιμοποιούμε δείκτες μέσα από τον κώδικα της συσκευής με τον ίδιο ακριβώς τρόπο που χρησιμοποιείται στην πρότυπη C που εκτελείται σε κώδικα εξυπηρετητή. Η δήλωση *c = a + b προσθέτει τις παραμέτρους a και b μαζί και αποθηκεύει το αποτέλεσμα στην μνήμη που υποδεικνύεται από τον c. Μπορούμε επίσης να αποκτήσουμε πρόσβαση μνήμης σε μια συσκευή μέσω κλήσεων στην cudamemcpy () από τον κώδικα εξυπηρετητή. Αυτές οι κλήσεις συμπεριφέρονται ακριβώς όπως τα τυποποιημένα στοιχεία C memcpy () με μια πρόσθετη παράμετρο που καθορίζει ποιος δείκτης εξυπηρετητή ή δείκτης προορισμού, δείχνει στη μνήμη της συσκευής. cudamemcpy(&a &dev_a, sizeof(int), cudamemcpydevicetohost) Στο παράδειγμα, παρατηρούμε ότι η τελευταία παράμετρος της cudamemcpy () είναι cudamemcpydevicetohost, ενημερώνοντας το runtime ότι ο δείκτης πηγής είναι ένας δείκτης της συσκευής και ο δείκτης προορισμού είναι δείκτης του εξυπηρετητή. 2.5 Άλλοι ειδικοί τύποι μνήμης Page-Locked Host Memory Το runtime παρέχει λειτουργίες που να επιτρέπουν τη χρήση της Page-Locked (επίσης γνωστή ως pinned) μνήμη εξυπηρετητή (σε αντίθεση με την τακτική σελιδοποιημένη (pageable) μνήμη του εξυπηρετητή που διατίθενται από την malloc() ): cudahostalloc() και cudafreehost() κατανέμουν και ελευθερώνουν page-locked μνήμη στον εξυπηρετητή (host). cudahostregister() δημιουργεί μια σειρά από page-locked μνήμη που κατανέμεται από την malloc(). Η page-locked μνήμη του εξυπηρετητή (host) είναι ένας περιορισμένος πόρος, έτσι ώστε οι κατανομές page-locked μνήμης θα αποτύχουν πολύ πριν από την κατανομή της σελιδοποιημένης μνήμης (pageable memory). Επιπλέον, μειώνοντας την ποσότητα της φυσικής μνήμης που είναι διαθέσιμη για το λειτουργικό σύστημα για σελιδοποίηση, η 27

29 χρήση υπερβολικής page-locked μνήμης μειώνει τη συνολική απόδοση του συστήματος. Texture Memory Η μνήμη υφής (texture) διαβάζεται από πυρήνες χρησιμοποιώντας τις λειτουργίες της συσκευής που περιγράφονται σαν texture συναρτήσεις (texture functions). Η διαδικασία της ανάγνωσης μια texture μνημες που πραγματοποιείται καλώντας μία από αυτές τις συναρτήσεις ονομάζεται texture fetch. Κάθε texture fetch καθορίζει μια παράμετρο που ονομάζεται ένα texture αντικείμενο για το API του texture αντικειμένου ή μια αναφορά texture για το API αναφοράς texture. Portable Memory Ένα μπλοκ της page-locked μνήμης μπορεί να χρησιμοποιηθεί σε συνδυασμό με οποιαδήποτε συσκευή στο σύστημα (Multi-Device System), αλλά από προεπιλογή, τα οφέλη από τη χρήση page-locked μνήμης είναι διαθέσιμα μόνο σε συνδυασμό με τη συσκευή που ήταν ενεργή όταν το μπλοκ είχε κατανέμει την μνήμη (και με όλες τις συσκευές που μοιράζονται το ίδιο ενιαίο χώρο διευθύνσεων, εάν υπάρχουν, Unified Virtual Address). Για να γίνουν διαθέσιμα αυτά τα πλεονεκτήματα σε όλες τις συσκευές, το μπλοκ πρέπει να κατανεμηθεί με την σημαία (flag) cudahostallocportable στην συνάρτηση cudahostalloc() ενεργοποιημένη ή για page-locked με το πέρασμα της σημαίας cudahostregisterportable στην cudahostregister(). Write-Combining Memory Από προεπιλογή η page-locked μνήμη του εξυπηρετητή (host) κατανέμεται ως λανθάνουσα (cacheable). Προαιρετικά μπορεί να κατανέμεται ως write-combining περνώντας την σημαία cudahostallocwritecombined στην cudahostalloc(). Η pagelocked μνήμη απελευθερώνει την λανθάνουσα L1 και L2 του εξυπηρετητή, καθιστώντας διαθέσιμη για το υπόλοιπο της αίτησης. Επιπλέον, η write-combined μνήμη δεν επηρεάζει τις μεταφορές σε όλο το PCI Express bus, το οποίο μπορεί να βελτιώσει την απόδοση της μεταφοράς μέχρι και 40%. Η ανάγνωση από write-combining μνήμη από τον εξυπηρετητή είναι απαγορευτικά αργή, έτσι θα πρέπει σε γενικές γραμμές να χρησιμοποιείτε για μνήμη όπου ο εξυπηρετητής γράφει μόνο. Mapped Memory Στις συσκευές υπολογιστικής ικανότητας μεγαλύτερη από 1.0, η page-locked μνήμη του εξυπηρετητή μπορεί επίσης να χαρτογραφηθεί (mapped) στον χώρο διευθύνσεων της συσκευής με το πέρασμα της σημαία cudahostallocmapped στην cudahostalloc() ή με το πέρασμα της σημαίας cudahostregistermapped στην cudahostregister(). Ένα τέτοιο μπλοκ έχει ως εκ τούτου σε γενικές γραμμές δύο διευθύνσεις: μία στη μνήμη του 28

30 εξυπηρετητή (host) που επιστρέφεται από την cudahostalloc() ή malloc(), και μία στη μνήμη της συσκευής που μπορεί να ανακτηθεί χρησιμοποιώντας την cudahostgetdevicepointer() και στη συνέχεια να χρησιμοποιηθεί για πρόσβαση στο μπλοκ μέσα από τον πυρήνα. Η μόνη εξαίρεση είναι για τους δείκτες που κατανέμονται με cudahostalloc() όταν ένας ενιαίος χώρος διευθύνσεων χρησιμοποιείται για τον κεντρικό υπολογιστή και τη συσκευή, όπως αναφέρεται στην Unified Virtual Address Space. Η πρόσβαση της μνήμη εξυπηρετητή μέσα από ένα πυρήνα έχει πολλά πλεονεκτήματα: Δεν υπάρχει ανάγκη για κατανομή ενός μπλοκ στη μνήμη της συσκευής για την αντιγραφή δεδομένων μεταξύ αυτού του μπλοκ και του μπλοκ στην μνήμη του εξυπηρετητή. Οι μεταφορές δεδομένων εκτελούνται εμμέσως από τον πυρήνα. Δεν υπάρχει καμία ανάγκη για χρήση των ροών δεδομένων (streams) για να επικαλύψει τη μεταφορά δεδομένων με την εκτέλεση του πυρήνα. Η μεταφορά δεδομένων προερχόμενη από τον πυρήνα επικαλύπτονται αυτόματα με την εκτέλεση του πυρήνα. Από την στιγμή που η page-locked μνήμη μοιράζεται μεταξύ του εξυπηρετητή (host) και της συσκευής, η εφαρμογή πρέπει να συγχρονίζει τις προσπελάσεις μνήμης χρησιμοποιώντας ροές δεδομένων streams ή events (Asynchronous Concurrent Execution) για να αποφευχθεί κάθε ενδεχόμενο κινδύνου read-after-write, write-afterread, ή write-after-write. Για να είναι σε θέση να ανακτηθεί ο δείκτης της συσκευής σε οποιαδήποτε χαρτογραφημένη page-locked μνήμη, η χαρτογράφηση της page-locked μνήμης πρέπει να είναι ενεργοποιημένη καλώντας την cudasetdeviceflags() με την σημαία cudadevicemaphost πριν γίνει οποιαδήποτε άλλη κλήση CUDA. Σε αντίθετη περίπτωση, η cudahostgetdevicepointer() θα επιστρέψει ένα σφάλμα. Η cudahostgetdevicepointer() επιστρέφει επίσης ένα σφάλμα, αν η συσκευή δεν υποστηρίζει χαρτογραφημένη page-locked μνήμη στον εξυπηρετητή. Οι εφαρμογές μπορούν να διερευνούν αυτή τη δυνατότητα, ελέγχοντας την ιδιότητα της συσκευής με την canmaphostmemory, η οποία είναι ίση με 1 για συσκευές που υποστηρίζουν. 2.6 Ατομικές Συναρτήσεις Μια ατομική συνάρτηση (atomic function) εκτελεί μια ανάγνωση-τροποποίηση-εγγραφή σε μια 32-bit ή 64-bit λέξη που βρίσκεται στην παγκόσμια (global) ή κοινόχρηστη (shared) μνήμη. Για παράδειγμα, η atomicadd () διαβάζει μια λέξη σε κάποια διεύθυνση στην παγκόσμια ή κοινόχρηστη μνήμη, προσθέτει έναν αριθμό σε αυτό, και γράφει το αποτέλεσμα πίσω στην ίδια διεύθυνση. Η συνάρτηση είναι ατομική, με την έννοια ότι μπορεί να εκτελείται εγγυημένα, χωρίς παρεμβολές από άλλα νήματα. Με άλλα λόγια, 29

31 κανένα άλλο νήμα δεν μπορεί να έχει πρόσβαση στην διεύθυνση έως ότου ολοκληρωθεί η λειτουργία. Οι ατομικές συναρτήσεις μπορούν να χρησιμοποιηθούν μόνο σε συναρτήσεις της συσκευής ενώ οι ατομικές συναρτήσεις σε page-locked μνήμη δεν είναι ατομικές από την σκοπιά του εξυπηρετητή (host) ή άλλων συσκευών. Η υποστήριξη για ατομικές συναρτήσεις ποικίλλει ανάλογα με την υπολογιστική ικανότητα της συσκευής GPU: Είναι διαθέσιμες μόνο για τις συσκευές υπολογιστικής ικανότητας 1.1 και άνω. Οι ατομικές συναρτήσεις που λειτουργούν σε 32-bit ακέραιες τιμές σε κοινόχρηστη (shared) μνήμη και ατομικές συναρτήσεις που λειτουργούν σε 64-bit ακέραιες τιμές στην παγκόσμια (global) μνήμη είναι διαθέσιμες μόνο για συσκευές της υπολογιστικής ικανότητας 1.2 και άνω. Ατομικές συναρτήσεις που λειτουργούν σε 64-bit ακέραιες τιμές σε κοινόχρηστη μνήμη είναι διαθέσιμες μόνο για συσκευές υπολογιστικής ικανότητας 2.x και άνω. Μόνο η atomicexch() και η atomicadd() μπορούν να λειτουργήσουν σε 32-bit floating-point μεταβλητές: o στην παγκόσμια (global) μνήμη για atomicexch() και συσκευές υπολογιστικής ικανότητας 1.1 και άνω. o στην κοινόχρηστη (shared) μνήμη για atomicexch() και συσκευές υπολογιστικής ικανότητας 1.2 και άνω. o στην παγκόσμια (global) και την κοινόχρηστη (shared) μνήμη για atomicadd() και συσκευές υπολογιστικών δυνατοτήτων 2.x και άνω. Ωστόσο, κάθε ατομική συνάρτηση μπορεί να υλοποιηθεί με βάση την atomiccas() (Compare And Swap). int atomiccas(int* address, int compare, int val); unsigned int atomiccas(unsigned int* address, unsigned int compare, unsigned int val); unsigned long long int atomiccas(unsigned long long int* address, unsigned long long int compare, unsigned long long int val); Δηλαδή διαβάζει την 32-bit ή 64-bit λέξη old που βρίσκεται στη διεύθυνση address στην παγκόσμια ή κοινόχρηστη μνήμη, υπολογίζει (old == compare? val : old), και αποθηκεύει το αποτέλεσμα πίσω στη μνήμη στην ίδια διεύθυνση. Αυτές οι τρεις λειτουργίες εκτελούνται σε μία ατομική συναλλαγή. Η συνάρτηση επιστρέφει το old. Μια ψευδο-υλοποίηση της AtomicCAS φαίνεται παρακάτω: int atomiccas(int *address, int compare, int val){ lock(address) { int old = *address; *address = (old == compare)? val : old; return old; } } 30

32 2.7 Asynchronous Concurrent Execution Ταυτόχρονη (Concurrent) εκτέλεση μεταξύ host-device. Προκειμένου να διευκολυνθεί η ταυτόχρονη εκτέλεση μεταξύ του εξυπηρετητή (host) και της συσκευής, μερικές κλήσεις συναρτήσεων είναι ασύγχρονες: ο έλεγχος επιστρέφεται στον εξυπηρετητή πριν η συσκευή ολοκληρώσει το έργο του. Οι κλήσεις μπορεί να είναι: Εκτέλεση πυρήνα (kernel). Αντιγραφές στην μνήμη μεταξύ δύο διευθύνσεων της ίδιας συσκευής. Αντιγραφές στην μνήμη από τον εξυπηρετητή (host)στη συσκευή για μπλοκ μνήμης 64 KB ή λιγότερο. Αντιγραφές στην μνήμη πραγματοποιούνται από συναρτήσεις που έχουν το πρόθεμα Async. Κλήσεις συναρτήσεων κατάστασης μνήμης. Οι προγραμματιστές μπορούν να απενεργοποιήσουν τις ασύγχρονες κλήσεις πυρήνα για όλες τις εφαρμογές CUDA με τη ρύθμιση της μεταβλητή περιβάλλοντος CUDA_LAUNCH_BLOCKING = 1. Αυτή η δυνατότητα παρέχεται για εντοπισμό σφαλμάτων και δεν πρέπει ποτέ να χρησιμοποιηθεί για λογισμικό παραγωγής. Οι κλήσεις του πυρήνα είναι συγχρονισμένες στις ακόλουθες περιπτώσεις : Η εφαρμογή λειτουργεί με debugger ή memory check (CUDA-gdb, CUDAmemcheck, Nsight) σε συσκευή υπολογιστικής δυνατότητας 1.x. Οι μετρητές του hardware συγκεντρώνονται μέσω ενός profiler (Nsight, Visual Profiler) Επικάλυψη της μεταφοράς δεδομένων και Εκτέλεσης Kernel Ορισμένες συσκευές υπολογιστικής δυνατότητας 1.1 και άνω μπορούν να εκτελέσουν ταυτόχρονα με την εκτέλεση του πυρήνα αντιγραφές μεταξύ page-locked μνήμης εξυπηρετητή (host) και μνήμης της συσκευής. Οι εφαρμογές μπορούν να ελέγξουν αυτή την δυνατότητα μέσω των ιδιοτήτων της συσκευής, συγκεκριμένα την μεταβλητής asyncenginecount, η οποία είναι μεγαλύτερη από το μηδέν για συσκευές που το υποστηρίζουν. Για συσκευές υπολογιστικής δυνατότητας 1.x, η δυνατότητα υποστηρίζεται μόνο για αντίγραφα μνήμης που δεν περιλαμβάνουν CUDA πίνακες ή 2D πίνακες που έχουν κατανεμηθεί μέσω cudamallocpitch() Ταυτόχρονη (Concurrent) εκτέλεση των Kernel Ορισμένες συσκευές υπολογιστικής ικανότητας 2.x και άνω μπορούν να εκτελέσουν πολλαπλούς πυρήνες ταυτόχρονα. Οι εφαρμογές μπορούν να ελέγχουν αυτή τη 31

33 δυνατότητα της συσκευής μέσω της μεταβλητής concurrentkernels, η οποία είναι ίση με 1 για συσκευές που το υποστηρίζουν. Ο μέγιστος αριθμός πυρήνων που μπορεί να εκτελέσει ταυτόχρονα μια συσκευή είναι 32 για συσκευές υπολογιστικής ικανότητας 3.5 και 16 για συσκευές χαμηλότερων υπολογιστικών δυνατοτήτων. Πυρήνες που χρησιμοποιούν πολλές υφές (textures) ή ένα μεγάλο ποσό της τοπικής μνήμης είναι λιγότερο πιθανό να εκτελεστούν ταυτόχρονα με άλλους πυρήνες Ταυτόχρονες μεταφορές δεδομένων Ορισμένες συσκευές υπολογιστικής ικανότητας 2.x και άνω μπορεί να αντιγράψουν ταυτόχρονα από τη page-locked μνήμη του εξυπηρετητή (host) στη μνήμη της συσκευής και από τη μνήμη της συσκευής στη page-locked μνήμη του εξυπηρετητή. Οι εφαρμογές μπορούν να ελέγξουν τη δυνατότητα αυτή μέσω της μεταβλητής ιδιοτήτων της συσκευής asyncenginecount, η οποία είναι ίση με 2 για συσκευές που το υποστηρίζουν Ροή δεδομένων (Streams) Οι εφαρμογές διαχειρίζονται την ταυτόχρονη εκτέλεση μέσω των ροών δεδομένων streams. Μια ροή δεδομένων είναι μια ακολουθία εντολών (ενδεχομένως να έχουν εκδοθεί από διαφορετικά νήματα του εξυπηρετητή (host)) που εκτελούνται στη σειρά. Από την άλλη πλευρά, διαφορετικές ροές δεδομένων μπορεί να εκτελέσουν τις εντολές τους εκτός σειράς μεταξύ τους ή ταυτόχρονα. Αυτή η συμπεριφορά δεν είναι εγγυημένη και ως εκ τούτου δεν πρέπει να θεωρείται δεδομένη η ορθότητα του (π.χ., η ενδοεπικοινωνία μεταξύ των πυρήνων είναι απροσδιόριστη). Δημιουργία και Καταστροφή Μια ροή δεδομένων ορίζεται με την δημιουργία ενός αντικειμένου ροή δεδομένων (stream) και καθορίζοντας το ως την παράμετρο ροή δεδομένων σε μια αλληλουχία από κλήσεις πυρήνα και αντιγραφές μνήμης μεταξύ του εξυπηρετητή (host) και της συσκευής. Το ακόλουθο δείγμα κώδικα δημιουργεί δύο ροές δεδομένων και κατανέμει έναν πίνακα float hostptr στην page-locked μνήμη. Κάθε μία από αυτά τις ροές δεδομένων (streams) ορίζεται από το δείγμα κώδικα ως μια ακολουθία από μία αντιγραφή μνήμης από τον εξυπηρετητή (host) στην συσκευή, μία κλήση του πυρήνα, και μία αντιγραφή μνήμης από τη συσκευή στον εξυπηρετητή (host). cudastream_t stream[2]; for (int i = 0; i < 2; ++i) cudastreamcreate(&stream[i] ); float * hostptr; cudamallochost(&hostptr, 2 * size ); 32

34 Κάθε ροή δεδομένων (stream) αντιγράφει το τμήμα του πίνακα εισόδου hostptr που του αναλογεί στον πίνακα inputdevptr στη μνήμη της συσκευής, επεξεργάζεται τον inputdevptr στη συσκευή καλώντας την MyKernel(),και αντιγράφει τα αποτελέσματα του outputdevptr πίσω στο ίδιο τμήμα του hostptr. Πιο κάτω περιγράφουμε την επικαλυπτόμενη συμπεριφορά των ροών δεδομένων (stream), ανάλογα με την υπολογιστική ικανότητα της συσκευής. Για να συμβεί οποιαδήποτε αλληλοεπικάλυψη ο hostptr πρέπει να δείχνει σε page-locked μνήμη του εξυπηρετητή (host). for (int i = 0; i < 2; ++i){ cudamemcpyasync(inputdevptr + i * size, hostptr + i * size, size, cudamemcpyhosttodevice, stream[i]); MyKernel <<<100, 512, 0, stream[i]>>>(outputdevptr + i * size, inputdevptr + i * size, size); } cudamemcpyasync(hostptr + i * size, outputdevptr + i * size, size, cudamemcpydevicetohost, stream[i]); Οι ροές δεδομένων (streams) απελευθερώνονται καλώντας την cudastreamdestroy(), περιμένοντας να ολοκληρωθούν όλες οι προηγούμενες εντολές στην συγκεκριμένη ροή δεδομένων πριν τις τερματίσει και επιστρέφει τον έλεγχο στο νήμα του εξυπηρετητή. for (int i = 0; i < 2; ++i) cudastreamdestroy(stream[i]); Προκαθορισμός του της ροής δεδομένων stream Οι κλήσεις πυρήνα και οι αντιγραφές μεταξύ εξυπηρετητή <-> συσκευής που δεν προσδιορίζουν οποιαδήποτε παράμετρο των ροών δεδομένων, ή ισοδύναμα αν θέτουν την παράμετρο της ροής δεδομένων (stream) στο μηδέν, αναθέτονται στην προεπιλεγμένη ροή δεδομένων και ως εκ τούτου, εκτελούνται σε σειρά. Ρητός (explicit) συγχρονισμός Υπάρχουν διάφοροι τρόποι για να συγχρονιστούν ρητά οι ροές δεδομένων (streams). cudadevicesynchronize() περιμένει έως ότου όλες οι προηγούμενες εντολές όλων των ροών δεδομένων (stream) και όλων των νημάτων συσκευής έχουν ολοκληρωθεί. cudastreamsynchronize() λαμβάνει ως παράμετρο μια ροή δεδομένων και περιμένει έως ότου έχουν ολοκληρωθεί όλες οι εντολές που προηγούνται του δεδομένου stream. Μπορεί να χρησιμοποιηθεί για το συγχρονισμό του εξυπηρετητή (host) με μια συγκεκριμένη ροή δεδομένων, επιτρέποντας σε άλλες ροές δεδομένων να συνεχίσουν την εκτέλεση τους στη συσκευή. 33

35 cudastreamwaitevent() αναγκάζει όλες τις εντολές που προστέθηκαν μετά την κλήση της με την δεδομένη ροή δεδομένων (stream) να καθυστερήσουν την εκτέλεσή τους μέχρι το δεδομένο event να ολοκληρωθεί. Η ροή δεδομένων μπορεί να είναι μηδέν, στην οποία περίπτωση όλες οι εντολές που προστίθενται σε κάθε ροή δεδομένων μετά την κλήση της cudastreamwaitevent() θα περιμένουν να ολοκληρωθεί το event. Για να αποφευχθεί άσκοπη επιβράδυνση, όλες οι συναρτήσεις συγχρονισμού συνήθως χρησιμοποιούνται για σκοπούς χρονομέτρησης ή για να απομονώσει μια κλήση ή μια αντιγραφή μνήμης που έχει αποτύχει. Έμμεσος (implicit) συγχρονισμός Δύο εντολές από διαφορετικές ροές δεδομένων δεν μπορούν να τρέχουν ταυτόχρονα, εάν εκδοθεί κάποια από τις ακόλουθες ενέργειες μεταξύ τους από το νήμα εξυπηρετητή (host): Κατανομή page-locked μνήμης στον εξυπηρετητή (host). Κατανομή μνήμης στην συσκευή. Καθορισμός μνήμης στην συσκευή. Αντιγραφή ανάμεσα σε δύο διευθύνσεις μνήμης της ίδιας συσκευής. Οποιαδήποτε εντολή CUDA στην προεπιλεγμένη ροή δεδομένων (stream). ένας διακόπτης μεταξύ της L1/shared διαμόρφωσης μνήμης περιγράφεται στην Υπολογιστική Δυνατότητα 2.x. Για συσκευές που υποστηρίζουν ταυτόχρονη εκτέλεση πυρήνων υπολογιστικής δυνατότητας 3,0 ή χαμηλότερη, κάθε λειτουργία που απαιτεί έλεγχο εξάρτησης για να ελέγξει αν έχει ολοκληρωθεί η streamed εκτέλεση πυρήνων μπορεί: Να αρχίσει να εκτελείται μόνο όταν όλα τα νημάτινα μπλοκ όλων των προηγούμενων πυρήνων από οποιοδήποτε stream στο πλαίσιο της CUDA έχουν ήδη αρχίσει την εκτέλεση τους. Να μπλοκάρει όλες τις άλλες κλήσεις πυρήνων που έχουν ξεκινήσει αργότερα από οποιοδήποτε stream στο πλαίσιο της CUDA μέχρι να ολοκληρωθεί η διαδικασία ελέγχου κλήσης του πυρήνα. Συμπεριφορά Επικάλυψης (Overlapping) Το ποσό της επικάλυψης εκτέλεσης μεταξύ δύο ροών δεδομένων (stream) εξαρτάται από την σειρά με την οποία οι εντολές εκδίδονται στην κάθε ροή δεδομένων (stream) και το κατά πόσον ή όχι η συσκευή υποστηρίζει την επικάλυψη κατά την μεταφορά δεδομένων και την εκτέλεση του πυρήνα, την ταυτόχρονη εκτέλεση πυρήνων, και/ή την ταυτόχρονη μεταφορά δεδομένων. 34

36 Για παράδειγμα, σε συσκευές που δεν υποστηρίζουν την ταυτόχρονη μεταφορά δεδομένων, οι δύο ροές δεδομένων του κώδικα της δημιουργίας και καταστροφής των stream, δεν επικαλύπτονται καθόλου, επειδή η αντιγραφή μνήμης από τον εξυπηρετητή (host) στη συσκευή εκδίδεται στο stream[1], δηλαδή μετά την αντιγραφή μνήμης από τη συσκευή προς τον εξυπηρετητή που εκδίδεται στο stream[0], έτσι ώστε να μπορεί να ξεκινήσει μόνο όταν ολοκληρωθεί η αντιγραφή μνήμης από τη συσκευή στον εξυπηρετητή η οποία έχει εκδοθεί στο stream[0]. Αν ο κώδικας ξαναγραφεί με τον ακόλουθο τρόπο, και αν υποτεθεί ότι η συσκευή υποστηρίζει την επικάλυψη της μεταφοράς δεδομένων και εκτέλεσης πυρήνα, τότε η αντιγραφή μνήμης από τον εξυπηρετητή στη συσκευή που εκδίδεται στο stream[1] επικαλύπτεται με την κλήση του πυρήνα που εκδίδεται στο stream[0]. for (int i = 0; i < 2; ++i) cudamemcpyasync(inputdevptr + i * size, hostptr + i * size, size, cudamemcpyhosttodevice, stream[i]); for (int i = 0; i < 2; ++i){ MyKernel<<<100, 512, 0, stream[i]>>> (outputdevptr + i * size, inputdevptr + i * size, size); for (int i = 0; i < 2; ++i){ cudamemcpyasync(hostptr + i * size, outputdevptr + i * size, size, cudamemcpydevicetohost, stream[i]); } } Στις συσκευές που υποστηρίζουν ταυτόχρονη μεταφορά δεδομένων, τα δύο streams του παραδείγματος της δημιουργίας και καταστροφής επικαλύπτονται. Η αντιγραφή μνήμης από τον εξυπηρετητή στη συσκευή που εκδίδεται στο stream[1] συμπίπτει με την αντιγραφή μνήμης από τη συσκευή στον εξυπηρετητή που εκδίδεται στο stream[0] ακόμη και με την κλήση του πυρήνα που εκδίδεται στο stream[0]. Οι εκτελέσεις του πυρήνα επικαλύπτονται διότι η δεύτερη κλήση πυρήνα εκδίδεται στο stream[1] πριν από την αντιγραφή μνήμης από τη συσκευή στον εξυπηρετητή που εκδίδεται στο stream[0]. Ωστόσο σε αυτή την περίπτωση, η αντιγραφή της μνήμης από τη συσκευή στον εξυπηρετητή εκδίδεται στο stream[0] επικαλύπτεται μόνο με τα τελευταία νημάτινα μπλοκ της κλήσης πυρήνα που εκδόθηκε στο stream[1], σύμφωνα με τον έμμεσο συγχρονισμό (implicit synchronization), το οποίο μπορεί να αντιπροσωπεύει μόνο ένα μικρό μέρος του συνολικού χρόνου εκτέλεσης του πυρήνα Events Το runtime παρέχει επίσης έναν τρόπο για να παρακολουθεί στενά την πρόοδο της συσκευής, καθώς και τον ακριβή χρονισμό, με το να αφήνει την εφαρμογή να καταγράφει ασύγχρονα κάποια γεγονότα (events) σε οποιοδήποτε σημείο στο πρόγραμμα και να τα περισυλλέγει όταν ολοκληρωθούν αυτά τα events. Ένα event έχει ολοκληρωθεί όταν έχουν ολοκληρωθεί όλες οι διαδικασίες (tasks) - ή όλες οι εντολές σε μια συγκεκριμένη ροή δεδομένων (stream). 35

37 Δημιουργία και Καταστροφή Το ακόλουθο δείγμα κώδικα δημιουργεί και καταστρέφει events: cudaevent_t start, stop; cudaeventcreate(&start); cudaeventcreate(&stop);... cudaeventdestroy(start); cudaeventdestroy(stop); Elapsed time Τα events μπορούν να χρησιμοποιηθούν για χρονομέτρηση του κώδικα π.χ. της δημιουργίας και καταστροφής με τον ακόλουθο τρόπο: cudaeventrecord(start, 0); MyKernel<<<100, 512, 0, stream[i]>>>(... ) cudaeventrecord(stop, 0); cudaeventsynchronize(stop); float elapsedtime; cudaeventelapsedtime(&elapsedtime, start, stop); Συγχρονισμένες κλήσεις Όταν γίνεται κλήση σε μια σύγχρονη συνάρτηση, ο έλεγχος δεν επιστρέφει στο νήμα του εξυπηρετητή (host) πριν η συσκευή ολοκληρώσει τις αιτούμενες διεργασίες. Αν το νήμα του εξυπηρετητή στη συνέχεια θα υποχωρήσει, μπλοκάρει, ή συνεχίσει, μπορεί να καθοριστεί με την κλήση της cudasetdeviceflags() με ορισμένες σημαίες, πριν το νήμα του εξυπηρετητή πραγματοποιήσει οποιαδήποτε άλλη κλήση CUDA. 2.8 Ενοποιημένο Virtual Address Space Όταν η εφαρμογή λειτουργεί ως μια διαδικασία 64-bit, χρησιμοποιείται ένας ενιαίος χώρος διευθύνσεων για τον host και για όλες τις συσκευές CC 2.0 και άνω. Αυτός ο χώρος διευθύνσεων χρησιμοποιείται για όλες τις κατανομές στη μνήμη του εξυπηρετητή host μέσω cudahostalloc()σε οποιοδήποτε μέρος της συσκευής μέσω cudamalloc(). Για εξακρίβωση του που δείχνει ένας pointer μπορεί να ελεγχθεί από την τιμή του δείκτη, χρησιμοποιώντας την cudapointergetattributes(). Κατά συνέπεια: Για αντιγραφή από ή προς τη μνήμη συσκευής για τις οποίες ο ενιαίος χώρος διευθύνσεων χρησιμοποιείται, η παράμετρος cudamemcpykind της cudamemcpy () μπορεί να ρυθμιστεί σε cudamemcpydefault. Κατανομές μέσω cudahostalloc() είναι αυτόματα portable σε όλες τις συσκευές για τις οποίες χρησιμοποιείται ο ενιαίος χώρος διευθύνσεων, και οι δείκτες που επιστρέφονται από cudahostalloc() μπορούν να χρησιμοποιηθούν απευθείας μέσα από τους πυρήνες. 36

38 Κεφάλαιο 1 Επιδόσεις και περιορισμοί 1.1 Χαρακτηριστικά συστήματος μετρήσεων Το υπολογιστικό σύστημα στο οποίο έγιναν οι μετρήσεις είναι ένα desktop PC με λειτουργικό σύστημα Windows 7 Ultimate edition 64-bit με εγκατεστημένο το Service Pack 1. Στο λειτουργικό σύστημα είχαν επίσης εγκατασταθεί όλες οι τελευταίες ενημερώσεις ασφαλείας από την Microsoft πέραν του Service Pack 1. Το σύστημα είναι 64-bit αρχιτεκτονικής και έχει μνήμη μεγέθους 12 GB RAM τεχνολογίας DDR3 (triplechannel). Επίσης στο υπολογιστικό σύστημα είναι εγκατεστημένο το σετ εργαλείων ανάπτυξης της Nvidia CUDA Toolkit στην έκδοση 5.5. Η έκδοση του driver της κάρτας γραφικών που είναι εγκατεστημένη είναι η Η ανάπτυξη του κώδικα έγινε με την βοήθεια του Visual Studio 2012 και του NVidia Nsight 4 ενώ οι μετρήσεις στα compiled source code αρχεία έγιναν στα 32-bit Κεντρική Μονάδα Επεξεργασίας Η Κεντρική Μονάδα Επεξεργασίας (CPU) του υπολογιστικού συστήματος είναι της εταιρίας Intel και το συγκεκριμένο μοντέλο στο οποίο εκτελέστηκε ο κλασικός αλγόριθμος είναι το intel i7 980x. Η συχνότητα λειτουργίας είναι στα 3,33 GHz με 6 πυρήνες. Για την υλοποίηση του κλασικού αλγορίθμου σε CPU χρησιμοποιήσαμε έναν cpu-core για να έχουμε ένα καλύτερο μέτρο σύγκρισης ως προς την απόδοση της παράλληλης υλοποίησης με GPU προς την σειριακή (single-core) υλοποίηση Κάρτα γραφικών Η κάρτα γραφικών είναι της εταιρίας Nvidia και το συγκεκριμένο μοντέλο στο οποίο εκτελέστηκε ο παράλληλος αλγόριθμος είναι το GeForce GTX 670. Η συγκεκριμένη κάρτα γραφικών είναι επεξεργαστικής ικανότητας 3.0 με 4GB VRAM τεχνολογίας DDR5 και έχει τα χαρακτηριστικά που φαίνονται στον Πίνακα 2.1. Τα χαρακτηριστικά της συσκευής μπορούμε να τα δούμε και από ένα απλό query στην cudadeviceprop(). Threads per Warp 32 Warps per Multiprocessor 64 Threads per Multiprocessor 2048 Thread Blocks per Multiprocessor 16 Total # of 32-bit registers per Multiprocessor Register allocation unit size 256 Registers per Thread 63 Shared Memory per Multiprocessor (bytes) Shared Memory Allocation unit size 256 Maximum Thread Block Size

39 1.2 Επιτάχυνση (Speedup) και αποτελεσματικότητα (Efficiency) Η επιτάχυνση (speedup) είναι ένας από τους απλούστερους και ευρύτερα χρησιμοποιούμενους δείκτες απόδοσης στη παράλληλη επεξεργασία. Γραμμική (Linear) ονομάζεται η επιτάχυνση όταν αυξάνεται ανάλογα με τον αριθμό των χρησιμοποιούμενων επεξεργαστών και αποτελεί μέτρο ιδανικής παραλληλοποίησης. Η επιτάχυνση ενός παραλληλοποιημένου προγράμματος ορίζεται ως ο λόγος: Speedup = T serial T parallel Όπου T serial ο χρόνος εκτέλεσης του σειριακού προγράμματος και T parallel ο χρόνος εκτέλεσης του αντίστοιχου παράλληλου προγράμματος. To occupancy της συσκευής GPU δίνεται από τη σχέση: Occupancy = # of threads on SM max # of threads on SM 1.3 Structure of Arrays και Array of Structures στην cuda Η επιλογή AoS έναντι SoA για βέλτιστη απόδοση εξαρτάται συνήθως για τον τύπο πρόσβασης στην μνήμη. Αυτό δεν περιορίζεται μόνο στην CUDA, ωστόσο, παρόμοιες εκτιμήσεις ισχύουν για κάθε αρχιτεκτονική όπου η απόδοση μπορεί να επηρεαστεί σημαντικά από το πρότυπο πρόσβασης μνήμης, π.χ. όπου έχουμε λανθάνουσα (cache) ή όπου οι επιδόσεις είναι καλύτερες για συνεχόμενη πρόσβαση στη μνήμη (π.χ. coalesced προσβάσεις μνήμης σε CUDA ). Σαν παράδειγμα για RGB εικονοστοιχεία (pixels) σε σχέση με ξεχωριστά στοιχεία RGB : struct { uint8_t r, g, b; } AoS[N]; struct { uint8_t r[n]; uint8_t g[n]; uint8_t b[n]; } SoA; Εάν απαιτείται να έχουμε πρόσβαση στα R/G/B στοιχεία του κάθε εικονοστοιχείου ταυτόχρονα τότε η χρήση AoS έχει νόημα, δεδομένου ότι οι διαδοχικές αναγνώσεις των R, G, B στοιχείων θα είναι συνεχόμενες και συνήθως περιέχονται μέσα στην ίδια γραμμή της λανθάνουσα (cache). Επίσης, για την CUDA αυτό σημαίνει συνεχόμενη (coalescing) ανάγνωση / εγγραφή μνήμης. Ωστόσο, αν πρόκειται για την επεξεργασία των χρωμάτων χωριστά η επιλογή της δομής SoA μπορεί να είναι πιο βολική, π.χ. αν θέλετε να αναβαθμιστούν όλες οι τιμές R κατά κάποιον παράγοντα κλίμακας, τότε στην SoA σημαίνει ότι όλες οι συνιστώσες R θα είναι συνεχόμενες. 38

40 Ένα επιπλέον ζήτημα είναι το padding / alignment. Για το παράδειγμα RGB, κάθε στοιχείο σε μια διάταξη AoS είναι διατεταγμένο σαν πολλαπλάσιο των 3 bytes, τα οποία μπορεί να μην είναι βολικά για CUDA, και SIMD ή σε ορισμένες περιπτώσεις ίσως απαιτούν γέμισμα ακόμη και εντός του struct για να κάνει πιο εύκολη την διάταξη (π.χ. πρόσθεση ενός uint8_t στοιχείου για να εξασφαλιστεί η διάταξη των 4 byte). Στην περίπτωση όμως των SoA τα στοιχεία είναι διατεταγμένα ανά byte και μπορεί να είναι πιο βολικό για ορισμένους αλγόριθμους ή αρχιτεκτονικές. Για τις περισσότερες εφαρμογές επεξεργασίας εικόνας η χρήση AoS είναι πολύ συχνή, αλλά για άλλες εφαρμογές, ή για συγκεκριμένες εργασίες στην επεξεργασίας εικόνας μπορεί να μην είναι πάντα η περίπτωση. Όταν δεν υπάρχει προφανής επιλογή η AoS θεωρείται ως προεπιλογή όπως και στο πρόγραμμα που θα αναπτύξουμε στην συγκεκριμένη εργασία. 1.4 Mapping των block-threads Grid Block 0 tid 0 tid base 1 Block 1 tid base tid 2 base 1... Block N base 1 tid N base 1 base tid N base base 1 Block N tid base N base base tid ( N Σχήμα 4.16 Mapping των block-threads ως προς Στο παραπάνω σχήμα αναπαριστάνεται το mapping των block-threads ως προς τις μεταβλητές N και base, για την συγκεκριμένη υλοποίηση του αλγορίθμου Closest Pair D&C. 1.5 Εκτίμηση, Παραλληλοποίηση, Βελτιστοποίηση, Ανάπτυξη (APOD) base +1) base 1 Ο APOD (Assess, Parallelize, Optimize, Deploy) κύκλος σχεδιασμού για εφαρμογές έχει στόχο να βοηθήσει τους προγραμματιστές εφαρμογών για να εντοπίσει γρήγορα τα τμήματα του κώδικά τους, που θα μπορούσαν πιο εύκολα να επωφεληθούν από την επιτάχυνση GPU, αρχίζοντας τη μόχλευση επιτάχυνσης όσο το δυνατόν πιο άμεσα. Η μεθοδολογία APOD είναι μια κυκλική διαδικασία: σε αρχική επιτάχυνση μπορεί να 39

41 επιτευχθούν, να δοκιμαστούν και να αναπτυχθούν μόνο με ελάχιστη αρχική επένδυση χρόνου, οπότε ο κύκλος μπορεί να ξεκινήσει και πάλι με τον εντοπισμό περαιτέρω ευκαιριών βελτιστοποίησης, επιπλέον επιτάχυνση, και στη συνέχεια την ανάπτυξη ακόμα πιο γρήγορων εκδόσεων. Assess Deploy Optimize Parallelize Σχήμα 2.1 Κύκλος APOD Εκτίμηση Για ένα υπάρχον πρόγραμμα, το πρώτο βήμα είναι να αξιολογήσει την εφαρμογή για να εντοπίσει τα τμήματα του κώδικα που είναι υπεύθυνα για το μεγαλύτερο μέρος του χρόνου εκτέλεσης. Έτσι μπορεί να αξιολογηθούν τα σημεία συμφόρησης για την παραλληλοποίηση και να αρχίσει η διερεύνηση για την επιτάχυνση GPU. Με την κατανόηση των απαιτήσεων και των περιορισμών του τελικού χρήστη και με την εφαρμογή των νόμων του Amdahl και του Gustafson, ο προγραμματιστής μπορεί να καθορίσει το ανώτατο όριο της βελτίωσης των επιδόσεων από την επιτάχυνση των συγκεκριμένων τμημάτων της εφαρμογής. Παραλληλοποίηση Έχοντας εντοπίσει τα hotspots και έχοντας ολοκληρώσει τις βασικές ασκήσεις για τους στόχους και τις προσδοκίες, ο προγραμματιστής της εφαρμογής θα πρέπει να παραλληλίσει τον κώδικα. Ανάλογα με τον αρχικό κώδικα, αυτό μπορεί να είναι τόσο απλό όσο καλώντας απλά μια GPU-βελτιστοποιημένη βιβλιοθήκη, όπως η cublas, cufft ή Thrust, ή θα μπορούσε να είναι τόσο απλό όπως προσθέτοντας μερικές οδηγίες προεπεξεργαστή και οδηγίες για τον μεταγλωττιστή (compiler) παραλληλισμού. Από την άλλη πλευρά, ορισμένες εφαρμογές απαιτούν κάποιο ποσό refactoring για να εκτεθεί ο εγγενής παραλληλισμός τους. Όπως ακόμη και για τις μελλοντικές αρχιτεκτονικές CPU θα απαιτείται η έκθεση του παραλληλισμού, προκειμένου να βελτιωθούν ή απλά να διατηρηθούν οι επιδόσεις των διαδοχικών εφαρμογών, η οικογένεια CUDA των παράλληλων γλωσσών προγραμματισμού (CUDA C / C++, CUDA Fortran, κλπ.), έχει ως στόχο να κάνει την έκφραση του εν λόγω παραλληλισμού 40

42 όσο το δυνατόν απλούστερη, ενώ ταυτόχρονα επιτρέπει τη λειτουργία σε CUDAcapable GPUs για σχεδίαση και μέγιστη παράλληλη απόδοση. Βελτιστοποίηση Μετά από ολοκλήρωση του κάθε κύκλου παραλληλισμού, ο προγραμματιστής μπορεί να κινηθεί προς την βελτιστοποίηση της εφαρμογής και τη βελτίωση των επιδόσεων. Δεδομένου ότι υπάρχουν πολλές πιθανές βελτιστοποιήσεις που μπορεί να θεωρηθούν, έχοντας μια καλή κατανόηση των αναγκών της εφαρμογής μπορεί να βοηθήσει να καταστήσει τη διαδικασία όσο το δυνατόν ομαλότερη. Ωστόσο, όπως και με την APOD στο σύνολό της, η βελτιστοποίηση του προγράμματος είναι μια επαναληπτική διαδικασία (εντοπίζει μια ευκαιρία για τη βελτιστοποίηση, εφαρμόζεται και δοκιμάζεται η βελτιστοποίηση, επαληθεύεται η επιτευγμένη επιτάχυνση, και επαναλαμβάνετε η διαδικασία), πράγμα που σημαίνει ότι δεν είναι απαραίτητο για έναν προγραμματιστή να δαπανήσει μεγάλα ποσά χρόνου απομνημονεύοντας το μεγαλύτερο μέρος όλων των πιθανών στρατηγικών βελτιστοποιήσεων πριν δει καλές επιταχύνσεις. Αντ 'αυτού, οι στρατηγικές μπορούν να εφαρμοστούν επαυξητικά καθώς εντοπίζονται. Οι βελτιστοποιήσεις μπορούν να εφαρμοστούν σε διάφορα επίπεδα, από την επικάλυψη μεταφοράς δεδομένων με υπολογισμό σε όλη τη διαδρομή μέχρι την εξομάλυνση των βραχυχρόνιων διακυμάνσεων των floating-point λειτουργιών. Τα διαθέσιμα εργαλεία προφίλ είναι ανεκτίμητης αξίας για την καθοδήγηση αυτής της διαδικασίας, δεδομένου ότι μπορεί να βοηθήσει ή να προτείνει μια καλύτερη πορεία δράσης για τις προσπάθειες βελτιστοποίησης του κύριου έργου και να παρέχει παραπομπές στα σχετικά τμήματα βελτιστοποίησης του κώδικα. Ανάπτυξη Αφού ολοκληρωθεί η επιτάχυνση στην GPU ενός ή περισσότερων συστατικών της εφαρμογής, είναι δυνατόν να συγκριθεί το αποτέλεσμα με την αρχική έκδοση. Υπενθυμίζεται ότι το πρώτο βήμα εκτίμησης επιτρέπει στον προγραμματιστή της εφαρμογής να καθορίσει ένα άνω φράγμα για την ενδεχόμενη επιτάχυνση που επιτυγχάνεται με την επιτάχυνση των κρίσιμων δεδομένων (hotspot). Πριν από την αντιμετώπιση άλλων hotspots για να βελτιωθεί η συνολική επιτάχυνση, ο προγραμματιστής της εφαρμογής θα πρέπει να εξετάσει τη λήψη της εν μέρει παραλληλισμένης εφαρμογής μέσα στην παραγωγή. Αυτό είναι σημαντικό για διάφορους λόγους: Για παράδειγμα, αυτό επιτρέπει στο χρήστη να επωφεληθούν από τις επενδύσεις τους όσο το δυνατόν νωρίτερα (η επιτάχυνση μπορεί να είναι μερική, αλλά εξακολουθεί να είναι πολύτιμη), και αυτό ελαχιστοποιεί τον κίνδυνο για τον προγραμματιστή και τον χρήστη, παρέχοντας μία εξελικτική και όχι επαναστατική σειρά αλλαγών στην εφαρμογή. 41

43 Κεφάλαιο 2 Αλγόριθμος Closest Pair Brute Force. 2.1 Closest Pair Brute Force Το πρόβλημα πλησιέστερου ζεύγους είναι ένα πρόβλημα της υπολογιστικής γεωμετρίας: Δίνονται n σημεία στο μετρικό χώρο, και ζητείται να βρεθεί ένα ζευγάρι των σημείων με τη μικρότερη δυνατή απόσταση μεταξύ τους. Το πρόβλημα του closest pair of points στο Ευκλείδειο επίπεδο, ήταν από τα πρώτα γεωμετρικά προβλήματα που αντιμετωπίστηκαν κατά τις απαρχές της συστηματικής μελέτης της υπολογιστικής πολυπλοκότητας των γεωμετρικών αλγορίθμων. Σχήμα 3.1 Closest Pair για το κόκκινο ζευγάρι Μια αφελής προσέγγιση του αλγόριθμου της εύρεσης αποστάσεων, μεταξύ όλων των ζευγών των σημείων και επιλέγοντας την ελάχιστη απόσταση, απαιτεί O(n 2 ) χρόνο. Αποδεικνύεται ότι το πρόβλημα μπορεί να επιλυθεί σε O(n logn) χρόνο σε ένα ευκλείδειο χώρο ή χώρο L P των σταθερών διάστασης d. Στο υπολογιστικό μοντέλο το οποίο υποθέτει ότι η συνάρτηση floor είναι υπολογίσιμη σε σταθερό χρόνο, το πρόβλημα μπορεί να επιλυθεί σε O(n log(logn)) χρόνο. Αν επιτρέψουμε την τυχαιοποίηση των σημείων μέχρι να χρησιμοποιηθεί μαζί με τη συνάρτηση βάσης (floor), το πρόβλημα μπορεί να λυθεί σε O(n) χρόνο. Το παρακάνω σχήμα παρουσιάζει δύο σημεία με συντεταγμένες (x, y). Για να βρεθεί η απόσταση μεταξύ δύο σημείων, θα χρησιμοποιήσουμε το θεώρημα του Πυθαγόρα: r = (a 2 + b 2 Έτσι, εάν τον εφαρμόσουμε για τα σημεία θα είναι: d = (x 1 x 2 ) 2 + (y 1 y 2 ) 2 42

44 Σχήμα 3.2 Εικονική αναπαράσταση απόστασης μεταξύ 2 point Για να καθορίσει την απόσταση από το πλησιέστερο έχουμε να συγκρίνουμε όλες τις αποστάσεις μεταξύ των σημείων. bruteforceclosestpair of P(1), P(2),... P(N) if N < 2 then return else mindistance P(1) - P(2) minpoints { P(1), P(2) } foreach i [1, N-1] foreach j [i+1, N] if P(i) - P(j) < mindistance then mindistance P(i) - P(j) minpoints { P(i), P(j) } endif endfor endfor return mindistance, minpoints endif Η απλή λύση είναι ένας O(n 2 ) αλγόριθμος (που μπορούμε να τον ονομάσουμε bruteforce αλγόριθμο) Ο ψευδοκώδικας (με pointers) θα μπορούσε να είναι απλά: 43

45 2.2 Closest Pair Brute Force για CPU Μια πρώτη έκδοση του αλγορίθμου υλοποιήθηκε στον CPU για αναφορά ως προς την απόδοση που θα θέλαμε ή θα μπορούσαμε να πετύχουμε με επιτάχυνση σε CUDA. Ο παρακάτω κώδικας είναι η βασική συνάρτηση που υπολογίζει το closest pair. float compare_points_bf(int N, point *P){ int i,j; float dx, dy, distance=0, min_dist=flt_max; point *p1, *p2; } for (i=0; i<(n-1); i++){ for (j=i+1; j*n; j++){ if ((distance = (P[i].x - P[j].x) * (P[i].x - P[j].x) + (P[i].y - P[j].y) * (P[i].y - P[j].y)) < min_dist){ min_dist = distance; p1 = &P[i]; p2 = &P[j]; } } } return sqrt(min_dist); Για την μέτρηση του αλγορίθμου χρησιμοποιήθηκαν διάφορα μεγέθη πίνακα σημείων (x,y) και τα αποτελέσματα φαίνονται παρακάτω. Τα αποτελέσματα είναι για την 32-bit έκδοση του εκτελέσιμου αρχείου χρησιμοποιώντας τον VCC μεταγλωττιστή (compiler). Ο αλγόριθμος εκτελέστηκε αρκετές φορές και σαν χρόνοι αναφοράς κρατήθηκαν οι μέσοι όροι των χρόνων εκτέλεσης για έκαστο μέγεθος πίνακα P[N]. N elements Time taken (sec) , , , , , , , , ,572 Πίνακας 3.1 Αποτελέσματα closest pair brute force για CPU 44

46 time in sec (log scale) Ανάπτυξη αλγόριθμου Closest Pair σε CUDA Απόδοση brute force CPU ,1 0, number of elements N Time taken (sec) Σχήμα 3.3 Απόδοση closest pair brute force για CPU Παρατηρούμε ότι για μεγάλα μεγέθη πίνακα, ο αλγόριθμος γίνεται ασύμφορα αργός, απαιτώντας σχεδόν μιάμιση ώρα για να ολοκληρωθεί για N=2millon στοιχεία, πράγμα που δικαιολογείται λόγω της πολυπλοκότητας O(n 2 ). Παρακάτω θα εκτελέσουμε ακόμα μια έκδοση του brute force ελάχιστου ζεύγους (closest pair) χρησιμοποιώντας caching blocks μια τεχνική που χρησιμοποιείται και σε όλους τους αλγορίθμους που παραλληλοποιούνται Τεχνική Caching για blocked algorithms Η τεχνική blocking είναι γνωστή για τη βελτιστοποίησης της αποτελεσματικότητας της χρήσης μεταξύ των ιεραρχιών μνήμης. Αντί να αποθηκεύονται στην μνήμη ολόκληρες γραμμές ή στήλες από έναν πίνακα, οι μπλοκ αλγόριθμοι λειτουργούν σε υποπίνακες ή τμήματα του πίνακα, έτσι ώστε τα δεδομένα που φορτώνονται στα ταχύτερη επίπεδα της ιεραρχίας μνήμης να επαναχρησιμοποιούνται. Τα δεδομένα λαμβάνονται με ένα θεωρητικό μοντέλο των συγκρούσεων δεδομένων στην λανθάνουσα (cache) μνήμη, η οποία έχει επικυρωθεί από μεγάλες ποσότητες προσομοίωσης. 45

47 Ο βαθμός παρέμβασης της λανθάνουσας (cache) είναι ιδιαίτερα ευαίσθητος στον βηματισμό (stride) των προσπελάσεων δεδομένων και του μεγέθους των μπλοκ, και μπορεί να προκαλέσουν μεγάλες διακυμάνσεις στην απόδοση του συστήματος για διαφορετικά μεγέθη πίνακα. Η συμβατική μέθοδος για χρήση ολόκληρης της λανθάνουσας (cache), ή ακόμα και ένα σταθερό κλάσμα της, είναι εσφαλμένη. Αν χρησιμοποιείται ένα σταθερό μέγεθος μπλοκ για ένα δεδομένο μέγεθος λανθάνουσας μνήμης, το μέγεθος μπλοκ που ελαχιστοποιεί το αναμενόμενο πλήθος αστοχίας διαβάσματος της λανθάνουσας μνήμης είναι πολύ μικρό. Ρυθμίζοντας το μέγεθος του μπλοκ σύμφωνα με το μέγεθος του πίνακα και τις παραμέτρους της λανθάνουσα (cache) μπορεί να βελτιώσει τη μέση απόδοση και να μειωθεί η διακύμανση επιδόσεων για διαφορετικά μεγέθη πίνακα. Τέλος, όποτε είναι δυνατόν, είναι επωφελής η αντιγραφή για μη συνεχόμενα επαναχρησιμοποιούμενα δεδομένα σε διαδοχικές διευθύνσεις της μνήμης. float compare_points_bf(register int N, register int B, point *P, register point *p1, *p2;){ register int i, j, ib, jb, iin, jjn, num_blocks = (N + (B-1)) / B; register float distance=0, min_dist=flt_max, regx, regy; //break array data in N/B blocks for (i = 0; i < num_blocks; i++){ for (j = i; j < num_blocks; j++){ //get the index range of each i and j blocks iin = ( ((i+1)*b) < N? ((i+1)*b) : N); jjn = ( ((j+1)*b) < N? ((j+1)*b) : N); //reads the moving frame block to compare with the i block for (jb = j * B; jb < jjn; jb++){ //avoid float comparisons that occur when i block = j block //Registers Allocated regx = P[jb].x; regy = P[jb].y; for (i==j? (ib=jb+1):(ib=i*b); ib < iin; ib++){ //calculate distance of current points if((distance = (P[ib].x - regx) * (P[ib].x - regx) + (P[ib].y - regy) * (P[ib].y - regy)) < min_dist){ min_dist = distance; p1 = &P[ib]; p2 = &P[jb]; } } } } } return sqrt(min_dist); } Είναι γνωστό ότι η ιεραρχία μνήμης μπορεί να χρησιμοποιηθεί καλύτερα εάν οι αλγόριθμοι υλοποιούνται με μπλοκ. Η τεχνική blocking είναι επίσης γνωστή ως tilling. Αντί να λειτουργούν σε μεμονωμένες καταχωρήσεις του πίνακα, ο υπολογισμός γίνεται σε υποπίνακες. Η τεχνική blocking μπορεί να εφαρμοστεί σε οποιαδήποτε και πολλαπλά επίπεδα ιεραρχίας μνήμης, συμπεριλαμβανομένης της εικονικής μνήμης, 46

48 λανθάνουσας, διανυσματικών καταχωρητών, και scalar καταχωρητές. Ως ένα παράδειγμα, όταν εφαρμόζεται το blocking σε δύο επίπεδα καταχωρητή και cache, παρατηρούμε ότι έχουμε αισθητή βελτίωση του χρόνου εκτέλεσης Αποτελέσματα μετρήσεων με τεχνική Caching Με την επαναχρησιμοποίηση των δεδομένων στο ταχύτερο επίπεδο της ιεραρχίας, μειώνει τη μέση καθυστέρηση πρόσβασης. Επίσης, μειώνει τον αριθμό των αναφορών σε χαμηλότερα επίπεδα της ιεραρχίας μνήμης. Η τεχνική είναι εξαιρετική για τη βελτιστοποίηση όπως για prefetching, κρύβοντας την καθυστέρηση, αλλά δεν μειώνει την απαίτηση σε εύρος ζώνης μνήμης. Η μείωση αυτή είναι ιδιαίτερα σημαντική για πολυεπεξεργαστές δεδομένου ότι το εύρος ζώνης της μνήμης είναι συχνά η βασική αιτία συμφόρησης του συστήματος. Μολαταύτα, όπως είναι αναμενόμενο με τους μοντέρνους μεταγλωττιστές (compilers), η βελτιστοποίηση που κάνουν στον κώδικα είναι αρκετά εξελιγμένη οπότε η βελτίωση στον χρόνο εκτέλεσης της cached έκδοσης του προγράμματος δεν προσφέρει αντικειμενικά μεγάλη βελτίωση στον χρόνο εκτέλεσης. Η ουσία της παραπάνω απόπειρας είναι να πάρουμε μία ιδέα για την λογική που θα πρέπει να ακολουθήσουμε κατά την μετατροπή του απλού αλγορίθμου σε παράλληλο ώστε να εκμεταλλευτούμε τα πλεονεκτήματα της κατάτμησης δεδομένων (data partitioning) μέσω tiling. Παρακάτω παρουσιάζονται τα αποτελέσματα της cached έκδοσης του προγράμματος. Block Size Number of elements ,079 0,310 1,260 4,960 19,740 78, , , , ,079 0,310 1,250 4,940 19,830 78, , , , ,080 0,320 1,260 4,920 19,640 78, , , , ,080 0,320 1,250 4,870 19,430 77, , , , ,079 0,310 1,240 4,850 19,340 77, , , , ,079 0,300 1,210 4,890 19,670 78, , , , ,078 0,310 1,210 4,870 19,510 78, , , ,300 1,200 4,860 19,420 77, , , ,190 4,780 19,310 77, , , ,760 19,230 77, , , ,972 76, , , , , , , , Πίνακας 3.2 Χρόνοι εκτέλεσης του cached closest pair brute force για CPU 47

49 Average Speedup % Time in seconds Ανάπτυξη αλγόριθμου Closest Pair σε CUDA 1340 Χρόνος για 1M στοιχεία 1320 non-cached speedup 7,2% cached Cache Block size Σχήμα 3.4 Χρόνος εκτέλεσης με χρήση tilling για 1million στοιχεία Παρατηρούμε ότι κατά μέσο όρο, έχουμε ένα μέσο speedup = 6% με την διαφορά να φαίνεται πιο έντονα για στοιχεία Ν > 512K. 10 Speedup για 1Μ στοιχεία Cache Block size Σχήμα 3.5 Speedup με χρήση tilling για 1million στοιχεία 48

50 Παρόλο που το tiling είναι μια παλιά ιδέα, είναι ακόμα πολύ σημαντικό σήμερα. Στον αρχικό κώδικα, για κάθε i, ενδέχεται να μπορούμε να χρησιμοποιήσουμε ξανά τα περισσότερα από τα P [ i ] στοιχεία εφόσον είναι ακόμα στην λανθάνουσα μνήμη (cached), αλλά μόνο αν το μήκος του εσωτερικού βρόχου είναι αρκετά μικρό για να χωρέσει εκεί. Το πραγματικό μέγεθος θα πρέπει να προσδιορίζεται από το επίπεδο της λανθάνουσας μνήμης (cache) που στοχεύουμε για tiling - η L1 θα παρέχει την καλύτερη απόδοση, καθώς είναι η πιο γρήγορη, αλλά είναι επίσης η μικρότερη και θα πρέπει να έχει μικρά μπλοκ και γενικά το overhead μπορεί να είναι πάρα πολύ μεγάλο. Η L2 επιτρέπει μεγαλύτερα tiles, αλλά έχει ελαφρώς μειωμένη απόδοση, και ούτω καθεξής. Έτσι, υποθέτοντας ότι το κάθε Point είναι μήκους 64 bit, μπορεί να χωρέσει με ασφάλεια 512 στοιχεία του πίνακα στην 32K L1, ή 4096 στοιχεία στην 256k L2. Θα έχουμε μια αστοχία για P [ i ] σε κάθε μπλοκ, αν το i είναι έξω από τα όρια του τρέχοντος μπλοκ j, πράγμα σχετικά αμελητέο. Γενικά, δεδομένου ότι είναι αρκετά καλός ο μεταγλωττιστής (compiler), μπορεί να προσπαθήσει να κάνει όλες αυτές τις βελτιώσεις αυτόματα. Είναι αρκετά περίπλοκο όμως, αλλά είναι εύκολο να αποδειχθεί εδώ ότι η αναδιάταξη είναι προφανώς ασφαλής. 49

51 2.3 Παραλληλοποίηση Closest Pair Brute Force για GPU σε CUDA Για την παραλληλοποίηση του αλγορίθμου θα πρέπει να ληφθεί υπόψη η αρχιτεκτονική του υλικού στην οποία προορίζεται να εκτελεστεί ο τροποποιημένος αλγόριθμος. Με αυτό τον τρόπο οι αλλαγές που θα γίνουν στον πηγαίο κώδικα θα έχουν ως αποτέλεσμα την εκμετάλλευση των ιδιαίτερων χαρακτηριστικών της αρχιτεκτονικής και την αύξηση της απόδοσης του αλγορίθμου. Σε αυτό το κεφάλαιο θα παρουσιαστούν οι αλλαγές που πρέπει να γίνουν ώστε ο κώδικας που θα προκύψει να εκμεταλλεύεται στο έπακρο την αρχιτεκτονική των GPU της Nvidia. Για την τροποποίηση του αλγορίθμου Closest Pair θα χρησιμοποιηθούν οι διεπαφές (APIs) και οι ανάλογες επεκτάσεις τις γλώσσας προγραμματισμού C. Όπως έχει ήδη προαναφερθεί, οι GPU είναι ιδανικές για να εκτελούν μια εντολή ταυτόχρονα σε διαφορετικά δεδομένα (SIMD), επομένως το πρώτο βήμα που θα πρέπει να γίνει είναι η εύρεση παραλληλίας και ο επιμερισμός των δεδομένων του αλγορίθμου. Παρόλο που η CUDA χειρίζεται τα νήματα με αυτόματο τρόπο, αυτό δεν απαλλάσσει τους προγραμματιστές από το να συμπεριλάβουν στην ανάλυση τους τα νήματα. Έτσι θα πρέπει να χωρίσουμε τα δεδομένα σε μικρότερα κομμάτια για διανομή μεταξύ των νημάτινων επεξεργαστών. Ας χωρίσουμε τον αλγόριθμο Closest Pair σε δυο βήματα, της προεργασίας και της εύρεσης, εξετάζοντας έτσι αν υπάρχει κάποια παραλληλία στα δεδομένα Μέγεθος σημείων (points) και απαιτήσεις διάταξης Οι εντολές της παγκόσμιας (global) μνήμης υποστηρίζουν την ανάγνωση ή εγγραφή λέξεων μεγέθους ίσο με 1, 2, 4, 8, ή 16 bytes. Οποιαδήποτε πρόσβαση (μέσω μιας μεταβλητής ή ενός δείκτη) σε δεδομένα που βρίσκονται στην παγκόσμια μνήμη συγκεντρώνεται σε μια ενιαία εντολή παγκόσμιας μνήμης, αν και μόνο αν το μέγεθος του τύπου δεδομένων είναι 1, 2, 4, 8, ή 16 bytes και τα δεδομένα είναι φυσικά διατεταγμένα (δηλαδή, η διεύθυνση της είναι πολλαπλάσιο αυτού του μεγέθους). Εάν δεν πληρούνται οι απαιτήσεις για το μέγεθος και την διάταξη, η πρόσβαση μοιράζεται σε πολλαπλές οδηγίες με interleaved πρότυπο πρόσβασης εμποδίζοντας αυτές τις οδηγίες από το να πετύχουν πλήρες coalescing. Ως εκ τούτου, συνιστάται η χρήση των τύπων που ικανοποιούν αυτή την απαίτηση για δεδομένα που βρίσκονται στην παγκόσμιας (global) μνήμη. Η απαίτηση της διάταξης πληρείται αυτόματα για τους ενσωματωμένους τύπους char, short, int, long, LongLong, float, double, όπως float2 ή float4. 50

52 Για τις δομές (structrures), οι απαιτήσεις για το μέγεθος και την διάταξη μπορούν να επιβληθούν από το μεταγλωττιστή με τα προσδιοριστικά διατάξεως align ( 8 ) για float2 ή align ( 16 ) για float4, όπως φαίνεται παρακάτω. Struct align (8) { float x; float y; }; Κάθε διεύθυνση μιας μεταβλητής που βρίσκεται στην παγκόσμια (global) μνήμη ή που επιστρέφεται από μία από τις ρουτίνες κατανομής μνήμης από τον οδηγό ή το runtime API είναι πάντα διατεταγμένη με τουλάχιστον 256 bytes. Η ανάγνωση μη φυσικά διατεταγμένων 8-byte ή 16-byte λέξεων παράγει λανθασμένα αποτελέσματα, έτσι απαιτείται ιδιαίτερη προσοχή για να διατηρηθεί η διάταξη της αρχικής διεύθυνσης οποιασδήποτε τιμής ή πίνακα τιμών αυτών των τύπων. Μια χαρακτηριστική περίπτωση όπου αυτό θα μπορούσε εύκολα να αγνοηθεί είναι όταν χρησιμοποιείτε κάποιο προσαρμοσμένο μοτίβο κατανομής παγκόσμιας (global) μνήμης, σύμφωνα με την οποία οι κατανομές των πολλαπλών πινάκων (με πολλαπλές κλήσεις προς cudamalloc () ή cumemalloc ()), αντικαθίσταται από την κατανομή ενός και μόνο μεγάλου μπλοκ μνήμης μοιρασμένο σε πολλαπλούς πίνακες, οπότε η αρχική διεύθυνση του κάθε πίνακα είναι απλά μια μετατόπιση (offset) από την αρχική διεύθυνση του μπλοκ του Μια πρώτη υλοποίηση σε GPU. Το πρώτο βήμα για την σχεδίαση ενός παράλληλου προγράμματος είναι ο επιμερισμός, δηλαδή ο λογικός επιμερισμός του προβλήματος σε υποπροβλήματα, τα οποία μπορούν να διανεμηθούν σε πολλαπλές διεργασίες. Υπάρχουν δύο τεχνικές επιμερισμού: domain decomposition (επιμερισμός δεδομένων) functional decomposition (επιμερισμός λειτουργιών) Επειδή επιτελείται η ίδια επεξεργασία σε όλα τα δεδομένα, η τεχνική που χρησιμοποιείται είναι στατικός επιμερισμός δεδομένων. Ο επιμερισμός δεδομένων μπορεί να πραγματοποιηθεί με πολλούς τρόπους αλλά για την συγκεκριμένη αρχιτεκτονική της CUDA προτείνεται η τεχνική επιμερισμού δεδομένων σε μπλοκ. Οι GPU τρέχει N αντίγραφα του κώδικα του πυρήνα μας, αλλά για να μπορούμε να δούμε μέσα στον κώδικα πιο μπλοκ τρέχει την κάθε στιγμή χρησιμοποιούμε τις ενσωματωμένες μεταβλητές χαρτογράφησης των νημάτων, μπλοκ και πλέγματος για να προσδιορίσουμε τον βηματισμό (stride). unsigned int tid = threadidx.x + blockidx.x * blockdim.x; unsigned int stride = blockdim.x * griddim.x; 51

53 Στην συγκεκριμένη υλοποίηση θα χρησιμοποιήσουμε 1D πίνακα τύπου float2. Κάθε νήμα αναλαμβάνει να συγκρίνει ένα στοιχείο με όλα τα ακόλουθα του σημεία στο μπλοκ ανά βήμα stride. Για την προσωρινή αποθήκευση των αποτελεσμάτων θα χρησιμοποιηθεί η κοινόχρηστη (shared) μνήμη της συσκευής ενώ για την εύρεση του closest pair και της αντίστοιχης απόστασης θα γίνει επιμέρους reduction σε κάθε μπλοκ και τελικά reduction στην παγκόσμια μνήμη στον πίνακα με τα υποψήφια closest pairs. Ο πίνακας που θα περιέχει τα υποψήφια Closest Pairs θα είναι τύπου AoS float2 και τα αντίστοιχα index της απόστασης μεταξύ τους αποθηκεύονται σε πίνακα float όπως φαίνεται παρακάτω. float4 *d_pairs; float *d_dist; Ο αλγόριθμος που περιγράφει το πρόγραμμα δίνεται παρακάτω σε ψευδοκώδικα: GPU_bruteForceClosestPair <<<blck, trd_blk>>> of P(1), P(2),... P(N) if N < 2 then return else let blck be the total number of launched blocks per stride let trd_blk be the total number of threads per block let tid thread mapping to corresponding element in P let ClosestPairs[ N trd_blk ] be the candidates closest pairs in global memory let Cache[trd_blk] be the caching array of points from P in shared memory let Cache_CP[trd_blk] be the caching array of candidate closest pairs per block in shared memory let mindistance INF; while tid < N Cache[threadIdx] P[tid] temp Cache[threadIdx] cache_index Get the beginning of the cached block or the next point if it is a comparison on the same block foreach i [cache_index, trd_blk] if distance(temp, Cache[i]) < mindistance then mindistance distance Cache_CP[threadID] {temp, Cache[i]} endif endfor sync_threads() Reduce <<<(Cache_CP[trd_blk]>>> ClosestPairs[ ] Cache_CP[0] trd_blk tid tid + stride endwhile Synchronize_Grid() tid Reduce <<<ClosestPairs[ return ClosestPairs[0] endif N ]>>> trd_blk 52

54 Παρακάτω παρατίθεται ο βασικός kernel compare_points_bf. global void compare_points_bf( float2 *dev_p, float *d_dist float4 *d_pairs ) { unsigned int tid = threadidx.x + blockidx.x * blockdim.x; unsigned int stride = blockdim.x * griddim.x; unsigned int cache_index, current_block, grid_offset = 0; float local_dist, local_min; float2 temp; float4 local_closestpair; shared float2 cache_p [BLOCK_SIZE]; shared float cache_d_dist [BLOCK_SIZE]; shared float4 cache_d_pairs [BLOCK_SIZE]; syncthreads(); while (tid < N){ temp.x = dev_p[tid].x; temp.y = dev_p[tid].y; local_closestpair.x = temp.x; local_closestpair.y = temp.y; local_min = FLT_MAX; //cached blocks of P[] in shared memory as cache_p[], current_block corresponds to P's cached sub-array for ( current_block = blockidx.x + (grid_offset * griddim.x); current_block < BLOCKS; current_block++){ //check for boundaries violation within each current block if (current_block * blockdim.x + threadidx.x < N){ //fetch data from GLOBAL memory to SHARED memory, coalesced memory access cache_p[threadidx.x] = dev_p[current_block * blockdim.x + threadidx.x]; } syncthreads(); //get the beginning of the cached block or the next point if it is a comparison //on the same block check boundaries of index for((current_block == blockidx.x + (grid_offset * griddim.x))? cache_index = threadidx.x + 1 : cache_index=0; (cache_index < blockdim.x) && (current_block * blockdim.x + cache_index) < N; cache_index++){ //calculate distance of current points local_dist = (cache_p[cache_index].x - temp.x) * (cache_p[cache_index].x - temp.x) + (cache_p[cache_index].y - temp.y) * (cache_p[cache_index].y - temp.y); if (local_dist < local_min){ local_min = local_dist; local_closestpair.z = cache_p[cache_index].x; local_closestpair.w = cache_p[cache_index].y; } } syncthreads(); } cache_d_dist [threadidx.x] = local_min; cache_d_pairs [threadidx.x] = local_closestpair; syncthreads(); } } //Parallel Reduction: Sequential Addressing in shared mem (bitwise right shift i) //reversed loop and threadid-based indexing - shift operations avoid expensive divisions and modulos. for (unsigned int i = blockdim.x / 2; i > 0; i >>= 1){ if ((threadidx.x < i) && (cache_d_dist [threadidx.x] > cache_d_dist [threadidx.x + i])){ cache_d_dist [threadidx.x] = cache_d_dist [threadidx.x + i]; cache_d_pairs[threadidx.x] = cache_d_pairs[threadidx.x + i]; } syncthreads(); } // We now have the min value of each block stored in cache_cp[0] and // we can store it in dev_closest_pair[] for global memory reduction; if (threadidx.x == 0){ d_dist [tid/block_size] = cache_d_dist [0]; d_pairs [tid/block_size] = cache_d_pairs[0]; } tid += stride; grid_offset ++; syncthreads(); 53

55 Τέλος, παρουσιάζεται ο πυρήνας για reduction στους υποψήφιους πίνακες Closest Pairs όπου παίρνουμε το σημείο με τη μικρότερη απόσταση. global void mindistreduction (float *d_dist float4 *d_pairs){ unsigned int tid = threadidx.x; unsigned int stride = blockdim.x; float local_d_dist; float4 local_d_pairs; shared float cache_d_dist [BLOCK_SIZE]; shared float4 cache_d_pairs [BLOCK_SIZE]; syncthreads(); local_d_dist = d_dist [tid]; local_d_pairs = d_pairs[tid]; tid += stride; syncthreads(); while (tid < N/BLOCK_SIZE){ if (d_dist[tid] < local_d_dist){ local_d_dist = d_dist [tid]; local_d_pairs = d_pairs[tid]; } tid += stride; syncthreads(); } cache_d_dist [threadidx.x] = local_d_dist; cache_d_pairs[threadidx.x] = local_d_pairs; syncthreads(); // Parallel Reduction for (unsigned int i = blockdim.x / 2; i > 0; i >>= 1){ if ((threadidx.x < i) && (cache_d_dist[threadidx.x] > cache_d_dist[threadidx.x + i])){ cache_d_dist [threadidx.x] = cache_d_dist [threadidx.x + i]; cache_d_pairs[threadidx.x] = cache_d_pairs[threadidx.x + i]; } syncthreads(); } } // We now have the min value stored in dev_closest_pair[0] syncthreads(); //write back to each element in d_dist[] and d_pairs[] the min value - pair if (threadidx.x == 0){ d_dist [0] = cache_d_dist [0]; d_pairs[0] = cache_d_pairs[0]; } Αποτελέσματα μετρήσεων Η μεγαλύτερη κατανάλωση χρόνου και άρα η μείωση της απόδοσης των αλγορίθμων οφείλεται κατά κύριο λόγο στους βρόγχους. Στην συγκεκριμένη υλοποίηση χρησιμοποιήσαμε τους παρακάτω πίνακες: 54

56 Πίνακας 3.3 Matrices used Η παραλληλοποίηση έγινε στο μεγαλύτερο και πιο σημαντικό κομμάτι του προγράμματος αφήνοντας ένα μικρό αναπόφευκτο κλάσμα σε σειριακό πρόγραμμα που εκτελείται στον host. Οι χρόνοι που επιτεύχθηκαν είναι αρκετά ικανοποιητικοί, όχι όμως και ο βαθμός κλιμάκωσης του αλγορίθμου. Για N > 2millions ο χρόνος εκτέλεσης είναι αρκετά μεγαλύτερος από τον αντίστοιχο χρόνο που μπορεί να επιτευχθεί με την προσέγγιση Divide & Conquer όπως θα δούμε στο επόμενο κεφάλαιο. Παρακάτω παρουσιάζονται τα αποτελέσματα των μετρήσεων, οι χρόνοι και η αντίστοιχη επιτάχυνση (speedup). N Μεταβλητές-Πίνακες Εμβέλεια (scope) float2 P [ N ] Host Memory float2 dev_p [ N ] Device Global memory float4 d_dist [ N/BLOCK_SIZE ] Device Global memory float4 d_pairs [ N/BLOCK_SIZE ] Device Global memory float2 cache_p [ BLOCK_SIZE ] Device Shared Memory float cache_d_dist [ BLOCK_SIZE ] Device Shared Memory float4 cache_d_dist [ BLOCK_SIZE ] Device Shared Memory Threads per Block (Speedup) CPU time ,190 0, (x141) 0, (x146) 0, (x147) ,290 0, (x162) 0, (x167) 0, (x135) ,880 0, (x171) 0, (x180) 0, (x147) ,460 1, (x190) 1, (x194) 2, (x157) ,631 6, (x197) 6, (x197) 8, (x162) ,572 26, (x203) 26, (x207) 32, (x167) for ever 120, , ,7881 Πίνακας 3.4 Χρόνοι εκτέλεσης Όπως φαίνεται και από τις μετρήσεις ο αλγόριθμος επιταχύνθηκε με ένα μέσο speedup x170 το οποίο είναι αρκετά ικανοποιητικό αποτέλεσμα για μια πρώτη υλοποίηση. Ωστόσο, τα προβλήματα του συγκεκριμένου αλγόριθμου παραμένουν ειδικά όσο αφορά τον παράγοντα κλιμάκωσης (scaling). Παρατηρούμε ότι, όπως και στην έκδοση για CPU, ο αλγόριθμος γίνεται αρκετά αργός για N > 2 million elements. Αυτό οφείλετε στο ότι κάθε thread αναλαμβάνει να συγκρίνει ένα στοιχείο με όλα τα υπόλοιπα 55

57 Time in seconds (Log scale) Ανάπτυξη αλγόριθμου Closest Pair σε CUDA ακόλουθα στοιχεία δημιουργώντας έτσι μια ανισότητα στον επιμερισμό φορτίου (load balance) του αλγόριθμου. Για παράδειγμα, για Ν Total Threads launched, το κάθε thread θα αναλάβει μόνο ένα σημείο για σύγκριση με όλα τα ακόλουθα σημεία, δημιουργώντας ένα μεγάλο time gap μεταξύ του πρώτου νήματος το οποίο έχει να κάνει N-1 συγκρίσεις, με το τελευταίο νήμα που έχει να κάνει μόνο 1 σύγκριση. Στο παρακάτω σχήμα φαίνεται η επιτάχυνση σε λογαριθμική κλίμακα Χρόνος εκτέλεσης Brute Force σε CUDA 1000 CPU time 100 speedup x CUDA time 1024 Trd/blck 0,1 0, Number of elements Σχήμα 3.6 Σχηματική αναπαράσταση επιτάχυνσης (log scale) Thread 0 P 0 P 1 P 2 P 3 P N 3 P N 2 P N 1 Thread 1 P 1 P 2 P 3 P N 3 P N 2 P N 1 Thread 2 P 2 P 3 P N 3 P N 2 P N 1... Thread N 2 P N 2 P N 1 Thread N 1 P N 1 Σχήμα 3.7 Load Balance των νημάτων (κόκκινο είναι idle time) 56

58 Average Speedup Ανάπτυξη αλγόριθμου Closest Pair σε CUDA 210 Speedup Brute Force GPU Number of Elements Πίνακας 3.8 Επιτάχυνση closest pair Brute Force για GPU Μια απλή και γρήγορη λύση στο load balance, είναι να εκτελούνται τα μισά ακριβώς threads και κάθε thread ανάλογα με το φορτίο που του έχει ανατεθεί να αναλαμβάνει τον υπολογισμό και των συμπληρωματικών σημείων P. Παρ όλο που πετύχαμε με αυτόν τον τρόπο καλύτερο load balance, οι χρόνοι εκτέλεσης είναι ίδιοι. Thread 0 P 0 P 1 P 2 P 3 P N 3 P N 2 P N 1 Thread 1 P 1 P 2 P 3 P N 3 P N 2 P N 1 P N 1 Thread 2 P 2 P 3 P N 3 P N 2 P N 1 P N 2 P N 1 Thread N 2 2 P N 2 2 P N P N P N 3 P N 2 P N 1 P N 2 +1 P N 2 +2 P N 2 P N 1 Thread N 2 1 P N 2 1 P N 2 P N 2 +1 P N 2 P N 1 P N 2 P N 2 +2 P N 2 +3 P N 2 P N 1 Σχήμα 3.9 Βελτίωση του Load Balance με ανάθεση συμπληρωματικού φορτίου 57

59 Ο CUDA compiler προσπαθεί να ελαχιστοποιήσει τη χρήση των καταχωρητών (register) για να μεγιστοποιήσει τον αριθμό των νημάτινων μπλοκ που μπορούν να είναι ενεργά ταυτόχρονα. Εάν ένα πρόγραμμα προσπαθεί να ξεκινήσει έναν πυρήνα για την οποία οι registers που χρησιμοποιούνται ανά νήμα επί το μέγεθος του νημάτινου μπλοκ είναι μεγαλύτερο από registers, η εκτέλεση θα αποτύχει. MP Η μεγιστοποίηση της πληρότητας μπορεί να βοηθήσει στην κάλυψη της καθυστέρησης (latency) κατά τη διάρκεια πρόσβασης της global μνήμης που ακολουθείται από ένα syncthreads (). Η πληρότητα καθορίζεται από την ποσότητα της κοινόχρηστης (shared) μνήμης και των registers που χρησιμοποιούνται από κάθε νημάτινο μπλοκ. Πρέπει να τονίσουμε ότι η επίτευξη μεγάλου ποσοστού πληρότητας δεν είναι πανάκεια για καλύτερη απόδοση σε όλους τους αλγόριθμους καθώς υπάρχουν και άλλοι παράγοντες και trade offs που πρέπει να ληφθούν υπ όψη. Για τους παραπάνω χρόνους το μέγιστο occupancy που πετύχαμε για τον πυρήνα <<<compare_points_bf>>> είναι 75% και για τον <<<mindistreduction>>> 100% με 256 threads/block. Ο βασικός περιορισμός της υλοποίησης είναι η χρήση σχετικά μεγάλου ποσοστού της κοινόχρηστης μνήμης. Παρακάτω φαίνονται αναλυτικά τα αποτελέσματα από τον occupancy calculator. Maximum Thread Blocks Per Multiprocessor Blocks/SM * Warps/Block = Warps/SM Limited by Shared Memory per Multiprocessor Physical Max Warps/SM = 64 Occupancy = 48 / 64 = 75% Active Threads per Multiprocessor 1536 Active Warps per Multiprocessor 48 Active Thread Blocks per Multiprocessor 6 Occupancy of each Multiprocessor 75% Τα γραφήματα μας ενημερώνουν για την πληρότητα, καθώς και τον αριθμό των ενεργών νημάτων (threads), στημονιών (warps), τα νημάτινα μπλοκ ανά πολυεπεξεργαστή, και το μέγιστο αριθμό των ενεργών μπλοκ στην GPU για το επιλεγμένο μέγεθος μπλοκ, με κόκκινο τρίγωνο, και για όλα τα άλλα πιθανά μεγέθη μπλοκ. 58

60 Multiprocessor Warp Occupancy (# warps) Multiprocessor Warp Occupancy (# warps) Ανάπτυξη αλγόριθμου Closest Pair σε CUDA My Block Size 256 Impact of Varying Block Size Threads Per Block Σχήμα 3.9 Occupancy και αντίστοιχα block sizes Impact of Varying Register Count Per Thread My Register Count Registers Per Thread Σχήμα 3.10 Occupancy και χρήση register ανά νήμα 59

61 Multiprocessor Warp Occupancy (#warps) Ανάπτυξη αλγόριθμου Closest Pair σε CUDA Impact of Varying Shared Memory Usage Per Block My Shared Memory Shared Memory Per Block Σχήμα 3.11 Occupancy και shared memory ανά block Μπορούμε να συμπεράνουμε ότι η χρήση της κοινόχρηστης (shared) μνήμη είναι ένα ισχυρό χαρακτηριστικό για έναν καλογραμμένο και βελτιστοποιημένο κώδικα CUDA. Η πρόσβαση στην κοινόχρηστη μνήμη είναι πολύ πιο γρήγορα από ό, τι η πρόσβαση στην παγκόσμια μνήμη, σχεδόν δύο τάξεις μεγέθους, επειδή βρίσκεται on-chip. Επειδή χρησιμοποιείται από κοινού από τα νήματα σε ένα μπλοκ, παρέχει ένα μηχανισμό συνεργασίας για τα νήματα. Ένας τρόπος για να χρησιμοποιηθεί η κοινόχρηστη μνήμη ώστε να αξιοποιεί μια τέτοια συνεργασία των νήμα είναι να επιτραπεί η συνεχόμενη- coalesced ανάγνωση-εγγραφή στην παγκόσμια μνήμη, κάτι που φαίνεται και στον κώδικα με σημαντική βελτίωση του χρόνου εκτέλεσης. 60

62 Κεφάλαιο 3 Αλγόριθμος Closest Pair Divide & Conquer. 3.1 Closest Pair Divide & Conquer Θεωρούμε τώρα το πρόβλημα της εύρεσης του πιο κοντινό ζεύγος σημείων σε ένα Q σύνολο για n 2 σημεία. «Closest» αναφέρεται στην Ευκλείδεια απόσταση: η απόσταση μεταξύ των σημείων p 1 και p 2 είναι (x 1 x 2 ) 2 + (y 1 y 2 ) 2 Δύο σημεία στο σύνολο Q μπορεί να συμπίπτουν, στην περίπτωση που η απόσταση μεταξύ τους είναι μηδενική. Αυτό το πρόβλημα έχει εφαρμογές, για παράδειγμα, στην κίνηση-ελέγχου. Ένα σύστημα για τον έλεγχο της εναέριας ή θαλάσσιας κυκλοφορίας θα πρέπει να προσδιορίσει τα δύο πιο κοντινά οχήματα, προκειμένου να ανιχνεύσει πιθανές συγκρούσεις. Η brute-force έκδοση του αλγόριθμου ελέγχει απλά όλα τα ( n 2 ) = Θ(n2 ) ζεύγη σημείων. Σε αυτή την ενότητα, θα περιγράψουμε τον αλγόριθμο με την προσέγγιση διαίρει και βασίλευε του οποίου ο χρόνος εκτέλεσης περιγράφεται από την γνωστή T(n) = 2T ( n 2 ) + O(n) οπότε και ο αλγόριθμος τρέχει σε χρόνο O(nlogn). Κάθε αναδρομική κλήση του αλγορίθμου παίρνει ως είσοδο ένα υποσύνολο P Q και τους πίνακες X και Y, καθένα από τα οποία περιέχει όλα τα σημεία της εισόδου του υποσυνόλου P. Τα σημεία στον πίνακα X ταξινομούνται έτσι ώστε οι x-συντεταγμένες του να αυξάνονται μονότονα. Ομοίως, στον πίνακα Υ οι y-συντεταγμένες είναι μονότονα αυξανόμενες κατά y. (Για την παράλληλη υλοποίηση μας θα χρησιμοποιήσουμε Bitonic sort και odd-even merge sort ). Για την επίτευξη του χρόνου O(nlogn) δεσμευόμαστε μιας και δεν έχουμε την πολυτέλεια να ταξινομούμε σε κάθε αναδρομική κλήση, αν το κάναμε, η επανάληψη για το χρόνο λειτουργίας θα είναι T(n) = 2T ( n 2 ) + O(nlogn) του οποίου η λύση είναι T(n) = O(nlog 2 n). Θα δούμε αργότερα πώς να χρησιμοποιήσουμε "presorting" για να διατηρηθεί αυτή η ταξινόμηση, χωρίς την ανάγκη επαναταξινόμησης σε κάθε αναδρομική κλήση. 61

63 Σε μια δεδομένη αναδρομική κλήση με εισόδους P, X και Y πρώτα ελέγχετε εάν P 3. Αν ναι, η κλήση εκτελεί απλά την brute-force μέθοδο που περιγράφεται παραπάνω, δοκιμάζει όλα τα ζευγάρια ( P ) και επιστρέφει το κοντινότερο ζευγάρι. Αν P 3, η 2 αναδρομική κλήση πραγματοποιεί το διαίρει και βασίλευε όπως θα περιγράψουμε στην συνέχεια. Divide: Βρίσκουμε μια κάθετη γραμμή L που τέμνει το σύνολο σημείων P σε δύο σειρές P L και P R έτσι ώστε P L = P, P 2 R = P, όλα τα σημεία P 2 L είναι πάνω ή προς τα αριστερά της γραμμής L, και όλα τα σημεία στο P R είναι πάνω ή προς τα δεξιά του L. Χωρίζουμε τον πίνακα X σε δύο πίνακες X L και X R, οι οποίοι περιέχουν τα σημεία των P L και P R αντίστοιχα, ταξινομημένα κατά τη μονοτονικά αυξανόμενη x-συντεταγμένη. Ομοίως, διαιρούμε τον πίνακα Y σε δύο πίνακες Υ L και Υ R, τα οποία περιέχουν τα σημεία των P L και P R, αντίστοιχα, ταξινομημένα κατά τη μονοτονικά αυξανόμενη y-συντεταγμένη. Conquer: Έχοντας ήδη χωρίσει τον P σε P L και P R, κάνουμε δύο αναδρομικές κλήσεις, μία για να βρούμε το πιο κοντινό ζεύγος σημείων στον P L και μία για να βρούμε το πλησιέστερο ζεύγος σημείων στον P R. Η είσοδος στην πρώτη κλήση είναι το υποσύνολο P L και οι πίνακες Χ L και Υ L, ενώ η δεύτερη κλήση λαμβάνει σαν είσοδο τους πίνακες P R, Χ R και Y R. Επίσης, έχουμε δ L και δ R οι ελάχιστες αποστάσεις που επιστρέφονται για P L και P R αντίστοιχα, και δ = min( δ L, δ R ). Combine: Το πιο κοντινό ζευγάρι είναι είτε το ζευγάρι με απόσταση δ που βρέθηκε σε μια από τις αναδρομικές κλήσεις, ή είναι ένα ζεύγος σημείων με ένα σημείο στο P L και το άλλο στο P R. Ο αλγόριθμος προσδιορίζει εάν υπάρχει ένα ζεύγος με ένα σημείο στην P L και τo άλλο σημείο στο P R των οποίων η απόσταση είναι μικρότερη από το ήδη υπολογισμένο δ. Παρατηρούμε ότι, αν ένα ζεύγος σημείων έχει απόσταση μικρότερη από δ, και τα δύο σημεία του ζεύγους πρέπει να είναι εντός δ μονάδων της γραμμής L. Έτσι, όπως δείχνει και το παρακάτω σχήμα, και οι δύο πρέπει να βρίσκονται στην οριακή κάθετη λωρίδα μήκους 2δ με κέντρο την γραμμή L. Για να εντοπιστεί ένα τέτοιο ζευγάρι, αν υπάρχει, πρέπει να: 1. Δημιουργήσουμε έναν πίνακα Υ, ο οποίος είναι ο πίνακας Υ χωρίς τα σημεία που δεν ανήκουν στην κάθετη λωρίδα πλάτους 2δ. Επίσης, είναι ταξινομημένος κατά την y-συντεταγμένη, όπως ακριβώς και ο Υ. 62

64 Αν p L P L και p R P R είναι λιγότερο από δ μονάδες μακριά, πρέπει να βρίσκονται μέσα σε ένα δ 2δ ορθογώνιο με κέντρο την γραμμή L. Σχήμα 4.1 Η βασική ιδέα του αλγόριθμου, η κάθετη λωρίδα από σημεία κοντά στην κεντρική τιμή 2. Για κάθε σημείο p του πίνακα Υ, βρίσκουμε τα σημεία p στο Υ που βρίσκονται εντός δ μονάδων. Όπως θα δούμε σύντομα, αρκεί να ελεγχθούν μόνο 7 σημεία που έπονται του p στον πίνακα Υ. Υπολογίζεται η απόσταση από το p για κάθε ένα από αυτά τα 7 σημεία, και αποθηκεύεται η απόσταση δ του πλησιέστερου ζεύγους που βρήκαμε μεταξύ όλων των ζευγών του Υ. 3. Αν δ < δ, τότε η κάθετη λωρίδα περιέχει πράγματι ένα πιο κοντινό ζευγάρι από ότι βρέθηκε στις αναδρομικές κλήσεις. Έτσι επιστρέφεται αυτό το ζευγάρι και η απόστασή του δ. Διαφορετικά, επιστρέφει το πιο κοντινό ζευγάρι και η απόσταση του δ που βρέθηκε από τις αναδρομικές κλήσεις. Εν συνεχεία, θα δείξουμε την ορθότητα του αλγορίθμου και πως επιτυγχάνεται ο O(nlogn) χρόνος εκτέλεσης Ορθότητα αλγόριθμου. Η ορθότητα του αλγορίθμου closest-pair είναι προφανής, με εξαίρεση δύο σημεία. Πρώτον, η βάση της αναδρομής όταν P 3, διασφαλίσουμε ότι δεν προσπαθούμε να λύσουμε ένα υποπρόβλημα που αποτελείται από ένα μόνο σημείο. Κατά δεύτερον, χρειαζόμαστε μόνο τα 7 επόμενα σημεία που ακολουθούν το p στον πίνακα Υ. Ας υποθέσουμε ότι σε κάποιο επίπεδο της αναδρομής, το πιο κοντινό ζεύγος σημείων είναι p L P L και p R P R. Επομένως, η απόσταση δ μεταξύ p L και p R είναι αυστηρά μικρότερη από δ. Το σημείο p L πρέπει να είναι πάνω ή στα αριστερά της γραμμής L και λιγότερο από δ μονάδες μακριά από αυτήν. Ομοίως, το p R είναι πάνω ή προς τα δεξιά της γραμμής L και λιγότερο από δ μονάδες μακριά. Επιπλέον, τα p L και p R απέχουν 63

65 κατακόρυφα μεταξύ τους τουλάχιστον δ μονάδες. Έτσι, όπως δείχνει και η παρακάτω εικόνα, τα p L και p R βρίσκονται μέσα στο δ 2δ ορθογώνιο με κέντρο την γραμμή L. (Μπορεί να υπάρχουν και άλλα σημεία εντός του ορθογώνιου). Ας δείξουμε τώρα ότι το ορθογώνιο δ 2δ μπορεί να περιέχει το πολύ 8 σημεία του P. Σχήμα 4.2 Αναπαράσταση των 4 σημείων που κατά ζεύγη έχουν απόσταση μικρότερη του δ και βρίσκονται μέσα στο ορθογώνιο με διαστάσεις δ 2δ. Στην αριστερή πλευρά είναι τα 4 σημεία στον P L, και στα δεξιά τα 4 σημεία στον P R. Το ορθογώνιο δ 2δ μπορεί να περιέχει 8 σημεία, εάν τα σημεία που εμφανίζονται στη γραμμή L είναι πραγματικά ζεύγη σημείων που συμπίπτουν με ένα σημείο από τον P L και ένα από το P R. Το ορθογώνιο αποτελείται από 2 τετράγωνα δ δ. Από την στιγμή που όλα τα σημεία μέσα στον P L είναι τουλάχιστον δ μονάδες μακριά, στο τετράγωνο μπορεί να βρίσκονται το πολύ 4 σημεία, όπως φαίνεται και στο διπλανό σχήμα. Ομοίως, το πολύ 4 σημεία του P R μπορεί να βρίσκονται στο δεξί δ δ τετράγωνο του ορθογωνίου. Έτσι, το πολύ 8 σημεία του P μπορούν να βρίσκονται εντός του ορθογωνίου δ 2δ. Επίσης, δεδομένου ότι τα σημεία πάνω στην γραμμή L μπορεί να είναι είτε στον P L είτε στον P R, μπορεί να έχουμε έως και 4 σημεία πάνω στην γραμμή. Αυτό το όριο επιτυγχάνεται εάν υπάρχουν δύο ζεύγη σημείων που συμπίπτουν έτσι ώστε κάθε ζεύγος να αποτελείται από ένα σημείο από τον P L και ένα σημείο από τον P R, το ένα ζεύγος είναι στη διατομή των L και την κορυφή του ορθογωνίου, και το άλλο ζεύγος στη διατομή των L και το κάτω μέρος του ορθογωνίου. Έχοντας δείξει ότι το πολύ 8 σημεία του P μπορεί να βρίσκονται μέσα στο ορθογώνιο, μπορούμε εύκολα να δούμε γιατί θα πρέπει να ελέγχουμε μόνο τα 7 σημεία που έπονται μετά από κάθε σημείο στον πίνακα Υ. Ακόμα και αν υποτεθεί ότι το πιο κοντινό ζευγάρι είναι p L και p R, ας υποθέσουμε, ότι το p L προηγείται του p R στον πίνακα Υ. Έτσι, ακόμα και αν το p L βρίσκεται στην αρχή του πίνακα Υ και το p R βρίσκεται προς το τέλος του Y, το p R θα είναι σίγουρα σε ένα από αυτά τα 7 σημεία που ακολουθούν το p L. Έτσι αποδεικνύεται η ορθή λειτουργία του αλγόριθμου. 64

66 3.2 Closest Pair Divide & Conquer για CPU Ένας καλύτερος αλγόριθμος βασίζεται στην αναδρομική Divide & Conquer προσέγγιση, η οποία είναι O (n logn). Ένας ψευδοκώδικας θα μπορούσε να είναι: ClosestPairD&C of (Sx, Sy) where Sx is P(1).. P(N) sorted by x coordinate, and Sy is P(1).. P(N) sorted by y coordinate (ascending order) if N < 3 then return closest points of Sx using brute-force algorithm else xl points of Sx from 1 to N/2 xr points of Sx from N/2 +1 to N xm Sx( N/2 ) x yl { p Sy : p x xm } yr { p Sy : p x > xm } (dl, pairl) closestpair of (xl, yl) (dr, pairr) closestpair of (xr, yr) (dmin, pairmin) (dr, pairr) if dl < dr then (dmin, pairmin) (dl, pairl) endif Syy { p Sy : xm - p x < dmin } m number of points in Syy (closest, closestpair) (dmin, pairmin) foreach i [1, m-1] k i + 1 while ((k m) && (Syy(k) y - Syy(i) y < dmin) if Syy(k) - Syy(i) < closest then (closest, closestpair) ( Syy(k) - Syy(i), {Syy(k), Syy(i)}) endif k k + 1 endwhile endfor return closest, closestpair endif 65

67 Ο αντίστοιχος κώδικας για την έκδοση CPU παρουσιάζεται παρακάτω. float closest(point* sx, int nx, point* sy, int ny, point *a, point *b) { int left, right, i; float d, min_d, x0, x1, mid, x; point a1, b1, *s_yy; /* Floor function for distance calculation */ if (nx <= 8) return brute_force(sx, nx, a, b); s_yy = (point*) malloc(sizeof(point) * ny); mid = sx[nx/2]->x; /* adding points to the y-sorted list; if a point's x is less than mid, add to the begining; if more, add to the end backwards, hence the need to reverse it */ left = -1; right = ny; for (i = 0; i < ny; i++) if (sy[i]->x < mid) s_yy[ ++ left ] = sy[i]; else s_yy[ -- right] = sy[i]; /* reverse the higher part of the list */ for (i = ny - 1; right < i; right ++, i--) { a1 = s_yy[right]; s_yy[right] = s_yy[i]; s_yy[i] = a1; } min_d = closest(sx, nx/2, s_yy, left + 1, a, b); d = closest(sx + nx/2, nx - nx/2, s_yy + left + 1, ny - left - 1, &a1, &b1); if (d < min_d) { min_d = d; *a = a1; *b = b1; } d = sqrt(min_d); /* get all the points within distance d of the center line */ left = -1; right = ny; for (i = 0; i < ny; i++) { x = sy[i]->x - mid; if (x <= -d x >= d) continue; if (x < 0) s_yy[++left ] = sy[i]; else s_yy[--right] = sy[i]; } /* compare each left point to right point */ while (left >= 0) { x0 = s_yy[left]->y + d; while (right < ny && s_yy[right]->y > x0) right ++; if (right >= ny) break; } x1 = s_yy[left]->y - d; for (i = right; i < ny && s_yy[i]->y > x1; i++) if ((x = dist(s_yy[left], s_yy[i])) < min_d) { min_d = x; d = sqrt(min_d); *a = s_yy[left]; *b = s_yy[i]; } left --; } free(s_yy); return min_d; 66

68 Time in Seconds (log scale) Ανάπτυξη αλγόριθμου Closest Pair σε CUDA Αποτελέσματα μετρήσεων Ο αλγόριθμος εκτελέστηκε περίπου 10 φορές για κάθε πλήθος Ν και σαν χρόνοι αναφοράς χρησιμοποιήθηκαν οι μέσοι όροι των χρόνων εκτέλεσης για έκαστο μέγεθος πίνακα P[N]. N elements CP Time without Total Time (sec) Sort time (%) sort (sec) ,060 0,121 50, ,124 0,258 51, ,260 0,518 49, ,540 1,140 52, ,320 2,680 50, ,700 5,140 47, ,370 11,650 45, ,170 27,149 44, ,910 63,390 43,4 Πίνακας 4.1 Αποτελέσματα closest pair Divide & conquer για CPU Πρέπει να επισημάνουμε ότι στους παραπάνω χρόνους φαίνεται ξεκάθαρα ότι ο χρόνος που απαιτείται για την ταξινόμηση των S_x και S_y καταλαμβάνει ένα αρκετά σημαντικό ποσοστό, περίπου 48%, του συνολικού χρόνου εκτέλεσης του αλγόριθμου. 16,00 Απόδοση Divide & Conquer CPU 8,00 4,00 2,00 1,00 0,50 0,25 0,13 0,06 0, Number of Elements Without Sorting time With Sorting time Σχήμα 4.3 Αναπαράσταση απόδοσης του Closest pair D&C CPU version 67

69 % of total running time Ανάπτυξη αλγόριθμου Closest Pair σε CUDA 100% 90% 80% 70% 60% 50% 40% 30% 20% 10% 0% D&C run time distribution Number of elements sorting time CP time Σχήμα 4.4 Ποσοστιαία αναπαράσταση χρόνου εκτέλεσης Closest pair D&C CPU version Παρατηρούμε ότι η επιλογή του αλγόριθμου ταξινόμησης παίζει σημαντικό ρόλο. Στην συγκεκριμένη υλοποίηση CPU χρησιμοποιήθηκε ο quicksort. Επίσης για μεγάλα μεγέθη πίνακα, ο αλγόριθμος είναι ικανοποιητικά γρήγορος, συγκριτικά πάντα με τον Brute Force, απαιτώντας λιγότερο χρόνο ακόμα και από την έκδοση του Brute Force για την GPU, πράγμα που δικαιολογείται λόγω της ασυμπτωτικής πολυπλοκότητας O(n logn). Η κλιμάκωση του αλγόριθμου φαίνεται να είναι αρκετά ικανοποιητική, σε αντίθεση με την έκδοση brute force, απαιτώντας μερικά milliseconds για N 1M στοιχεία. 3.3 Παραλληλοποίηση Closest Pair Divide & Conquer για GPU σε CUDA Σε αυτό το κεφάλαιο θα παρουσιαστούν οι αλλαγές που πρέπει να γίνουν ώστε ο κώδικας που θα προκύψει να εκμεταλλεύεται στο έπακρο την αρχιτεκτονική των GPU της Nvidia με την προσέγγιση bottom-up. Η απόδοση του divide & conquer βασίζεται σε μεγάλο βαθμό στην ταχύτητα του επιλεγμένου αλγόριθμου ταξινόμησης. Για την παράλληλη έκδοση θα επιλέξουμε τον bitonic sort για την αρχική ταξινόμηση του πίνακα P σαν S_x, δηλαδή ταξινόμηση ως προς τις x-συντεταγμένες, και τον odd-even merge sort για τις επιμέρους επαναληπτικές ταξινομήσεις στον πίνακα S_y ως προς τις y-συντεταγμένες δεδομένου του S_x. Παρακάτω θα αναλύσουμε τους επιλεγμένους αλγόριθμους ταξινόμησης. Οι υλοποιήσεις των δύο αλγορίθμων ταξινόμησης είναι στην ουσία τροποποιημένη έκδοση του CUDA sample, CUDA Sorting Networks, ώστε να μπορούν να χρησιμοποιηθούν για τον Closest Pair. 68

70 3.3.1 CUDA Closest Pair Divide & Conquer Ο αλγόριθμος που θα αναλύσουμε στην συνέχεια βασίζεται σε bottom-up προσέγγιση λόγω της ευκολίας υπολογισμού της απόστασης και των αντίστοιχων σημείων της βάσης (base) με χρήση brute force. Οι πιο σημαντικές μεταβλητές που θα χρησιμοποιούνται για υπολογισμό των index με απλές διαιρέσεις ακεραίων, μέγεθος του εκάστοτε batch αλλά και το πλήθος των batch είναι η tid, BatchSize και BatchLength. Η κοινόχρηστη (shared) μνήμη μπορεί να χρησιμοποιηθεί για να έχουμε μόνο μία φορά πρόσβαση στα δεδομένα από την global μνήμη. Επίσης, μπορεί να χρησιμοποιηθεί για να αποφευχθούν οι ασυνεχής (uncoalesced) προσπελάσεις μνήμης με τη φόρτωση και την αποθήκευση δεδομένων σε ένα συγχωνευμένο coalesced μοτίβο από την global μνήμη με μετέπειτα αναδιάταξη της στην κοινόχρηστη (shared) μνήμη. Εκτός από τις συγκρούσεις στις τράπεζες μνήμης ανά στημόνι (warp) (memory bank conflicts), δεν υπάρχει καμία ποινή για τις μη διαδοχικές ή μη ευθυγραμμισμένες προσβάσεις από ένα warp στην shared μνήμη. Μεταβλητές-Πίνακες Εμβέλεια (scope) float2 h_p [N] Host Memory float2 h_s_x [N] Host Memory float2 h_s_y [N] Host Memory float2 d_s_x [N] Device Global memory float2 d_s_y [N] Device Global memory bool d_s_yy [N/BASE] Device Global memory float d_dist [N/BASE] Device Global memory float4 d_pairs [N/BASE] Device Global memory Πίνακας 4.2 Χρόνοι εκτέλεσης O αλγόριθμος εκτελεί το πολύ κ = log 2 N log 2 base = log 2 N base επαναλήψεις στο κύριο loop δηλαδή εξαρτάται από τον αριθμό των στοιχείων Ν αλλά και από το πλήθος των στοιχείων της βάσης που επιλέγουμε για να εφαρμόσουμε επιμέρους τον Brute Force. Η συγκεκριμένη υλοποίηση υποστηρίζει 32 base 1024 με τα όρια να εξαρτάται και από την υπολογιστική δυνατότητα της συσκευής (compute capability) και συνεπώς από την διαθέσιμη κοινόχρηστη (shared) μνήμη. Με 1024 στοιχεία τύπου float2 θα έχουμε byte = 8KBytes και για float byte = 4KBytes, συνολικα 12ΚΒ δεσμευμένα ανά μπλοκ από τα συνολικά 48KB shared memory ανά Steam Multiprocessor που διαθέτει η NVidia 670 (Compute Capability 3.0). 69

71 Παρακάτω δίνεται ο αλγόριθμος σε ψευδοκώδικα για την έκδοση D&C της GPU: GPU_ClosestPair_D&C of P(1), P(2),... P(N) where N power of 2 if N < 2 then return else d_sx points of P[N] sorted as of x coordinate. d_sy points of d_sx[n] sorted as of y coordinate. d_syy corresponing block of size BASE of d_sy that has at least 1 points within the vertical strip (from point L x ), sorted as of y coordinate with length N. BASE d_dist, d_pairs candidate closest pair points and corresponding min dist of length N BASE //sort P as of x coordinate and store it in d_sx bitonicsort <<< d_sx, P, 1, N >>> //find min CP on each batch of length BASE bruteforce <<< d_sx, d_dist, d_pairs >>> for (batchlength = 2 BASE, batchlength N, batchlength >> 1) N batchsize BatchLength //subsequent sort of each batch of length batchlength //of points in d_sx as of y coordinate bitonicsort <<< d_sy, d_sx, batchsize, batchlength >>> //fetch current minimum CP with Parallel reduction on d_dist-d_pairs //Result will be stored at index 0 of d_dist and d_pairs mindistreduction <<< d_dist, d_pairs >>> i [d_syy] false endif endfor //Find the points in d_sy that are within distance d_dist[0] from corresponding L x //in the vertical strip and mark the corresponding indices of d_sy in d_syy findverticalstrip <<< d_syy, d_sx, d_sy, d_dist, batchlength >>> //Compare each point in the vertical stip with the onward 7 points //in the vertical strip of the corresponding batch of length batchlength comparestrippoints <<< d_syy, d_sy, d_dist, batchlength >>> //Get the Closest Pair and Minimum Distance at index 0. mindistreduction <<< d_dist, d_pairs >>> Για την υλοποίηση θα χρησιμοποιήσουμε κοινό min_dist και min_pair για όλα τα επιμέρους μπλοκ και batch ενώ λόγω της bottom-up προσέγγισης δεν αλλοιώνει το αποτέλεσμα και διευκολύνει τους υπολογισμούς πετυχαίνοντας λιγότερα read/writes και 70

72 συναλλαγές με τους πίνακες. Το υποψήφιο ζευγάρι με την αντίστοιχη ελάχιστη απόσταση σε κάθε step του αλγόριθμου υπολογίζεται από τον πυρήνα mindistreduction, αποθηκεύεται στο d_dist[0] και d_pairs[0] και έπειτα στην constant memory για γρηγορότερο global broadcast, πετυχαίνοντας έτσι ανάλογη ταχύτητα επιπέδου register. Παρακάτω θα δούμε αναλυτικότερα τον κάθε πυρήνα και πως συμβάλλει έκαστος στον υπολογισμό. Η λογική που χωρίζονται και ταξινομούνται οι πίνακες είναι η εξής: S_x 0 1 base-1 base 2 base 1 N-base N-2 N-1 S_y 0 1 base-1 base 2 base 1 N-base N-2 N-1 Index = tid base S_yy N base 3 N base 2 N base 1 d_dist d_pairs N base 3 N base 3 N base 2 N base 2 N base 1 N base 1 Σχήμα 4.17 Καταμερισμός των βασικών πινάκων Το πλήθος των δεσμίδων (batch) εξαρτάται από το base το οποίο επίσης ορίζει και τον αριθμό των T threads ανά μπλοκ. Έτσι κάθε μπλοκ θα αποτελείται από T = base νήματα (threads) με εξαίρεση τον πυρήνα mindistreduction ο οποίος εκτελείται πάντα με 1 μπλοκ και μέγιστο αριθμό νημάτων ανά μπλοκ (maximum threads / block) (ανάλογο του Compute Capability της GPU). Για παράδειγμα αν N = και base = 32 θα έχουμε N = base = 2048 το οποίο 32 είναι και το μήκος των πινάκων S_yy, d_dist και d_pairs. 71

73 Οι πιο σημαντικοί λόγοι που επιλέξαμε αυτήν την αντιστοιχία είναι οι εξής: Μικρότεροι πίνακες και συνεπώς χαμηλότερο utilization της Global Memory. Ευκολότερη διαχείριση των αντίστοιχων στοιχείων ανά μπλοκ. Αποφυγή ενός μεγάλου μέρους memory bank conflict λόγω αναγκαίας προσπέλασης της Global μνήμη και επίτευξη αποτελεσματικότερων coalesced reads-writes. Αποτελεσματικότερη χρήση της διαθέσιμης κοινόχρηστης (Shared) Memory. Γρηγορότερο Reduction λόγω μικρότερου μεγέθους πινάκων. Ας δούμε παρακάτω πώς διαχειρίζονται αυτούς τους βασικούς πίνακες οι πυρήνες για τον υπολογισμό του closest pair. bitonicsort : Ταξινόμηση μία φορά του P πίνακα ως προς x και αποθήκευση στον S_x. Εν συνεχεία, επιμέρους ταξινόμηση του S_y των αντίστοιχων σημείων του S_x ως προς y ανά 2 συνεχόμενα batch, τα οποία συγχωνεύονται σε ένα batch, όπου το μήκος των σε κάθε βήμα δίνεται από το batchlength. Έτσι, για παράδειγμα, στο πρώτο βήμα το batchlength = 2 base και στο τελευταίο θα είναι batchlength = Ν. Παρακάτω υποθέτουμε τα σημεία P(x,y) με Ν = 16 και base = 2. P 4,0 9,12 10,7 0,8 14,10 8,14 3,1 7,3 1,9 2,13 6,11 12,15 5,2 13,4 15,6 11,5 bitonicsort ως προς x, batchlength = Ν S_x 0,8 1,9 2,13 3,1 4,0 5,2 6,11 7,3 8,14 9,12 10,7 11,5 12,15 13,4 14,10 15,6 bitonicsort ως προς y, batchlength = 4 S_y 3,1 0,8 1,9 2,13 4,0 5,2 7,3 6,11 11,5 10,7 9,12 8,14 12,15 13,4 14,10 15,6 bitonicsort ως προς y, batchlength = 8 S_y 4,0 3,1 5,2 7,3 0,8 1,9 6,11 2,13 11,5 10,7 9,12 8,14 12,15 13,4 14,10 15,6 bitonicsort ως προς y, batchlength=n=16 S_y 4,0 3,1 5,2 7,3 13,4 11,5 15,6 10,7 0,8 1,9 14,10 6,11 9,12 2,13 8,14 12,15 Σχήμα 4.18 Βασική μεταβολή των πινάκων S_x, S_y με bitonicsort για N = 16, base = 2 72

74 bruteforce : Μετά την ταξινόμηση των στοιχείων του πίνακα P στον πίνακα S_x ως προς την x συντεταγμένη υπολογίζεται η απόσταση και το ζευγάρι σημείων του κάθε επιμέρους αρχικού batch με πλήθος σημείων που ισούται με base. Ο αριθμός των μπλοκς που εκτελούνται παράλληλα είναι N, αν με κάθε μπλοκ να έχει αριθμό threads ίσο με base, ενώ οι συγκρίσεις χρησιμοποιούν αποκλειστικά την Shared Memory. Κάθε μπλοκ βρίσκει το δικό του ελάχιστο-ζεύγος με reduction στην shared μνήμη και το αποτέλεσμα γράφεται στο αντίστοιχο index των d_dist tid base και d_pairs tid. Η πολυπλοκότητα του πυρήνα εξαρτάται από το μέγεθος του κάθε base μπλοκ δηλαδή τον αριθμό των T threads και ισούται με base, O(base 2 ). block base S_x 0,8 1,9 2,13 3,1 4,0 5,2 6,11 7,3 8,14 9,12 10,7 11,5 12,15 13,4 14,10 15,6 d_ Dist d_ Pairs Σχήμα 4.19 bruteforce για N = 16, base = 2 bruteforce kernel <<< N blocks, BASE threads>>> of S_x(1), S_x(2),... S_x(N) BASE min_δ distance initialized to min_pair corresponding closest pairs tid threadidx + blockidx * blockdim, mapping to corresponding element in S_x srd_s_x shared array of length BASE, S_x points from tid to tid + BASE srd_dist shared array of length BASE, for storing corresponding min dist srd_pairs shared array of length BASE, for storing corresponding closest pair while (tid < N) srd_s_x[threadidx] S_x[tid] sync_threads() foreach i [threadidx + 1, blockdim] δ distance(srd_s_x[threadidx], srd_s_x[i]) if δ < min_δ then min_δ δ min_pair {srd_s_x[threadidx], srd_s_x[i]} endif endfor srd_dist [threadidx] min_δ srd_pairs[threadidx] min_pair sync_threads() parallel_reduction(srd_dist, srd_pairs) if threadidx == 0 then Dist tid srd_dist [0] BASE Pairs tid BASE srd_pairs[0] endif tid tid + blockdim * griddim sync_threads() endwhile 73

75 mindistreduction : Αναλαμβάνει το παράλληλο reduction με Sequential Addressing στην κοινόχρηστη (shared) μνήμη για τους πίνακες d_dist και d_pairs. Η χρήση Sequential Addressing εγγυάται ότι δεν θα έχουμε Shared Memory Bank Conflicts. Σε αντίθεση με την παγκόσμια (global) μνήμη, δεν υπάρχει ποινή για (strided) βηματικές προσβάσεις κοινόχρηστης μνήμης. Ο συγκεκριμένος πυρήνας εκτελείται πάντα με 1 μπλοκ λόγω συγχρονισμού και μικρού μεγέθους των πινάκων. Σχήμα 4.19 Parallel Reduction: Sequential Addressing Η επιλογή της bottom-up προσέγγισης καθώς και η φύση του αλγορίθμου όσο αφορά τον υπολογισμό των οριακών σημείων L x του κάθε ζεύγους δεσμίδων (batches), μετά από την συγχώνευση 2 γειτονικών batch, μας επιτρέπει να χρησιμοποιήσουμε με ασφάλεια σαν απόσταση-ζεύγος σημείων την συνολικά ελάχιστη απόσταση που υπολογίζεται σε κάθε βήμα και αποθηκεύεται στους πίνακες και πιο συγκεκριμένα στο 1 ο στοιχείο αυτών, δηλαδή d_dist[0] και d_pairs[0]. // Parallel Reduction for (unsigned int i = blockdim.x / 2; i > 0; i >>= 1){ if ((threadidx.x < i) && (cache_d_dist[threadidx.x] > cache_d_dist[threadidx.x + i])){ cache_d_dist [threadidx.x] = cache_d_dist [threadidx.x + i]; cache_d_pairs[threadidx.x] = cache_d_pairs[threadidx.x + i]; } syncthreads(); } // We now have the min value stored in dev_closest_pair[0] //write back to each element in d_dist[] and d_pairs[] the min value - pair if (threadidx.x == 0){ d_dist [0] = cache_d_dist [0]; d_pairs[0] = cache_d_pairs[0]; } 74

76 Έτσι όπως φαίνεται και στο σχήμα 4.1 η απόσταση που λαμβάνεται υπ όψη για την εύρεση των σημείων που ανήκουν σε κάθε επιμέρους vertical strip S_yy θα είναι η δ = d_dist [0]. Με Τ threads παράλληλα στο μπλοκ, εξαρτώμενο πάντα από το Compute Capability της GPU, η πολυπλοκότητα για τις συγκρίσεις των στοιχείων στους πίνακες d_dist και d_pairs είναι O ( M ) με M = N δηλαδή O T base (Ν), T Για το reduction στην κοινόχρηστη (shared) μνήμη O(log 2 M) = O(log 2 Ν) οπότε O ( Ν Τ + log 2 Ν) και για το νημάτινο μπλοκ με Ν = Τ έχουμε τελικά O(log 2 Ν). findverticalstrip: Για κάθε σημείο p του πίνακα, S_y εντοπίζουμε τα σημεία p στον S_yy που βρίσκονται εντός δ μονάδων. Το σημείο γραμμή L x που αντιστοιχεί στο κάθε merged batch ζευγάρι υπολογίζεται ως: tid batchlength L x = d_s_x [ batchlength + ] batchlength 2 x Αν ένα μπλοκ, μήκους base, περιέχει σημείο που απέχει από το L x απόσταση μικρότερη ή ίση με δ τότε θέτουμε true το αντίστοιχο index στον πίνακα S_yy tid. base Έτσι γνωρίζουμε ποιο μπλοκ χρειάζεται να ελέγξει τα σημεία του, αποφεύγοντας περιττά reads-copies φορτία από την global memory στην shared memory. Όσο προχωράνε τα steps του βασικού for loop στον host και αυξάνεται το batchlength, όλο και περισσότερα μπλοκ θα ανήκουν στο ίδιο batch και ως εκ τούτου θα έχουν κοινό L x γεγονός που δεν αποτελεί πρόβλημα λόγω αποθήκευσης του στην L2 cache και του μηχανισμού broadcast. Για παράδειγμα, ας υποθέσουμε ότι έχουμε τον πίνακα από το σχήμα 4.18 και είμαστε σε ένα ενδιάμεσο στάδιο με N = 16, base = 2, blocks = 8, threads = 2, block batchlength = 8 και batchsize = 2. Εδώ σε κάθε δέσμη (batch) θα έχουμε 4 νήματα που διαβάζουν την θέση του L x αντί για 8. Η διαφορά στον χρόνο υπολογισμού γίνεται αισθητή όσο το batchlength τείνει στο N. 1 st batch 0,8 1,9 2,13 3,1 4,0 5,2 6,11 7,3 8,14 9,12 10,7 11,5 12,15 13,4 14,10 15,6 thread 0, thread 1 thread 0, thread 1 BLOCK 0 BLOCK 1 L x Σχήμα 4.20 Υπολογισμός του L x και των σημείων S_yy 75

77 Παρακάτω δίνεται ο αλγόριθμος του kernel findverticalstrip σε ψευδοκώδικα: findverticalstrip <<< N BASE L x blocks, BASE threads>>> of S_y(1), S_y(2),... S_y(N) in S_yy corresponding middle point L from S_x Global mimimum distance threadidx + blockidx * blockdim, map to corresponding element in S_x,S_y min_δ tid block_has_sp shared variable for corresponding block batchlength length of each batch while (tid < N) if threadidx == 0 then block_has_sp false endif sync_threads() tid batchlength L x S_x [ batchlength + ] batchlength if S_y[tid].x - L x < min_δ then block_has_sp true endif sync_threads() if threadidx == 0 then S_yy tid BASE true endif tid tid + blockdim * griddim sync_threads() endwhile 2 x comparestrippoints: Ο πιο σημαντικός kernel, από άποψη φορτίου (workload), όπου ελέγχονται όλα τα σημεία που βρίσκονται στην κάθετη λωρίδα του αντίστοιχου πίνακα S_yy. Σε κάθε thread ανατίθεται ένα σημείο και υπολογίζεται αν το σημείο αυτό ανήκει ή όχι στην κάθετη λωρίδα. Όπως και στον αρχικό αλγόριθμο για CPU, αρκεί να ελεγχθούν μόνο 7 σημεία που έπονται του p στον πίνακα S_yy. Για να αποφύγουμε όμως τον υπερβολικό αριθμό προσβάσεων στην global memory της GPU, θα χρησιμοποιήσουμε την ίδια προσέγγιση, δηλαδή, ότι χρειάζεται να ελεγχθούν το πολύ 8 σημεία- μπλοκ, και στο επίπεδο των επιμέρους μπλοκ. Συνεπώς, κάθε μπλοκ που περιέχει σημείο εντός της κάθετης λωρίδας έχει το αντίστοιχο S_yy index = true και θα χρειάζεται να ελέγξει το πολύ τα επόμενα 7 μπλοκς, για την worst case υπόθεση όπου κάθε επόμενο μπλοκ έχει μόνο ένα σημείο για σύγκριση, για τα οποία το αντίστοιχο index τους στον S_yy είναι επίσης true. Σε περίπτωση που ο index του S_yy είναι false δεν επιβαρύνεται με φορτίο το αντίστοιχο μπλοκ. Ως αποτέλεσμα, η συγκεκριμένη σχεδιαστική επιλογή σχεδόν τετραπλασίασε το συνολικό speedup στην πλειοψηφία των δοκιμών. Για την περίπτωση όπου N=1M 76

78 είχαμε 330ms (speedup x3,4) έναντι ~90ms (speedup x13). Οι συγκρίσεις που γίνονται για τα k μπλοκ με k = Ν είναι Ο(8 k log BASE 2 k) = Ο(k log 2 k). Εν αντιθέσει, στην περίπτωση όπου δεν θα είχαμε τον περιορισμό στον έλεγχο των συγκρίσεων μεταξύ των μπλοκ θα ήταν Ο(k 2 log 2 k). Ο kernel τερματίζει σε δύο περιπτώσεις: όταν φτάσει στο όριο που αναλογεί στην αντίστοιχη δέσμη (batch) (μέσω της μεταβλητής sub_tid = tid batchlength batchlength + batchlength). όταν κάθε μπλοκ ολοκληρώσει την σύγκριση με τα 7 επόμενα μπλοκς (μέσω της shared variable block_count). 1 st batch 2 nd batch L x 0,8 1,9 2,13 3,1 4,0 5,2 6,11 7,3 8,14 9,12 10,7 11,5 12,15 13,4 14,10 15,6 trd 0, trd 1 trd 0, trd 1 trd 0, trd 1 trd 0, trd 1 BLOCK 0 BLOCK 1 BLOCK 2 BLOCK 3 S_yy index Σχήμα 4.21 Συγκρίσεις σημείων εντός του batch για block=base=2 και batchlength = 8 Για παράδειγμα, όπως φαίνεται και στο σχήμα 4.21, το BLOCK 0 έχει τον αντίστοιχο δείκτη (index) του S_yy = true άρα περιέχει σημείο εντός της κάθετης λωρίδας. Αρχικά, κάθε εσωτερικό σημείο του BLOCK 0 θα συγκριθεί με τα επόμενα εσωτερικά σημεία του. Στη συνέχεια, τα σημεία του BLOCK 0 θα συγκριθούν με τα σημεία των επόμενων μπλοκ τα οποία επίσης περιέχονται στην κάθετη λωρίδα. Το BLOCK 1 όμως δεν περιέχει στοιχεία προς σύγκριση άρα θα προσπελαστεί χωρίς να αυξηθεί ο μετρητής block_count. Τα BLOCK 2 και BLOCK 3 περιέχουν σημεία οπότε θα γίνει η σύγκριση με τα αντίστοιχα σημεία του BLOCK 0 και θα αυξηθεί και ο μετρητής block_count. Στο συγκεκριμένο παράδειγμα το loop του kernel θα τερματίσει στο BLOCK 3 επειδή είναι και το όριο του συγκεκριμένου batch. Ο συγκεκριμένος kernel καταλαμβάνει το μεγαλύτερο ποσοστό χρόνου, περίπου 68% του χρόνου εκτέλεσης του αλγόριθμου closest pair, μη συμπεριλαμβανομένου του χρόνου ταξινόμησης. 77

79 Παρακάτω δίνεται ο αλγόριθμος του πυρήνα comparestrippoints σε ψευδοκώδικα: comparestrippoints <<< N blocks, BASE threads>>> of {S_y(1), S_y(2),...S_y(N)} S_yy BASE min_δ minimum distance initialized to Disτ[0] min_pair corresponding closest pair points Pairs[0] δ thread distance local_point thread point tid threadidx + blockidx * blockdim, map to corresponding element in S_x,S_y subtid sliding block for mapping to corresponding element in S_y, S_yy batchlength length of each batch block_count shared variable counter srd_s_y shared array of length BASE, S_y points from tid to tid + BASE srd_dist shared array of length BASE, for storing corresponding min dist srd_pairs shared array of length BASE, for storing corresponding closest pair while (tid < N) if S_yy tid == true then BASE subtid tid if threadidx == 0 then block_count 0 endif local_point S_y[tid] while (subtid < tid batchlength batchlength + batchlength && block_count < 8) if S_yy subtid == true then BASE if threadidx == 0 then block_count++ endif srd_s_y[threadidx] S_y[subtid] sync_threads() foreach i [(tid == subtid? threadidx + 1 : 0), blockdim] δ distance(local_point, srd_s_y[i]) if (tid, subtid Vertical Strip) && (δ < Disτ[0]) then min_δ δ min_pair {local_point, srd_s_y[i]} endif endfor endif subtid subtid + blockdim sync_threads() endwhile srd_dist [threadidx] min_δ srd_pairs[threadidx] min_pair sync_threads() parallel_reduction(srd_dist, srd_pairs) if threadidx == 0 then Dist tid srd_dist [0] BASE Pairs tid BASE srd_pairs[0] endif endif tid tid + blockdim * griddim sync_threads() endwhile 78

80 3.3.2 Αποτελέσματα μετρήσεων: Παρακάτω παρουσιάζονται τα αποτελέσματα μετρήσεων για διάφορα μεγέθη του Ν και base, το αντίστοιχο speedup και το αντίστοιχο βάρος του αλγόριθμου έναντι του χρόνου ταξινόμησης. N Threads per Block CPU time ,123 8,423 8,964 10, ,627 18,178 19,163 22, ,536 39,866 42,648 46, ,256 88,816 93,651 98, , , , , , , , , , , , , , , , ,315 Πίνακας 4.3 Αποτελέσματα closest pair divide & conquer GPU σε milliseconds (ms) N Speedup ,26 14,37 13,50 11, ,15 14,19 13,46 11, ,90 12,99 12,15 11, ,97 12,84 12,17 11, ,50 13,16 12,62 11, ,21 11,88 11,59 10, ,18 11,67 11,42 10, ,40 11,91 11,66 10,98 Πίνακας 4.4 Speedup closest pair divide & conquer GPU 79

81 Average Speedup Ανάπτυξη αλγόριθμου Closest Pair σε CUDA 16 Speedup Divide & Conquer GPU Σχήμα 4.22 Επιτάχυνση closest pair divide & conquer για GPU Όπως φαίνεται και από τις μετρήσεις ο αλγόριθμος επιταχύνθηκε με ένα μέσο speedup x12 το οποίο είναι αρκετά ικανοποιητικό αποτέλεσμα. Ωστόσο, για να έχουμε μια καλύτερη εικόνα και τα προβλήματα και τους περιορισμούς του συγκεκριμένου αλγόριθμου θα πρέπει να εξετάσουμε και το χρονικό ποσοστό εκτέλεσης που ανήκει στο sorting. Παρακάτω, παρατίθενται τα ποσοστά του συνολικού χρόνου που απαιτούνται για την ταξινόμηση ανά πλήθος N και μεγέθους μπλοκ (block size). Παρατηρούμε ότι η επιλογή του αλγόριθμου ταξινόμησης παίζει σημαντικό ρόλο. Στην συγκεκριμένη υλοποίηση GPU χρησιμοποιήθηκε ο bitonic sort για την ταξινόμηση του S_x και του S_y λόγω βέλτιστων αποτελεσμάτων. Για τις μετρήσεις χρησιμοποιήθηκε: Number of Elements ομοιόμορφη κατανομή από floats χωρίς διπλότυπα σημεία σε διάστημα (0, 1] και [ 1, 0) (0, 1] κανονική κατανομή από floats σε διάστημα με διπλότυπες τιμές [ 1, 1] με μ = 0 και σ ν = λ ν 1 όπου λ = 1 10 = 0,1 και ν {Ζ+ και ν < 6}. και με μέσο όρο από 10 μετρήσεις ανά κατηγορία

82 Sorting portion of total time Ανάπτυξη αλγόριθμου Closest Pair σε CUDA Επίσης για μεγάλα μεγέθη πίνακα, Ν > 8Μ, ο αλγόριθμος είναι ικανοποιητικά γρήγορος, με τον παράγοντα του χρόνου ταξινόμησης να επηρεάζει ακόμα περισσότερο, σχεδόν 90% του χρόνου εκτέλεσης. 100 D&C run time distribution GPU Numer of Elements Σχήμα 4.23 Ποσοστό χρόνου ταξινόμησης του closest pair divide & conquer για GPU Ο κύριος λόγος που επηρεάζει η ταξινόμηση την συνολική απόδοση σε τέτοιο υψηλό βαθμό, οφείλεται κυρίως στην bottom-up προσέγγιση του αλγόριθμου και η ανάγκη για επιμέρους συγχώνευση των δεσμίδων (batch) του πίνακα S_y σε κάθε επανάληψηβήμα (iteration). Επίσης, όπως αναφέραμε στην αρχή του κεφαλαίου, ο αριθμός των βημάτων (step) του αλγόριθμου κ επηρεάζει και τον αριθμό των επιμέρους ταξινομήσεων (sort) στον πίνακα S_y, εξ ου και τα ελαφρώς χαμηλότερα ποσοστά ταξινόμησης του χρόνου εκτέλεσης για μεγέθη μπλοκ 256 και 512. Το trade-off εξαρτάται και από την χειρότερη επίδοση του αλγόριθμου closest pair για τις επιλογές μπλοκ 256 και 512 λόγω περιορισμού του πυρήνα comparestrippoints σχετιζόμενου με την χρήση μεγάλου μέρους της κοινόχρηστης (shared) μνήμης ανά μπλοκ. Όπως διακρίνουμε και στα σχήματα η καλύτερη επίδοση επιτυγχάνεται με μέγεθος μπλοκ ίσο με 128. Το occupancy που επιτεύχθηκε στους πυρήνες με το συγκεκριμένο μέγεθος μπλοκ είναι ~ 98% ενώ μόνο στον πυρήνα comparestrippoints ήταν περίπου %. Παρ όλα αυτά, όπως προαναφέραμε και στην αρχή του κεφαλαίου, το occupancy δεν είναι η πανάκεια για καλύτερη απόδοση. Στην συγκεκριμένη περίπτωση η ρύθμιση (tunning) των πυρήνων (kernel) πραγματοποιήθηκε με βάση την ελαχιστοποίηση των branch divergence, την χρήση 81

83 Multiprocessor Warp Occupancy (# warps) Ανάπτυξη αλγόριθμου Closest Pair σε CUDA καταχωρητών (register), του μεγέθους της κοινόχρηστης (shared) memory και των bank conflict, και το πλήθος πρόσβασης στην global μνήμη. Τα if statements εκτελούνται συλλογικά από όλα τα threads ανά μπλοκ, με εξαίρεση στις αναπόφευκτες περιπτώσεις ανανέωσης με εγγραφή (update) των μεταβλητών που αντιστοιχούν στην ελάχιστη απόσταση (minimum distance) και ελάχιστο ζεύγος (minimum pair), τόσο σε τοπική (local) σκοπιά ανά νήμα, όσο και για την κοινόχρηστη (shared) - global σκοπιά μνήμης. Ο βασικός περιορισμός της υλοποίησης είναι η χρήση σχετικά μεγάλου ποσοστού της κοινόχρηστης (shared) μνήμης. Παρακάτω φαίνονται αναλυτικά τα αποτελέσματα από τον occupancy calculator. Maximum Thread Blocks Per Multiprocessor Blocks/SM * Warps/Block = Warps/SM Limited by Shared Memory per Multiprocessor Physical Max Warps/SM = 64 Occupancy = 48 / 64 = 75% Active Threads per Multiprocessor 1536 Active Warps per Multiprocessor 48 Active Thread Blocks per Multiprocessor 12 Occupancy of each Multiprocessor 75% Τα γραφήματα μας ενημερώνουν για την πληρότητα, καθώς και τον αριθμό των ενεργών νημάτων, στημονιών (warps), τα νημάτινα μπλοκ ανά πολυεπεξεργαστή, και το μέγιστο αριθμό των ενεργών μπλοκ στην GPU για το επιλεγμένο μέγεθος μπλοκ, με κόκκινο τρίγωνο, αλλά και για όλα τα άλλα πιθανά μεγέθη μπλοκ. Impact of Varying Block Size My Block Size Threads Per Block Σχήμα 4.25 Occupancy και αντίστοιχα block sizes 82

84 Multiprocessor Warp Occupancy (#warps) Multiprocessor Warp Occupancy (# warps) Ανάπτυξη αλγόριθμου Closest Pair σε CUDA Impact of Varying Register Count Per Thread My Register Count Registers Per Thread Σχήμα 3.26 Occupancy και χρήση register ανά νήμα Impact of Varying Shared Memory Usage Per Block My Shared Memory Shared Memory Per Block Σχήμα 3.27 Occupancy και shared memory ανά block Η εκτέλεση του αλγορίθμου πραγματοποιήθηκε με την προεπιλεγμένη ρύθμιση (default caching mode) δηλαδή με load granularity των 128 bytes και ιεραρχική δομή στα cache hits από L1 cache > L2 cache > GMEM. 83

85 3.3.4 Ανασκόπηση αποτελεσμάτων: Όπως φαίνεται και από τα γραφήματα των αποτελεσμάτων, μπορούμε να κάνουμε μια εκτίμηση για τα προβλήματα που αντιμετωπίσαμε κατά τον σχεδιασμό και την υλοποίηση έχοντας πάντα σημείο αναφοράς ένα μέσο όρο για το performance των kernels. Σχετικά με την αξιοποίηση (utilization) και τις βελτιστοποιήσεις που αφορούν την μνήμη, οι οποίες είναι και οι πιο σημαντικές, δόθηκε υψηλή προτεραιότητα στην βηματική strided coallescent πρόσβαση, στην ελαχιστοποίηση μεταφοράς δεδομένων μεταξύ του εξυπηρετητή (host CPU) και της συσκευής (GPU device), με σημαντικό χαρακτηριστικό την ασύγχρονη εκτέλεση πυρήνων, ακόμη και αν σημαίνει ότι η εκτέλεση ορισμένων πυρήνων στη συσκευή δεν παρουσιάζουν σημαντικό κέρδος απόδοσης σε σχέση με τη λειτουργία τους στην CPU. Οι χρόνοι που επιτεύχθηκαν με 128 threads/block επέφεραν χαμηλότερο utilization γύρω στο 50%, ενώ με 512 threads/block το utilization ήταν γύρω στο 70% με trade off περισσότερες προσβάσεις-συναλλαγές (access-transactions) στην κοινόχρηστη (shared)-global μνήμη και περιορισμό στον μέγιστο αριθμό ενεργών μπλοκ ανά πολυεπεξεργαστή (multiprocessor) άρα και χειρότερους χρόνους. Επιπροσθέτως, δεν χρησιμοποιήθηκε επικαλυπτόμενη αντιγραφή και εκτέλεση δεδομένων αλλά οι αντιγραφές της μνήμης και η εκτέλεση του πυρήνα πραγματοποιήθηκαν σε διαδοχικά στάδια καθώς επίσης δεν εντοπίστηκε register spilling στο profiling των πυρήνων (kernel). Σχετικά με την επιλογή του μεγέθους των μπλοκ, επαληθεύτηκε ότι μεταξύ 128 και 256 νήματα ανά μπλοκ είναι η καλύτερη επιλογή και μια καλή αρχή για πειραματισμό με διαφορετικά μεγέθη μπλοκ. Χρησιμοποιήθηκαν 3 έως 4 φορές μικρότερα μπλοκ αντί για ένα μεγάλο μπλοκ ανά πολυεπεξεργαστή επειδή, όπως φάνηκε και μέσα από το profiling, η καθυστέρηση (latency) επηρεάζει σημαντικά την απόδοση. Συμπεραίνουμε ότι η τεχνική αυτή είναι ιδιαίτερα ευεργετική για πυρήνες που καλούνε συχνά syncthreads(). Σχετικά με την βελτιστοποίηση της χρήσης arithmetic και την χρήση ενός νήματος να επεξεργάζεται πολλαπλά στοιχεία ενός πίνακα στην shared μνήμη, αποδείχτηκε ιδιαίτερα ευεργετική ακόμα και αν τα όρια, όπως τα νήματα ανά μπλοκ δεν είναι το βασικό ζήτημα. Αυτό συμβαίνει επειδή κάποιες λειτουργίες κοινές σε κάθε στοιχείο μπορεί να εκτελεστούν από το νήμα μια φορά, αποσβένοντας το κόστος επί του αριθμού των κοινών στοιχείων μνήμης που επεξεργάζονται από ένα νήμα. Επιπροσθέτως, η χρήση shift operations προς αποφυγή διαιρέσεων και υπολογισμούς modulo επιβάλλεται όποτε είναι δυνατόν όπως στα reductions κοινόχρηστης (shared) 84

86 μνήμης. Ειδικά για δύναμη του 2, το ( i n ) είναι ισοδύναμο με i >> log 2(n) και το modulo (i % n) είναι ίσο με (i & (n 1)). Επιπλέον, υψηλή προτεραιότητα δόθηκε και στην αποφυγή εκτέλεσης μέσα από divergent branches στο ίδιο στημόνι (warp) για καλύτερες επιδόσεις, με tradeoff τις περισσότερες προσβάσεις και συναλλαγές στην κοινόχρηστη (shared) μνήμη και περισσότερες αριθμητικές (arithmetic) πράξεις. Το optimization του μεταγλωττιστή (compiler) μπορεί να ξετυλίγει (unroll) τους βρόχους if ή switch statements χρησιμοποιώντας πρόβλεψη διακλάδωσης. Σε αυτές τις περιπτώσεις, κανένα στημόνι δεν μπορεί ποτέ να αποκλίνει. Για τις περιπτώσεις όπως φαίνεται στον παρακάτω κώδικα, όπου δεν μπορεί να εξαλειφθεί τελείως η απόκλιση των νημάτων, αποφεύχθηκε η χρήση syncthreads() στο εσωτερικό των αποκλίνων branch. for (unsigned int i = blockdim.x / 2; i > 0; i >>= 1){ // Branch Divergence - no penalty, idle threads if ((threadidx.x < i) && (cache_d_dist[threadidx.x] > cache_d_dist[threadidx.x + i])){ cache_d_dist [threadidx.x] = cache_d_dist [threadidx.x + i]; cache_d_pairs[threadidx.x] = cache_d_pairs[threadidx.x + i]; } syncthreads(); }... //write back to each element in d_dist[] and d_pairs[] the min value pair // no penalty if (threadidx.x == 0){ d_dist [0] = cache_d_dist [0]; d_pairs[0] = cache_d_pairs[0]; }... // no penalty if (threadidx.x == 0) block_count = 0; Ο εσωτερικός συγχρονισμός των thread με ενδεχομένως αποκλίνων κώδικα (π.χ., loop πάνω σε input array) μπορεί να προκαλέσει απρόβλεπτα λάθη. Όπως φαίνεται και παραπάνω, λήφθηκε μέριμνα ώστε να εξασφαλιστεί ότι όλα τα νήματα έχουν συγκλίνει στο σημείο όπου καλείται συγχρονισμός του μπλοκ (block barrier) με syncthreads(). Για παράδειγμα, στην τρίτη περίπτωση του παραπάνω κώδικα, δεν υπάρχει καμία απόκλιση. Το θέμα είναι, ότι τα νήματα δεν χάνουν κανένα κύκλο (εκτός από τη σύγκριση για το threadidx) ενώ δεν δημιουργείται κάποιο είδος Hazard (read-beforewrite κλπ) ακόμα και αν δεν αναθέταμε την εκτέλεση της εντολής block_count = 0 μόνο στο threadid.x = 0, επειδή υπάρχει μηχανισμός που αποτρέπει την σειριοποίηση των προσβάσεων εγγραφής (write access) στην ίδια διεύθυνση κοινόχρηστης (shared) μνήμης. Γενικά, ένα αίτημα από κάποιο στημόνι προς την κοινόχρηστη μνήμη δεν δημιουργεί bank conflicts μεταξύ των νημάτων που έχουν πρόσβαση σε οποιαδήποτε διεύθυνση μέσα στην ίδια λέξη μήκους 32-bit (παρόλο που οι δύο διευθύνσεις πέφτουν 85

87 στο ίδιο bank). Στην περίπτωση για προσβάσεις εγγραφής, κάθε διεύθυνση γράφεται από ένα μόνο νήμα (ποιο ακριβώς εκτελεί την εγγραφή είναι απροσδιόριστο). Ο γενικός κανόνας δεν είναι να αποφευχθεί πλήρως η διακλάδωση, αλλά να αποφευχθεί η απόκλιση εσωτερικά των στημονιών (warp). Τα προβλήματα απόδοσης προκύπτουν όταν υπάρχουν πολλά ανομοιόμορφα branch με ανεξάρτητη εκτέλεση από τα εκάστοτε νήματα μέσα στα στημόνια, όπου κάθε απόκλιση (branch) παίρνει πολλούς κύκλους για να ολοκληρωθεί. Εδώ, δεν παρουσιάζεται κανένα είδος απόκλισης αφού ο κλάδος έχει μόνο μία εναλλακτική λύση (όχι else statement), δεν χάνεται κύκλος (το if block έχει να εκτελέσει εντολές έτσι κι αλλιώς). Παρακάτω παρατίθεται κώδικας με divergent branch του kernel comparestrippoints.... //compare only if corresponding point of threadidx.x was in the strip if (tid_is_strip_point!= false){ <- Divergence //Compare each point in the strip, diff index if initial block or following same block for (uint i = (tid == sub_tid? threadidx.x + 1 : 0); i < blockdim.x; i++){ //check if point[sub_tid] is within vertical strip loc_dist = cache_d_s_y[i].x - Lx; Divergence -> if ( (loc_dist <= CONST_D_DIST) && (loc_dist >= (-CONST_D_DIST)) ){ loc_dist = sqrtf((cache_d_s_y[i].x - local_strip_point.x) * (cache_d_s_y[i].x - local_strip_point.x) + (cache_d_s_y[i].y - local_strip_point.y) * (cache_d_s_y[i].y - local_strip_point.y)); if (loc_dist < loc_min_dist){ loc_min_dist = loc_dist; loc_min_pair.x = local_strip_point.x; loc_min_pair.y = local_strip_point.y; loc_min_pair.z = cache_d_s_y[i].x; loc_min_pair.w = cache_d_s_y[i].y; } } } } syncthreads();... Στην συγκεκριμένη περίπτωση έχουμε 2 if-statements με branch divergence σχεδόν 37% (για UDS uniform distribution sample) των συνολικών εκτελέσεων. Εδώ θα μπορούσαμε να αποφύγουμε την 1 η απόκλιση με τον έλεγχο να πραγματοποιηθεί στο 3 ο if-statement αλλά το tradeoff είναι περισσότερες συναλλαγές με την κοινόχρηστη (shared) μνήμη με αποτέλεσμα ελαφρώς χειρότερους χρόνους εκτέλεσης. Το ίδιο ισχύει και για το 2 ο if-statement το οποίο είναι αναγκαίο για σημαντική μείωση των συνολικών συναλλαγών (transactions) μνήμης και περιττών αριθμητικών πράξεων (arithmetic operations). 86

88 Κεφάλαιο 4 Ανακεφαλαίωση Συμπεράσματα Όπως φαίνεται και από τις μετρήσεις στα κεφάλαια 3 και 4, η επιτάχυνση εξαρτάται σημαντικά από τον τύπο αλγορίθμου, με τον brute force να είναι πιο ιδανικός για παραλληλοποίηση χωρίς βέβαια να συνεπάγεται και καλύτερους χρόνους από τον αντίστοιχο Divide & Conquer. Επίσης είδαμε πως μπορεί η χρήση caching τεχνικών να βελτιώσει σημαντικά την επίδοση. Η συνολική απόδοση βασίστηκε στις στρατηγικές βελτιστοποίησης που περιστρέφονται γύρω από τρεις βασικές στρατηγικές: Μεγιστοποίηση παράλληλη εκτέλεση Η βελτιστοποίηση της χρήσης της μνήμης για να επιτευχθεί το μέγιστο εύρος ζώνης της μνήμης Βελτιστοποίηση της χρήσης εντολών για να επιτευχθεί το μέγιστο instruction throughput Η μεγιστοποίηση παράλληλης εκτέλεσης ξεκινά με δόμηση του αλγορίθμου με έναν τρόπο που εκθέτει όσο είναι δυνατόν παραλληλισμό δεδομένων. Μόλις εκτεθεί, πρέπει να αντιστοιχίζεται με το hardware όσο το δυνατόν αποτελεσματικότερα. Αυτό γίνεται με την προσεκτική επιλογή της διαμόρφωσης εκτέλεσης κάθε πυρήνα. Η εφαρμογή θα πρέπει επίσης να μεγιστοποιεί την παράλληλη εκτέλεση σε ένα υψηλότερο επίπεδο με την ρητή έκθεση ταυτόχρονης εκτέλεσης στη συσκευή μέσω των stream, καθώς και την μεγιστοποίηση της ταυτόχρονης εκτέλεσης μεταξύ του host και της συσκευής. H βελτιστοποίηση της χρήσης της μνήμης ξεκινά με την ελαχιστοποίηση της μεταφοράς δεδομένων μεταξύ του εξυπηρετητή (host CPU) και της συσκευής (GPU), διότι αυτές οι μεταφορές έχουν πολύ χαμηλότερο εύρος ζώνης από ό, τι η μεταφορά δεδομένων εσωτερικά της συσκευής. Η πρόσβαση των πυρήνων στην παγκόσμια (global) μνήμη, θα πρέπει να ελαχιστοποιηθεί με τη μεγιστοποίηση της κοινόχρηστης μνήμης στη συσκευή. Όπως και στην υλοποίηση της εργασίας, η καλύτερη βελτιστοποίηση θα μπορούσε ακόμη να αποφεύγει οποιαδήποτε μεταφορά δεδομένων με απλή εκ νέου χρήση των δεδομένων που βρίσκονται ήδη στην μνήμη. Το αποτελεσματικό εύρος ζώνης μπορεί να διαφέρει κατά μία τάξη μεγέθους, ανάλογα με το πρότυπο πρόσβασης για κάθε τύπο μνήμης. Το επόμενο βήμα στη βελτιστοποίηση της χρήσης της μνήμης είναι επομένως να οργανωθούν οι προσβάσεις μνήμης, σύμφωνα με τα βέλτιστα πρότυπα πρόσβασης. Αυτή η βελτιστοποίηση είναι ιδιαίτερα σημαντική για τις συναλλαγές με την global μνήμη, επειδή η λανθάνουσα 87

89 πρόσβαση κοστίζει εκατοντάδες κύκλους ρολογιού. Σε αντίθεση, οι προσπελάσεις σε κοινόχρηστη μνήμη, συνήθως, χρίζουν βελτιστοποίησης μόνο όταν υπάρχει υψηλός βαθμός σε bank conflicts. Όσο αφορά την βελτιστοποίηση της χρήσης εντολών, η χρήση των αριθμητικών εντολών που έχουν χαμηλό throughput πρέπει να αποφεύγεται. Αυτό υποδηλώνει την ανταλλαγή ακρίβειας για ταχύτητα όταν φυσικά δεν επηρεάζει το τελικό αποτέλεσμα. Επίσης σημαντικό, είναι και η σωστή χρήση control flow εντολών λόγω της SIMT αρχιτεκτονικής. Όσο αφορά την προσέγγιση με Divide & Conquer, διαπιστώθηκε η μεγάλη εξάρτηση της απόδοσης από την σωστή επιλογή και υλοποίηση του αλγόριθμου ταξινόμησης. Στην συγκεκριμένη περίπτωση, ο bitonic sort αλγόριθμος έδωσε αρκετά καλύτερα αποτελέσματα από τον odd-even mergesort. Θα μπορούσε, για μελλοντικές εργασίες να ελεγχθεί και με περεταίρω αλγορίθμους όπως ο απλός mergesort με εξειδικευμένο σχεδιασμό για καλύτερη απόδοση στις επιμέρους συγχωνεύσεις, ανά ζεύγος batch μήκους batchlength, του πίνακα S_y. Σημαντική παράμετρος στην απόδοση είναι το μέγεθος του προβλήματος αλλά και η επιλογή του μεγέθους των μπλοκ, δηλαδή των νημάτων ανά μπλοκ, του ποσού της κοινόχρηστης (shared) μνήμης και φυσικά η υπολογιστική δυνατότητα της συσκευής. Τα καλύτερα αποτελέσματα επιτυγχάνονται για μεγάλα μεγέθη πινάκων, παραδείγματος χάριν για Ν = 8Million (~ 64Mbytes) ή 16Million (~136Mbytes). Αυτό υπαγορεύει και την απαίτηση της κάρτας γραφικών για μαζική παράλληλη επεξεργασία κάτι που συνεπάγεται την ύπαρξη αφθονίας στοιχείων προς επεξεργασία. Πρέπει να αναφέρουμε ότι ο σχεδιασμός του αλγορίθμου δεν έγινε με καθαρά παράλληλη προσέγγιση, όπως φάνηκε και στα σημεία που είχαμε branch divergence τα οποία θα μπορούσαν ίσως να έχουν αποφευχθεί με μία καλύτερη σχεδίαση. Επίσης, το πιο σημαντικό εμπόδιο όσο αφορά την D&C υλοποίηση, πέρα της ταξινόμησης, έγκειται στην δυσκολία του εντοπισμού των σημείων που ανήκουν στην κάθετη λωρίδα καθώς και το γεγονός ότι τα σημεία εντοπίζονται σε τυχαίες θέσεις μνήμης προσθέτοντας σημαντική καθυστέρηση στις συναλλαγές με την παγκόσμια (global) μνήμη λόγω παράτυπων αναφορών μνήμης (Irregular memory reference). Αξίζει να αναφερθεί επίσης ότι, η έμμεση πρόσβαση μνήμης συσκευής από τους πυρήνες (kernels) με χρήση pointer δεν ενδείκνυται για τις GPU, προσθέτοντας ακόμα ένα περιορισμό στον σχεδιασμό του αλγορίθμου για εντοπισμό των σημείων της κάθετης λωρίδας. Η υλοποίηση που παρουσιάσαμε αφορά πράξεις απλής ακρίβειας (single precision). Ο ίδιος αλγόριθμος για διπλή ακρίβειας (double precision) μπορεί να αποτελέσει αντικείμενο μελλοντικής εργασίας καθώς οι σημερινές δυνατότητες double precision των καρτών γραφικών είναι σχετικά περιορισμένες. Επίσης, θα μπορούσε να υλοποιηθεί μια 88

90 πιο γενική έκδοση του closest pair για N-διαστάσεις με περαιτέρω έρευνα καθώς και να χρησιμοποιηθεί πιο αναλυτικός πυρήνας για τα reductions με completely unrolled loops, και περισσότερα στοιχεία ανά νήμα. Τέλος, για μελλοντικές εργασίες, η παραλληλοποίηση του αλγορίθμου θα μπορούσε να γίνει με αναδρομικές κλήσεις πυρήνων με την προϋπόθεση ότι υποστηρίζεται από την συσκευή (για GPU με υπολογιστική ικανότητα 2.0 και άνω). Επιπροσθέτως, η χρήση ενός υβριδικού αλγορίθμου που χρησιμοποιεί παράλληλα την GPU και την CPU θα μπορούσε να βελτιστοποιήσει τον αλγόριθμο με την προϋπόθεση ότι η κάθε μονάδα θα αναλαμβάνει το μέρος του προβλήματος στο οποίο είναι αποδοτικότερη. Ο αλγόριθμος θα μπορούσε να υλοποιηθεί με χρήση του opencl framework ή να συνδυαστεί με openmp ή ακόμα και με χρήση του MPI για clusters. 89

91 Παράρτημα Sorting networks Ένα δίκτυο ταξινόμησης είναι ένα αφηρημένο μαθηματικό μοντέλο ενός δικτύου με comparators που χρησιμοποιείται για να ταξινομήσει μια ακολουθία αριθμών. Κάθε συγκριτής (comparator) συνδέει δύο σύρματα και ταξινομεί τις τιμές εξάγοντας την μικρότερη τιμή προς ένα σύρμα, και την μεγαλύτερη προς άλλο. Η κύρια διαφορά μεταξύ των δικτύων ταξινόμησης με τους αλγόριθμους ταξινόμησης με σύγκριση είναι ότι σε ένα δίκτυο ταξινόμησης η ακολουθία των συγκρίσεων έχει οριστεί εκ των προτέρων, ανεξάρτητα από την έκβαση των προηγούμενων συγκρίσεων. Αυτή η ανεξαρτησία των αλληλουχιών σύγκρισης είναι χρήσιμη για την παράλληλη εκτέλεση των αλγορίθμων. Η θεωρία των δικτύων ταξινόμησης είναι αρκετά βαθιά και πολύπλοκη. Ορισμός: Έστω J = { 0,..., n 1 } είναι ένα σύνολο δεικτών και έστω A είναι ένα σύνολο με μια σχέση τάξης. Μία αλληλουχία δεδομένων είναι μια χαρτογράφηση α: J Α, δηλαδή μια ακολουθία μήκους n δεδομένων. Το σύνολο όλων των ακολουθιών δεδομένων μήκους n πάνω από το Α συμβολίζεται με A n. Ορισμός: Το πρόβλημα ταξινόμησης αποτελείται από αναδιάταξη αυθαίρετης ακολουθίας δεδομένων a 0,..., a n 1, a i Α σε μία ακολουθία δεδομένων a φ(0),..., a φ(n 1) τέτοια ώστε a φ(i) a φ(j) για i < j όπου φ είναι μια μετάθεση του συνόλου δεικτών J = { 0,..., n 1 }. Ο ορισμός αυτός ασχολείται μόνο με την απλούστερη περίπτωση ταξινόμησης μιας σειράς δεδομένων σε αύξουσα σειρά. Δίκτυο Συγκριτών (Comparators) Σχήμα 4.5 Ένα απλό sorting network που αποτελείται από τέσσερα καλώδια και πέντε υποδοχείς. Οι συγκριτές εισήχθησαν ανεπίσημα για την υπόθεση J = { 0,..., n 1 }. Ένας συγκριτής [i j] ταξινομεί το στοιχείο i και το στοιχείο j της ακολουθίας δεδομένων σε μη φθίνουσα σειρά. Τυπικά, ένας συγκριτής είναι μια χαρτογράφηση που εφαρμόζεται σε ακολουθία δεδομένων. Δίκτυο από συγκριτές 90

92 Σχήμα 4.6 Παράδειγμα συγκριτών Ορισμός: Ένας συγκριτής είναι μια χαρτογράφηση [i: j]: Α n A n, i, j {0,..., n 1} με [i j](α) i = min (a i, a j ) [i j](α) j = max(a i, a j ) [i j](α) k = a k για κάθε k με k i, k j για όλα τα α A n. Το Σχήμα 4.6 δείχνει την γραφική παράσταση ενός δικτύου συγκριτών με τους συγκριτές [1: 3] και [2: 1] να εφαρμόζονται στην ακολουθία δεδομένων 6, 8, 5, 1. Ορισμός: Στάδιο σύγκρισης S είναι μια σύνθεση από συγκριτές S = [i 1 j 1 ]... [i k : j k ], k N τέτοια ώστε όλα τα i r και j S να είναι ξένα (ακόμη και τα i r και j S είναι ξένα μεταξύ τους). Οι συγκριτές μέσα στο ίδιο στάδιο σύγκρισης μπορεί να εκτελεστούν παράλληλα. Σχήμα 4.7 Δίκτυο συγκριτών 2 σταδίων Ορισμός: Ένα δίκτυο συγκριτών είναι μια σύνθεση από στάδια σύγκρισης. Το Σχήμα 4.7 δείχνει ένα δίκτυο συγκριτών με δύο στάδια. Δίκτυο ταξινόμησης (Sorting Network) Ορισμός: Ένα δίκτυο ταξινόμησης είναι ένα δίκτυο σύγκρισης που ταξινομεί όλες τις ακολουθίες εισόδου. 91

93 Το δίκτυο του συγκριτή στο παραπάνω παράδειγμα δεν είναι δίκτυο ταξινόμησης, δεδομένου ότι δεν ταξινομεί την αλληλουχία 3, 1, 4, 2. Το πρόβλημα κατά πόσο ένα αυθαίρετο δίκτυο συγκριτών είναι δίκτυο ταξινόμησης ή όχι δεν είναι εύκολο να λυθεί σε γενικές γραμμές εφόσον πρόκειται για ένα NP-complete πρόβλημα. Πέρα από αυτό, τα δίκτυα ταξινόμησης μπορεί φυσικά να κατασκευαστούν συστηματικά και να αποδειχθούν ότι είναι σωστά. Η ταξινόμηση δικτύων είναι ειδική περίπτωση των γενικών αλγορίθμων ταξινόμησης, δεδομένου ότι όλες οι συγκρίσεις είναι ανεξάρτητες των δεδομένων. Η ταξινόμηση δικτύου bubblesort αποτελείται από μια πρώτη διαγώνιο n 1 συγκριτών, που μετακινούν το μεγαλύτερο στοιχείο στην τελευταία θέση. Τα εναπομείναντα n 1 στοιχεία ταξινομούνται αναδρομικά εφαρμόζοντας την ίδια διαδικασία. Η bubblesort αποτελείται από n(n 1) συγκρίσεις, τοποθετημένες σε 2n 3 στάδια. 2 Σχήμα 4.8 Δίκτυο συγκριτών 2 σταδίων Ωστόσο, λόγω της μεγάλης σταθερά της πολυπλοκότητας το δίκτυο είναι ανέφικτο, αλλά υπάρχουν πρακτικά δίκτυα ταξινόμησης με μια πολυπλοκότητα συγκριτών της τάξης O(n log (n) 2 ): Bitonic, Odd-even mergesort και Shellsort. Bitonic sort Η Bitonic Ταξινόμηση είναι ένα από τα ταχύτερα δίκτυα ταξινόμησης. Ένα δίκτυο ταξινόμησης είναι ένα ιδιαίτερο είδος των αλγόριθμων ταξινόμησης, όπου η ακολουθία των συγκρίσεων δεν είναι εξαρτώμενη από τα δεδομένα. Αυτό κάνει την ταξινόμηση των δικτύων κατάλληλη για την εφαρμογή σε hardware ή σε παράλληλες σειρές επεξεργαστών. Το bitonic δίκτυο ταξινόμησης αποτελείται από Θ(n log(n) 2 ) συγκριτές (comparators). Έχει την ίδια ασυμπτωτική πολυπλοκότητα με τον odd-even Mergesort και Shellsort. Αν και είναι ήδη γνωστό ένα δίκτυο ταξινόμησης με O(n log(n)) comparators, λόγω της 92

94 μεγάλης σταθεράς του overhead είναι βραδύτερο από ό, τι το bitonic sort για, πρακτικά, όλα τα μεγέθη προβλημάτων. Το ακόλουθο bitonic sort, έχει αναπτυχθεί με βάση την αρχή 0-1(αναφορά στο παράρτημα). Η αρχή 0-1 αναφέρει ότι ένα δίκτυο σύγκρισης που ταξινομεί κάθε ακολουθία από 0 και 1 είναι ένα δίκτυο ταξινόμησης, δηλαδή ταξινομεί κάθε ακολουθία από αυθαίρετες τιμές. Βασικές Αρχές Ορισμός: Μια ακολουθία a = a 0,..., a n 1 με a i { 0, 1 }, όπου i = 0,..., n 1 καλείται ακολουθία 0-1. Μια ακολουθία 0-1 καλείται διτονική, αν περιέχει το πολύ δύο αλλαγές μεταξύ του 0 και 1. Για παράδειγμα αν υπάρχει υπο-ακολουθία μήκους k, m { 1,..., n } τέτοια ώστε a 0,..., a k 1 = 0, a k,..., a m 1 = 1, a m,..., a n 1 = 0 ή a 0,..., a k 1 = 1, a k,..., a m 1 = 0, a m,..., a n 1 = 1 Στο ακόλουθο σχήμα, παρουσιάζονται διαφορετικά παραδείγματα από διτονικές ακολουθίες 0-1 με τα 0 να είναι άσπρα και τα 1 γκρίζα. Σχήμα 4.9 Δίκτυο συγκριτών Ορισμός: Έστω n N, n περιττός. Το δίκτυο συγκριτών B n ορίζεται ως εξής: Β n = [ 0 n ] [ 1 n + 1 ] [ n 1 n 1 ] (Διπλανό σχήμα) Σχήμα

95 Θεώρημα: Έστω a = a 0,..., a n 1 είναι μια διτονική αλληλουχία 0-1, όπου n N, n περιττός. Η εφαρμογή του δικτύου σύγκρισης B n στο α συνεπάγεται: Β n (α) = b 0,..., bn 2 1 c 0,..., cn 2 1 όπου όλα τα b i είναι μικρότερα ή ίσα με όλα τα c j, δηλαδή b i c j για όλα τα i, j { 0,..., n 1 } και επιπλέον 2 b 0,..., bn 2 1 είναι bitonic και c 0,..., c n 1 είναι bitonic. 2 Απόδειξη: Έστω a = a 0,..., a n_1 είναι μια διτονική αλληλουχία 0-1. Μια αναπαράσταση σε δύο σειρές φαίνεται στο παρακάτω σχήμα. Η αλληλουχία αρχίζει με μηδενικά, συνεχίζει με άσσους, και τελειώνει με μηδενικά (πρώτη περίπτωση) ή ξεκινά με άσσους, συνεχίζει με μηδενικά, και τελειώνει με άσσους (δεύτερη περίπτωση). Οι περιφέρειες των άσσων μπορεί να επικαλύπτονται ή όχι. Αρκετές άλλες παραλλαγές είναι δυνατές. Η εφαρμογή του δικτύου σύγκρισης B n αντιστοιχεί σε μια σύγκριση μεταξύ της άνω και κάτω γραμμής. Σε κάθε περίπτωση, επιτυγχάνεται το θεωρητικό αποτέλεσμα: όλα τα b i είναι μικρότερα ή ίσα απ όλα τα c j και τα b και c είναι bitonic. Σχήμα 4.11 Διτονικές ακολουθίες 0-1 και εφαρμογή του B n 94

96 Διτονικό Δίκτυο Ταξινόμησης (Bitonic sorting Network) Τα δομικά μπλοκ του Bitonic Sort δικτύου είναι τα δίκτυα συγκριτών B k με διαφορετικά k, όπου k είναι μια δύναμη του 2. Με τη στρατηγική του διαίρει και βασίλευε, σχηματίζουμε τα δίκτυα Bitonic Merge και Bitonic Sort. Πρώτα δημιουργούμε το δίκτυο συγκριτών Bitonic Merge που θα ταξινομεί μία bitonic ακολουθία. Λόγω του θεωρήματος, το δίκτυο σύγκρισης Β n παράγει δύο bitonic υποαλληλουχίες, όπου όλα τα στοιχεία της πρώτης είναι μικρότερα ή ίσα από εκείνες της δεύτερης. Ως εκ τούτου, το BitonicMerge μπορεί να δημιουργηθεί αναδρομικά όπως φαίνεται στο παρακάτω σχήμα. Σχήμα 4.12 BitonicMerge(n) Η bitonic αλληλουχία είναι απαραίτητη ως είσοδος για το Bitonic Merge και αποτελείται από δύο ταξινομημένες υποαλληλουχίες, όπου η πρώτη είναι σε αύξουσα σειρά και η δεύτερη σε φθίνουσα σειρά. Οι υποαλληλουχίες είναι επίσης ταξινομημένες με αναδρομική εφαρμογή της Bitonic Sort (Σχήμα 4.11). Σχήμα 4.13 BitonicSort(n) 95

97 Odd-Even Mergesort Ο αλγόριθμος ταξινόμησης με συγχώνευση βασίζεται σε έναν αλγόριθμο που συγχωνεύει δύο ταξινομημένα μισά μιας ακολουθίας σε μια εντελώς ταξινομημένη ακολουθία. Σε αντίθεση με την mergesort, ο αλγόριθμος αυτός δεν είναι εξαρτώμενος από τα δεδομένα, δηλαδή οι ίδιες συγκρίσεις πραγματοποιούνται ανεξάρτητα από τα πραγματικά δεδομένα. Ως εκ τούτου, ο odd-even mergesort μπορεί να υλοποιηθεί ως ένα δίκτυο ταξινόμησης. Αλγόριθμος Συγχώνευσης Ο παρακάτω αλγόριθμος συνδυάζει μια ακολουθία των οποίων τα δύο μισά ταξινομούνται σε μια ταξινομημένη ακολουθία. Αλγόριθμος Odd-Even Merge(n) Είσοδος: Έξοδος: Μέθοδος: Μια ακολουθία a 0,..., a n 1 μήκους n > 1, του οποίου τα δύο μισά a 0,..., a n 2 1 και α n 2,..., a n 1 ταξινομούνται και n είναι σε δύναμη του 2. Η ταξινομημένη ακολουθία If n > 2, then 1. Εφαρμόζουμε αναδρομικά τον odd even merge ( n ) στην άρτια 2 ακολουθία a 0, a 2,, a n 2 και στην περιττή ακολουθία α 1, a 3,, a n Συγκρίνουμε [α i a i+1 ] για όλα τα στοιχείου i {1, 3, 5, 7,..., n 3} else συγκρίνουμε τα [a 0 α 1 ]. Ορθότητα Η ορθότητα του αλγορίθμου συγχώνευσης αποδεικνύεται με επαγωγή και την 0-1-αρχή. Ας υποθέσουμε ότι η αλληλουχία n = 2 1 είναι ταξινομημένη από τη σύγκριση [a 0 α 1 ]. Έστω λοιπόν n = 2 k, k > 1 και ας υποθέσουμε ότι ο αλγόριθμος είναι σωστός για όλα τα μικρότερα k (επαγωγική υπόθεση). Υποθέτουμε την ακολουθία 0-1a = a 0,..., a n 1 η οποία είναι διατεταγμένη σε σειρές ενός πίνακα με δύο στήλες. Η αντίστοιχη χαρτογράφηση των θέσεων δείκτη απεικονίζεται στο σχήμα 4.12a, εδώ για n = 16. Τότε το σχήμα 4.12b δείχνει μια πιθανή κατάσταση της αλληλουχίας 0-1. Καθένα από τα δύο μισά της είναι ταξινομημένο ξεκινά με κάποια μηδέν (λευκό) και τελειώνει με κάποια 1 (γκρι). 96

98 Σχήμα 4.14 Περιπτώσεις κατά την εκτέλεση της odd-even συγχώνευσης Στην αριστερή στήλη βρίσκεται η άρτια ακολουθία, δηλαδή, όλες οι a i με το i άρτιο, δηλαδή a 0, α 2, α 4 κλπ. Στη δεξιά στήλη βρίσκεται η περιττή ακολουθία, δηλαδή όλα τα a i με το i περιττό, ήτοι α 1, α 3, α 5 κλπ. Ακριβώς όπως και η αρχική αλληλουχία η άρτια και η περιττή υποαλληλουχία αποτελείται από δύο ταξινομημένα μισά. Με την επαγωγική υπόθεση, η αριστερά και η δεξιά στήλη κατατάσσονται σύμφωνα με αναδρομική εφαρμογή της odd even merge( n 2 ) στο 1ο βήμα του αλγορίθμου. Η δεξιά στήλη μπορεί να έχει το πολύ δύο 1 περισσότερους από την αριστερή στήλη (Σχήμα 4.12c). Μετά την εκτέλεση των συγκρίσεων του 2 ου βήματος του αλγορίθμου (Σχήμα 4.12d), σε κάθε περίπτωση, ο πίνακας είναι ταξινομημένος (Σχήμα 4.12e). Έστω T(n) είναι ο αριθμός των συγκρίσεων που εκτελούνται από την odd even merge(n). Στη συνέχεια έχουμε για n > 2. T(n) = 2 Τ ( n 2 ) + n 2 1. Με T(2) = 1 έχουμε T(n) = n (log(n) 1) + 1 δηλαδή O(n log(n)). 2 Δίκτυο Ταξινόμησης (Odd-even mergesort Network) Με την αναδρομική εφαρμογή του αλγορίθμου συγχώνευσης σχηματίζεται ο αλγόριθμος odd-even Mergesort. Είσοδος: Μια ακολουθία a 0,..., a n 1 με n να είναι σε δύναμη του 2. Έξοδος: Μέθοδος: Η ταξινομημένη ακολουθία If n > 1, then 97

99 1. Εφαρμόζουμε αναδρομικά τον odd even merge ( n ) στα δύο μισά 2 της αλληλουχίας, δηλαδή a 0, a 2,, an 2 1 και αn,, a n odd even merge(n); Σχήμα 4.15 Odd-even mergesort για n = 8 Ο αριθμός των συγκριτικών για τον odd-even Mergesort είναι O(n log(n) 2 ). Θεώρημα: 0-1 αρχή Ένα απαραίτητο εργαλείο για την απόδειξη της ορθότητας της ταξινόμησης των δικτύων είναι το 0-1 αρχή. Η 0-1 αρχή αναφέρει τα ακόλουθα: Θεώρημα: (0-1 αρχή) Εάν ένα δίκτυο ταξινόμησης ταξινομεί κάθε ακολουθία από 0 και 1, τότε αυτό ταξινομεί κάθε αυθαίρετη ακολουθία τιμών. Η απόδειξη της 0-1 αρχής δεν είναι πολύ δύσκολη. Ωστόσο, είναι πολύ χρήσιμο να έχουμε κάποιους ορισμούς και λήμματα έτοιμα. Προκαταρκτικά Ορισμός: Έστω A και B ταξινομημένα σύνολα. Μια απεικόνιση f A B καλείται μονότονη αν όλα τα a 1, a 2 A και a 1 a 2 f (a 1 ) f (a 2 ) Λήμμα: Έστω f A B είναι μονότονη απεικόνιση. Τότε το ακόλουθο ισχύει για όλα τα a 1, a 2 A : f (min(a 1, a 2 )) = min (f (a 1 ), f (a 2 )) 98

Παράλληλος προγραμματισμός σε επεξεργαστές γραφικών

Παράλληλος προγραμματισμός σε επεξεργαστές γραφικών Παράλληλος προγραμματισμός σε επεξεργαστές γραφικών Συστήματα Παράλληλης Επεξεργασίας 9ο εξάμηνο, ΣΗΜΜΥ Εργαστήριο Υπολογιστικών Συστημάτων (CSLab) Νοέμβριος 2010 Περιεχόμενα...1 Σύντομη Ιστορική Αναδρομή...2

Διαβάστε περισσότερα

Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών

Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών Συστήματα Παράλληλης Επεξεργασίας 9ο εξάμηνο, ΣΗΜΜΥ Εργαστήριο Υπολογιστικών Συστημάτων (CSLab) Δεκέμβριος 2015 Περιεχόμενα 2 01 / 2014 Προγραμματισμός

Διαβάστε περισσότερα

Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών

Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών Συστήματα Παράλληλης Επεξεργασίας 9ο εξάμηνο, ΣΗΜΜΥ Εργαστήριο Υπολογιστικών Συστημάτων (CSLab) Φεβρουάριος 2014 Περιεχόμενα 1 Εισαγωγή 2 Επεξεργαστές

Διαβάστε περισσότερα

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

Πανεπιστήμιο Θεσσαλίας Τμήμα Μηχανικών Η/Υ, Τηλεπικοινωνιών και Δικτύων Πανεπιστήμιο Θεσσαλίας Τμήμα Μηχανικών Η/Υ, Τηλεπικοινωνιών και Δικτύων Οργάνωση Η/Υ Ενότητα 1η: Εισαγωγή στην Οργάνωση Η/Υ Άσκηση 1: Αναλύστε τη διαδοχική εκτέλεση των παρακάτω εντολών MIPS με βάση τις

Διαβάστε περισσότερα

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

Οργάνωση επεξεργαστή (2 ο μέρος) ΜΥΥ-106 Εισαγωγή στους Η/Υ και στην Πληροφορική Οργάνωση επεξεργαστή (2 ο μέρος) ΜΥΥ-106 Εισαγωγή στους Η/Υ και στην Πληροφορική Ταχύτητα εκτέλεσης Χρόνος εκτέλεσης = (αριθμός εντολών που εκτελούνται) Τί έχει σημασία: Χ (χρόνος εκτέλεσης εντολής) Αριθμός

Διαβάστε περισσότερα

Το ολοκληρωμένο κύκλωμα μιας ΚΜΕ. «Φέτα» ημιαγωγών (wafer) από τη διαδικασία παραγωγής ΚΜΕ

Το ολοκληρωμένο κύκλωμα μιας ΚΜΕ. «Φέτα» ημιαγωγών (wafer) από τη διαδικασία παραγωγής ΚΜΕ Το ολοκληρωμένο κύκλωμα μιας ΚΜΕ Η Κεντρική Μονάδα Επεξεργασίας (Central Processing Unit -CPU) ή απλούστερα επεξεργαστής αποτελεί το μέρος του υλικού που εκτελεί τις εντολές ενός προγράμματος υπολογιστή

Διαβάστε περισσότερα

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

ΕΙΔΙΚΟΤΗΤΑ: ΤΕΧΝΙΚΟΣ ΕΦΑΡΜΟΓΩΝ ΠΛΗΡΟΦΟΡΙΚΗΣ ΜΑΘΗΜΑ: ΕΙΣΑΓΩΓΗ ΣΤΗΝ ΠΛΗΡΟΦΟΡΙΚΗ ΕΙΔΙΚΟΤΗΤΑ: ΤΕΧΝΙΚΟΣ ΕΦΑΡΜΟΓΩΝ ΠΛΗΡΟΦΟΡΙΚΗΣ ΜΑΘΗΜΑ: ΕΙΣΑΓΩΓΗ ΣΤΗΝ ΠΛΗΡΟΦΟΡΙΚΗ (Τμήματα Υπολογιστή) ΕΚΠΑΙΔΕΥΤΗΣ:ΠΟΖΟΥΚΙΔΗΣ ΚΩΝΣΤΑΝΤΙΝΟΣ ΤΜΗΜΑΤΑ ΗΛΕΚΤΡΟΝΙΚΟΥ ΥΠΟΛΟΓΙΣΤΗ Κάθε ηλεκτρονικός υπολογιστής αποτελείται

Διαβάστε περισσότερα

CUDA Compute Unified Device Architecture

CUDA Compute Unified Device Architecture CUDA Compute Unified Device Architecture Καλέρης Κωνσταντίνος Πεµπτοετής φοιτητής του τµήµατος Ηλεκτρολόγων Μηχανικών & Τεχνολογίας Η/Υ του Πανεπιστηµίου Πατρών ee5972@upnet.gr Καλλέργης Γεώργιος Πεµπτοετής

Διαβάστε περισσότερα

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

Συστήματα μνήμης και υποστήριξη μεταφραστή για MPSoC Συστήματα μνήμης και υποστήριξη μεταφραστή για MPSoC Πλεονεκτήματα MPSoC Είναι ευκολότερο να σχεδιαστούν πολλαπλοί πυρήνες επεξεργαστών από τον σχεδιασμό ενός ισχυρότερου και πολύ πιο σύνθετου μονού επεξεργαστή.

Διαβάστε περισσότερα

Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών

Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών Συστήματα Παράλληλης Επεξεργασίας 9ο εξάμηνο, ΣΗΜΜΥ Εργαστήριο Υπολογιστικών Συστημάτων (CSLab) Νοέμβριος 2017 1 Εισαγωγικά 2 Compute Unified Device

Διαβάστε περισσότερα

Μηχανοτρονική. Τμήμα Μηχανικών Παραγωγής και Διοίκησης 7 ο Εξάμηνο,

Μηχανοτρονική. Τμήμα Μηχανικών Παραγωγής και Διοίκησης 7 ο Εξάμηνο, Τμήμα Μηχανικών Παραγωγής και Διοίκησης 7 ο Εξάμηνο, 2016-2017 ΜΙΚΡΟΕΠΕΞΕΡΓΑΣΤΕΣ Μικροϋπολογιστής Υπολογιστής που χρησιμοποιείται για την είσοδο, επεξεργασία και έξοδο πληροφοριών. Είδη μικροϋπολογιστών:

Διαβάστε περισσότερα

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

Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών Απόδοση ΚΜΕ. (Μέτρηση και τεχνικές βελτίωσης απόδοσης) Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών 2016-17 Απόδοση ΚΜΕ (Μέτρηση και τεχνικές βελτίωσης απόδοσης) http://mixstef.github.io/courses/comparch/ Μ.Στεφανιδάκης Κεντρική Μονάδα Επεξεργασίας

Διαβάστε περισσότερα

All Pairs Shortest Path

All Pairs Shortest Path All Pairs Shortest Path χρησιμοποιώντας Κυπριώτη Αικατερίνη 6960 Μόσχογλου Στυλιανός 6978 20 Ιανουαρίου 2012 Περιεχόμενα 1 Πρόλογος 3 2 Ο σειριακός APSP 3 3 Η παραλληλοποίηση με 5 3.1 Το προγραμματιστικό

Διαβάστε περισσότερα

Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών

Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών Παράλληλος Προγραμματισμός σε Επεξεργαστές Γραφικών Συστήματα Παράλληλης Επεξεργασίας 9ο εξάμηνο, ΣΗΜΜΥ Εργαστήριο Υπολογιστικών Συστημάτων (CSLab) Νοέμβριος 2016 1 Εισαγωγικά 2 Compute Unified Device

Διαβάστε περισσότερα

Προγραμματισμός Συστημάτων Υψηλών Επιδόσεων (ΗΥ421) Εργασία Εξαμήνου

Προγραμματισμός Συστημάτων Υψηλών Επιδόσεων (ΗΥ421) Εργασία Εξαμήνου Προγραμματισμός Συστημάτων Υψηλών Επιδόσεων (ΗΥ421) Εργασία Εξαμήνου ΟΜΑΔΑ: Ιωαννίδης Σταύρος ΑΕΜ: 755 Ντελής Γιώργος ΑΕΜ: 726 Επιλογή της Εργασίας Για την εργασία μας επιλέξαμε την βελτιστοποίηση της

Διαβάστε περισσότερα

Κεντρική Μονάδα Επεξεργασίας

Κεντρική Μονάδα Επεξεργασίας Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών 2016-17 Κεντρική Μονάδα Επεξεργασίας (Σχεδιασμός και λειτουργία μιας απλής ΚΜΕ) http://mixstef.github.io/courses/comparch/ Μ.Στεφανιδάκης

Διαβάστε περισσότερα

Παράλληλος Προγραμματισμός με OpenCL

Παράλληλος Προγραμματισμός με OpenCL Παράλληλος Προγραμματισμός με OpenCL Συστήματα Παράλληλης Επεξεργασίας 9ο εξάμηνο, ΣΗΜΜΥ Εργαστήριο Υπολογιστικών Συστημάτων (CSLab) Δεκέμβριος 2017 1 Γενικά για OpenCL 2 Platform Model 3 Execution Model

Διαβάστε περισσότερα

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

Αρχιτεκτονική υπολογιστών 1 Ελληνική Δημοκρατία Τεχνολογικό Εκπαιδευτικό Ίδρυμα Ηπείρου Αρχιτεκτονική υπολογιστών Ενότητα 11-12 : Δομή και Λειτουργία της CPU Ευάγγελος Καρβούνης Παρασκευή, 22/01/2016 2 Οργάνωση της CPU Η CPU πρέπει:

Διαβάστε περισσότερα

Δομημένος Προγραμματισμός (ΤΛ1006)

Δομημένος Προγραμματισμός (ΤΛ1006) Τεχνολογικό Εκπαιδευτικό Ίδρυμα Κρήτης Σχολή Εφαρμοσμένων Επιστημών Τμήμα Ηλεκτρονικών Μηχανικών Τομέας Αυτοματισμού και Πληροφορικής Δομημένος Προγραμματισμός (ΤΛ1006) Δρ. Μηχ. Νικόλαος Πετράκης, Καθηγητής

Διαβάστε περισσότερα

; Γιατί είναι ταχύτερη η λήψη και αποκωδικοποίηση των εντολών σταθερού μήκους;

; Γιατί είναι ταχύτερη η λήψη και αποκωδικοποίηση των εντολών σταθερού μήκους; Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών 2015-16 Αρχιτεκτονικές Συνόλου Εντολών (ΙΙ) (Δομή Εντολών και Παραδείγματα) http://di.ionio.gr/~mistral/tp/comparch/ Μ.Στεφανιδάκης Αρχιτεκτονική

Διαβάστε περισσότερα

ΑΣΚΗΣΗ 6: ΔΕΙΚΤΕΣ. Σκοπός της Άσκησης. 1. Εισαγωγικά στοιχεία για τους Δείκτες

ΑΣΚΗΣΗ 6: ΔΕΙΚΤΕΣ. Σκοπός της Άσκησης. 1. Εισαγωγικά στοιχεία για τους Δείκτες Σκοπός της Άσκησης ΑΣΚΗΣΗ 6: ΔΕΙΚΤΕΣ Ο σκοπός αυτής της εργαστηριακής άσκησης είναι η εξοικείωση με τη χρήση των δεικτών (pointers). Οι δείκτες δίνουν την δυνατότητα σε προγράμματα να προσομοιώνουν τη

Διαβάστε περισσότερα

3 ο Εργαστήριο Μεταβλητές, Τελεστές

3 ο Εργαστήριο Μεταβλητές, Τελεστές 3 ο Εργαστήριο Μεταβλητές, Τελεστές Μια μεταβλητή έχει ένα όνομα και ουσιαστικά είναι ένας δείκτης σε μια συγκεκριμένη θέση στη μνήμη του υπολογιστή. Στη θέση μνήμης στην οποία δείχνει μια μεταβλητή αποθηκεύονται

Διαβάστε περισσότερα

Είναι το «μυαλό» του υπολογιστή μας. Αυτός κάνει όλους τους υπολογισμούς και τις πράξεις. Έχει δική του ενσωματωμένη μνήμη, τη λεγόμενη κρυφή

Είναι το «μυαλό» του υπολογιστή μας. Αυτός κάνει όλους τους υπολογισμούς και τις πράξεις. Έχει δική του ενσωματωμένη μνήμη, τη λεγόμενη κρυφή 1 Είναι το «μυαλό» του υπολογιστή μας. Αυτός κάνει όλους τους υπολογισμούς και τις πράξεις. Έχει δική του ενσωματωμένη μνήμη, τη λεγόμενη κρυφή μνήμη(cache). Η cache είναι πολύ σημαντική, πολύ γρήγορη,

Διαβάστε περισσότερα

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

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΣΧΟΛΗ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΤΟΜΕΑΣ ΤΕΧΝΟΛΟΓΙΑΣ ΠΛΗΡΟΦΟΡΙΚΗΣ ΚΑΙ ΥΠΟΛΟΓΙΣΤΩΝ ΕΡΓΑΣΤΗΡΙΟ ΥΠΟΛΟΓΙΣΤΙΚΩΝ ΣΥΣΤΗΜΑΤΩΝ www.cslab.ece.ntua.gr Διπλωματικές

Διαβάστε περισσότερα

ΕΘΝΙΚΟ ΚΑΙ ΚΑΠΟΔΙΣΤΡΙΑΚΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΑΘΗΝΩΝ

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

Διαβάστε περισσότερα

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

i Throughput: Ο ρυθμός ολοκλήρωσης έργου σε συγκεκριμένο χρόνο Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών 6-7 Απόδοση ΚΜΕ (Μέτρηση και τεχνικές βελτίωσης απόδοσης) http://mixstef.github.io/courses/comparch/ Μ.Στεφανιδάκης Κεντρική Μονάδα Επεξεργασίας

Διαβάστε περισσότερα

Αρχιτεκτονική Μνήμης

Αρχιτεκτονική Μνήμης ΕΣ 08 Επεξεργαστές Ψηφιακών Σημάτων Αρχιτεκτονική Μνήμης Τμήμα Επιστήμη και Τεχνολογίας Τηλεπικοινωνιών Πανεπιστήμιο Πελοποννήσου Βιβλιογραφία Ενότητας Kuo [2005]: Chapters 3 & 4 Lapsley [2002]: Chapter

Διαβάστε περισσότερα

Π Τ Υ Χ Ι Α Κ Η / Δ Ι Π Λ Ω Μ ΑΤ Ι Κ Η Ε Ρ ΓΑ Σ Ι Α

Π Τ Υ Χ Ι Α Κ Η / Δ Ι Π Λ Ω Μ ΑΤ Ι Κ Η Ε Ρ ΓΑ Σ Ι Α Αριστοτέλειο Πανεπιστήμιο Θεσσαλονίκης Σχολή Θετικών Επιστημών Τμήμα Πληροφορικής Π Τ Υ Χ Ι Α Κ Η / Δ Ι Π Λ Ω Μ ΑΤ Ι Κ Η Ε Ρ ΓΑ Σ Ι Α Αυτόνομη ρύθμιση προγραμμάτων που χρησιμοποιούν GPU Δοδόπουλος Ρωμανός

Διαβάστε περισσότερα

Παράλληλα Συστήματα. Γιώργος Δημητρίου. Ενότητα 3 η : Παράλληλη Επεξεργασία. Πανεπιστήμιο Θεσσαλίας - Τμήμα Πληροφορικής

Παράλληλα Συστήματα. Γιώργος Δημητρίου. Ενότητα 3 η : Παράλληλη Επεξεργασία. Πανεπιστήμιο Θεσσαλίας - Τμήμα Πληροφορικής Γιώργος Δημητρίου Ενότητα 3 η : Παράλληλη Επεξεργασία Παράλληλες Αρχιτεκτονικές Παράλληλο σύστημα είναι ένα σύνολο από επεξεργαστικά στοιχεία (processing elements) τα οποία: συνεργάζονται για γρήγορη επίλυση

Διαβάστε περισσότερα

1. Οργάνωση της CPU 2. Εκτέλεση εντολών 3. Παραλληλία στο επίπεδο των εντολών 4. Γραμμές διοχέτευσης 5. Παραλληλία στο επίπεδο των επεξεργαστών

1. Οργάνωση της CPU 2. Εκτέλεση εντολών 3. Παραλληλία στο επίπεδο των εντολών 4. Γραμμές διοχέτευσης 5. Παραλληλία στο επίπεδο των επεξεργαστών ΑΡΧΙΤΕΚΤΟΝΙΚΗ ΥΠΟΛΟΓΙΣΤΩΝ ΟΡΓΑΝΩΣΗ ΥΠΟΛΟΓΙΣΤΙΚΩΝ ΣΥΣΤΗΜΑΤΩΝ Ι Γ. Τσιατούχας 2 ο Κεφάλαιο ιάρθρωση 1. Οργάνωση της 2. εντολών 3. Παραλληλία στο επίπεδο των εντολών 4. Γραμμές διοχέτευσης 5. Παραλληλία στο

Διαβάστε περισσότερα

Προγραμματισμός Η/Υ (ΤΛ2007 )

Προγραμματισμός Η/Υ (ΤΛ2007 ) Τμήμα Ηλεκτρονικών Μηχανικών Τ.Ε.Ι. Κρήτης Προγραμματισμός Η/Υ (ΤΛ2007 ) Δρ. Μηχ. Νικόλαος Πετράκης (npet@chania.teicrete.gr) Ιστοσελίδα Μαθήματος: https://eclass.chania.teicrete.gr/ Εξάμηνο: Εαρινό 2015-16

Διαβάστε περισσότερα

Παραλληλισμός σε επίπεδο εντολών

Παραλληλισμός σε επίπεδο εντολών Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών 2015-16 Παραλληλισμός σε επίπεδο εντολών (Pipelining και άλλες τεχνικές αύξησης απόδοσης) http://di.ionio.gr/~mistral/tp/comparch/ Μ.Στεφανιδάκης

Διαβάστε περισσότερα

Αρχιτεκτονική Επεξεργαστών Ψ.Ε.Σ

Αρχιτεκτονική Επεξεργαστών Ψ.Ε.Σ ΕΣ 08 Επεξεργαστές Ψηφιακών Σηµάτων Αρχιτεκτονική Επεξεργαστών Ψ.Ε.Σ Βιβλιογραφία Ενότητας Kehtarnavaz [2005]: Chapter 3 Kuo [2005]: Chapters 1 & 4-5 Lapsley [2002]: Chapter 4 Hayes [2000]: Κεφάλαιo 8

Διαβάστε περισσότερα

Κεντρική Μονάδα Επεξεργασίας. Επανάληψη: Απόδοση ΚΜΕ. ΚΜΕ ενός κύκλου (single-cycle) Παραλληλισμός σε επίπεδο εντολών. Υπολογιστικό σύστημα

Κεντρική Μονάδα Επεξεργασίας. Επανάληψη: Απόδοση ΚΜΕ. ΚΜΕ ενός κύκλου (single-cycle) Παραλληλισμός σε επίπεδο εντολών. Υπολογιστικό σύστημα Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών 2016-17 Παραλληλισμός σε επίπεδο εντολών (Pipelining και άλλες τεχνικές αύξησης απόδοσης) http://mixstef.github.io/courses/comparch/ Μ.Στεφανιδάκης

Διαβάστε περισσότερα

Εικονική Μνήμη (Virtual Μemory)

Εικονική Μνήμη (Virtual Μemory) ΗΥ 431 Αρχιτεκτονική Παραλλήλων Συστημάτων Διάλεξη 16 Εικονική Μνήμη (Virtual Μemory) Νίκος Μπέλλας Τμήμα Ηλεκτρολόγων Μηχανικών και Μηχανικών Η/Υ Απλό πείραμα int *data = malloc((1

Διαβάστε περισσότερα

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

Προβλήματα, αλγόριθμοι, ψευδοκώδικας Προβλήματα, αλγόριθμοι, ψευδοκώδικας October 11, 2011 Στο μάθημα Αλγοριθμική και Δομές Δεδομένων θα ασχοληθούμε με ένα μέρος της διαδικασίας επίλυσης υπολογιστικών προβλημάτων. Συγκεκριμένα θα δούμε τι

Διαβάστε περισσότερα

Παράλληλη Επεξεργασία

Παράλληλη Επεξεργασία Παράλληλη Επεξεργασία Φροντιστήριο: Εισαγωγή στα Πολυεπεξεργαστικά Συστήματα Διερασίες και Νήματα σε Πολυεπεξεργαστικά Συστήματα Εργαστήριο Πληροφοριακών Συστημάτων Υψηλής Επίδοσης Parallel and Distributed

Διαβάστε περισσότερα

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

Θέματα Μεταγλωττιστών Γιώργος Δημητρίου Ενότητα 10 η : Βελτιστοποιήσεις Τοπικότητας και Παραλληλισμού: Εξαρτήσεις και Μετασχηματισμοί Βρόχων Επεξεργασία Πινάκων Παραλληλισμός επιπέδου βρόχου Λόγω παραλληλισμού δεδομένων Επιτυγχάνεται

Διαβάστε περισσότερα

Διεργασίες (μοντέλο μνήμης & εκτέλεσης) Προγραμματισμός II 1

Διεργασίες (μοντέλο μνήμης & εκτέλεσης) Προγραμματισμός II 1 Διεργασίες (μοντέλο μνήμης & εκτέλεσης) Προγραμματισμός II 1 lalis@inf.uth.gr Ο κώδικας δεν εκτελείται «μόνος του» Ο εκτελέσιμος κώδικας αποθηκεύεται σε ένα αρχείο Το αρχείο είναι μια «παθητική» οντότητα

Διαβάστε περισσότερα

ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2015

ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2015 ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2015 ΤΕΧΝΟΛΟΓΙΑ (Ι) ΤΕΧΝΙΚΩΝ ΣΧΟΛΩΝ ΘΕΩΡΗΤΙΚΗΣ ΚΑΤΕΥΘΥΝΣΗΣ Μάθημα : Μικροϋπολογιστές

Διαβάστε περισσότερα

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

Αρχιτεκτονική υπολογιστών 1 Ελληνική Δημοκρατία Τεχνολογικό Εκπαιδευτικό Ίδρυμα Ηπείρου Αρχιτεκτονική υπολογιστών Ενότητα 9 : Ομάδες Εντολών: Ιδιότητες και Λειτουργίες Ευάγγελος Καρβούνης Παρασκευή, 15/01/2016 Τι είναι ομάδα εντολών;

Διαβάστε περισσότερα

ΚΕΦΑΛΑΙΟ 2: Χειρισµός εδοµένων

ΚΕΦΑΛΑΙΟ 2: Χειρισµός εδοµένων ΚΕΦΑΛΑΙΟ 2: Χειρισµός εδοµένων 2.1 Αρχιτεκτονική Υπολογιστών 2.1 Αρχιτεκτονική Υπολογιστών 2.2 Γλώσσα Μηχανής 2.3 Εκτέλεση προγράµµατος 2.4 Αριθµητικές και λογικές εντολές 2.5 Επικοινωνία µε άλλες συσκευές

Διαβάστε περισσότερα

Ερωτήσεις πολλαπλής επιλογής - Κεφάλαιο 2. Α1. Ο αλγόριθμος είναι απαραίτητος μόνο για την επίλυση προβλημάτων πληροφορικής

Ερωτήσεις πολλαπλής επιλογής - Κεφάλαιο 2. Α1. Ο αλγόριθμος είναι απαραίτητος μόνο για την επίλυση προβλημάτων πληροφορικής Ερωτήσεις πολλαπλής επιλογής - Κεφάλαιο 2 Α1. Ο αλγόριθμος είναι απαραίτητος μόνο για την επίλυση προβλημάτων πληροφορικής Α2. Ο αλγόριθμος αποτελείται από ένα πεπερασμένο σύνολο εντολών Α3. Ο αλγόριθμος

Διαβάστε περισσότερα

- Εισαγωγή - Επίπεδα μνήμης - Ολοκληρωμένα κυκλώματα μνήμης - Συσκευασίες μνήμης προσωπικών υπολογιστών

- Εισαγωγή - Επίπεδα μνήμης - Ολοκληρωμένα κυκλώματα μνήμης - Συσκευασίες μνήμης προσωπικών υπολογιστών Μάθημα 4.5 Η Μνήμη - Εισαγωγή - Επίπεδα μνήμης - Ολοκληρωμένα κυκλώματα μνήμης - Συσκευασίες μνήμης προσωπικών υπολογιστών Όταν ολοκληρώσεις το μάθημα αυτό θα μπορείς: Να αναφέρεις τα κυριότερα είδη μνήμης

Διαβάστε περισσότερα

Προγραμματισμός Ι. Δυναμική Διαχείριση Μνήμης. Δημήτρης Μιχαήλ. Ακ. Έτος 2011-2012. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Προγραμματισμός Ι. Δυναμική Διαχείριση Μνήμης. Δημήτρης Μιχαήλ. Ακ. Έτος 2011-2012. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο Προγραμματισμός Ι Δυναμική Διαχείριση Μνήμης Δημήτρης Μιχαήλ Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο Ακ. Έτος 2011-2012 Ανάγκη για Δυναμική Μνήμη Στατική Μνήμη Μέχρι τώρα χρησιμοποιούσαμε

Διαβάστε περισσότερα

Chapter 5. Ο επεξεργαστής: διαδρομή δεδομένων και μονάδα ελέγχου. Ενδέκατη (11 η ) δίωρη διάλεξη.

Chapter 5. Ο επεξεργαστής: διαδρομή δεδομένων και μονάδα ελέγχου. Ενδέκατη (11 η ) δίωρη διάλεξη. Chapter 5 Ο επεξεργαστής: διαδρομή δεδομένων και μονάδα ελέγχου Ενδέκατη (11 η ) δίωρη διάλεξη. Διαφάνειες διδασκαλίας από το πρωτότυπο αγγλικό βιβλίο (4 η έκδοση), μετάφραση: Καθ. Εφαρμογών Νικόλαος Πετράκης,

Διαβάστε περισσότερα

Αρχιτεκτονική Μνήµης

Αρχιτεκτονική Μνήµης ΕΣ 08 Επεξεργαστές Ψηφιακών Σηµάτων Αρχιτεκτονική Μνήµης Τµήµα Επιστήµη και Τεχνολογίας Τηλεπικοινωνιών Πανεπιστήµιο Πελοποννήσου Βιβλιογραφία Ενότητας Kuo [2005]: Chapters 3 & 4 Lapsley [2002]: Chapter

Διαβάστε περισσότερα

Εισαγωγή στην Αριθμητική Ανάλυση

Εισαγωγή στην Αριθμητική Ανάλυση Εισαγωγή στην Αριθμητική Ανάλυση Εισαγωγή στη MATLAB ΔΙΔΑΣΚΩΝ: ΓΕΩΡΓΙΟΣ ΑΚΡΙΒΗΣ ΒΟΗΘΟΙ: ΔΗΜΗΤΡΙΑΔΗΣ ΣΩΚΡΑΤΗΣ, ΣΚΟΡΔΑ ΕΛΕΝΗ E-MAIL: SDIMITRIADIS@CS.UOI.GR, ESKORDA@CS.UOI.GR Τι είναι Matlab Είναι ένα περιβάλλον

Διαβάστε περισσότερα

Παραλληλισµός Εντολών (Pipelining)

Παραλληλισµός Εντολών (Pipelining) ΕΣ 08 Επεξεργαστές Ψηφιακών Σηµάτων Παραλληλισµός Εντολών (Pipelining) Τµήµα Επιστήµη και Τεχνολογίας Τηλεπικοινωνιών Πανεπιστήµιο Πελοποννήσου Βιβλιογραφία Ενότητας Kuo [2005]: Chapter 3: Section 3.4,

Διαβάστε περισσότερα

ΤΕΧΝΙΚΕΣ ΑΥΞΗΣΗΣ ΤΗΣ ΑΠΟΔΟΣΗΣ ΤΩΝ ΥΠΟΛΟΓΙΣΤΩΝ I

ΤΕΧΝΙΚΕΣ ΑΥΞΗΣΗΣ ΤΗΣ ΑΠΟΔΟΣΗΣ ΤΩΝ ΥΠΟΛΟΓΙΣΤΩΝ I ΤΕΧΝΙΚΕΣ ΑΥΞΗΣΗΣ ΤΗΣ ΑΠΟΔΟΣΗΣ ΤΩΝ ΥΠΟΛΟΓΙΣΤΩΝ I MIPS Η MIPS (Microprocessor without Interlocked Pipeline Stages) είναι μία αρχιτεκτονική συνόλου εντολών (ISA) γλώσσας μηχανής που αναπτύχθηκε από την εταιρεία

Διαβάστε περισσότερα

Γενική οργάνωση υπολογιστή «ΑΒΑΚΑ»

Γενική οργάνωση υπολογιστή «ΑΒΑΚΑ» Περιεχόμενα Γενική οργάνωση υπολογιστή «ΑΒΑΚΑ»... 2 Καταχωρητές... 3 Αριθμητική-λογική μονάδα... 3 Μονάδα μνήμης... 4 Μονάδα Εισόδου - Εξόδου... 5 Μονάδα ελέγχου... 5 Ρεπερτόριο Εντολών «ΑΒΑΚΑ»... 6 Φάση

Διαβάστε περισσότερα

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

Προγραμματισμός Η/Υ 1 (Εργαστήριο) Προγραμματισμός Η/Υ 1 (Εργαστήριο) Ενότητα 2: Δομή ενός προγράμματος C Καθηγήτρια Εφαρμογών: Τσαγκαλίδου Ροδή Τμήμα: Ηλεκτρολόγων Μηχανικών Τ.Ε. Άδειες Χρήσης Το παρόν εκπαιδευτικό υλικό υπόκειται σε άδειες

Διαβάστε περισσότερα

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

Πανεπιστήμιο Θεσσαλίας Τμήμα Ηλεκτρολόγων Μηχανικών & Μηχανικών Υπολογιστών Πανεπιστήμιο Θεσσαλίας Τμήμα Ηλεκτρολόγων Μηχανικών & Μηχανικών Υπολογιστών Αρχιτεκτονική Υπολογιστών Άσκηση 6: Ασκήσεις Εξαμήνου Μέρος Β Νοέμβριος 2016 Στην άσκηση αυτή θα μελετήσουμε την εκτέλεση ενός

Διαβάστε περισσότερα

Στοιχεία αρχιτεκτονικής μικροεπεξεργαστή

Στοιχεία αρχιτεκτονικής μικροεπεξεργαστή Στοιχεία αρχιτεκτονικής μικροεπεξεργαστή Αριθμός bit δίαυλου δεδομένων (Data Bus) Αριθμός bit δίαυλου διευθύνσεων (Address Bus) Μέγιστη συχνότητα λειτουργίας (Clock Frequency) Τύποι εντολών Αριθμητική

Διαβάστε περισσότερα

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

Πανεπιστήμιο Θεσσαλίας Τμήμα Ηλεκτρολόγων Μηχανικών & Μηχανικών Υπολογιστών Τμήμα Πληροφορικής Πανεπιστήμιο Θεσσαλίας Τμήμα Ηλεκτρολόγων Μηχανικών & Μηχανικών Υπολογιστών Τμήμα Πληροφορικής Άσκηση : Λυμένες Ασκήσεις Έστω ένα σύστημα μνήμης, στο οποίο έχουμε προσθέσει μια κρυφή μνήμη θυμάτων 6 θέσεων

Διαβάστε περισσότερα

Λειτουργικά Συστήματα (διαχείριση επεξεργαστή, μνήμης και Ε/Ε)

Λειτουργικά Συστήματα (διαχείριση επεξεργαστή, μνήμης και Ε/Ε) Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Εισαγωγή στην Επιστήμη των Υπολογιστών 2015-16 Λειτουργικά Συστήματα (διαχείριση επεξεργαστή, και Ε/Ε) http://di.ionio.gr/~mistral/tp/csintro/ Μ.Στεφανιδάκης Τι είναι

Διαβάστε περισσότερα

Τι είναι ένα λειτουργικό σύστημα (ΛΣ); Μια άλλη απεικόνιση. Το Λειτουργικό Σύστημα ως μέρος του υπολογιστή

Τι είναι ένα λειτουργικό σύστημα (ΛΣ); Μια άλλη απεικόνιση. Το Λειτουργικό Σύστημα ως μέρος του υπολογιστή Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Εισαγωγή στην Επιστήμη των Υπολογιστών 2014-15 Λειτουργικά Συστήματα (διαχείριση επεξεργαστή, και Ε/Ε) http://di.ionio.gr/~mistral/tp/csintro/ Μ.Στεφανιδάκης Τι είναι

Διαβάστε περισσότερα

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

Αρχιτεκτονική Υπολογιστών Τμήμα Μηχανικών Πληροφορικής & Τηλεπικοινωνιών Αρχιτεκτονική Υπολογιστών Ενότητα 13: (Μέρος Γ ) Συστήματα Παράλληλης & Κατανεμημένης Επεξεργασίας Δρ. Μηνάς Δασυγένης mdasyg@ieee.org Εργαστήριο Ψηφιακών

Διαβάστε περισσότερα

Αρχιτεκτονικές Συνόλου Εντολών (ΙΙ)

Αρχιτεκτονικές Συνόλου Εντολών (ΙΙ) Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών 2016-17 Αρχιτεκτονικές Συνόλου Εντολών (ΙΙ) (Δομή Εντολών και Παραδείγματα) http://mixstef.github.io/courses/comparch/ Μ.Στεφανιδάκης Αρχιτεκτονική

Διαβάστε περισσότερα

Αρχιτεκτονικές Συνόλου Εντολών

Αρχιτεκτονικές Συνόλου Εντολών Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών 2016-17 Αρχιτεκτονικές Συνόλου Εντολών (Instruction Set Architectures - ISA) http://mixstef.github.io/courses/comparch/ Μ.Στεφανιδάκης Ο

Διαβάστε περισσότερα

i Όλες οι σύγχρονες ΚΜΕ είναι πολυπλοκότερες!

i Όλες οι σύγχρονες ΚΜΕ είναι πολυπλοκότερες! Ιόνιο Πανεπιστήμιο Τμήμα Πληροφορικής Αρχιτεκτονική Υπολογιστών 2016-17 Κεντρική Επεξεργασίας (Σχεδιασμός και λειτουργία μιας απλής ΚΜΕ) http://mixstef.github.io/courses/comparch/ Μ.Στεφανιδάκης Η υπολογιστική

Διαβάστε περισσότερα

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

Ψευδοκώδικας. November 7, 2011 Ψευδοκώδικας November 7, 2011 Οι γλώσσες τύπου ψευδοκώδικα είναι ένας τρόπος περιγραφής αλγορίθμων. Δεν υπάρχει κανένας τυπικός ορισμός της έννοιας του ψευδοκώδικα όμως είναι κοινός τόπος ότι οποιαδήποτε

Διαβάστε περισσότερα

Μικροεπεξεργαστές - Μικροελεγκτές Ψηφιακά Συστήματα

Μικροεπεξεργαστές - Μικροελεγκτές Ψηφιακά Συστήματα Μικροεπεξεργαστές - Μικροελεγκτές Ψηφιακά Συστήματα 1. Ποια είναι η σχέση της έννοιας του μικροεπεξεργαστή με αυτή του μικροελεγκτή; Α. Ο μικροεπεξεργαστής εμπεριέχει τουλάχιστο έναν μικροελεγκτή. Β. Ο

Διαβάστε περισσότερα

Ανάπτυξη Εφαρμογών σε Προγραμματιστικό Περιβάλλον

Ανάπτυξη Εφαρμογών σε Προγραμματιστικό Περιβάλλον Ανάπτυξη Εφαρμογών σε Προγραμματιστικό Περιβάλλον ΚΕΦΑΛΑΙΑ 3 και 9 ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ ΚΑΙ ΑΛΓΟΡΙΘΜΟΙ ΠΙΝΑΚΕΣ Δεδομένα αφαιρετική αναπαράσταση της πραγματικότητας και συνεπώς μία απλοποιημένη όψη της δηλαδή.

Διαβάστε περισσότερα

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

ΕΘΝΙKΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΣΧΟΛΗ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΕΡΓΑΣΤΗΡΙΟ ΥΠΟΛΟΓΙΣΤΙΚΩΝ ΣΥΣΤΗΜΑΤΩΝ. Ονοματεπώνυμο: ΑΜ: ΕΘΝΙKΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΣΧΟΛΗ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΕΡΓΑΣΤΗΡΙΟ ΥΠΟΛΟΓΙΣΤΙΚΩΝ ΣΥΣΤΗΜΑΤΩΝ Ονοματεπώνυμο: ΑΜ: ΑΡΧΙΤΕΚΤΟΝΙΚΗ ΥΠΟΛΟΓΙΣΤΩΝ (τμήμα Μ - Ω) Κανονική εξεταστική Φεβρουαρίου

Διαβάστε περισσότερα

ΠΕΡΙΕΧΟΜΕΝΑ. 1.1. Υλικό και Λογισμικό.. 1 1.2 Αρχιτεκτονική Υπολογιστών.. 3 1.3 Δομή, Οργάνωση και Λειτουργία Υπολογιστών 6

ΠΕΡΙΕΧΟΜΕΝΑ. 1.1. Υλικό και Λογισμικό.. 1 1.2 Αρχιτεκτονική Υπολογιστών.. 3 1.3 Δομή, Οργάνωση και Λειτουργία Υπολογιστών 6 ΠΕΡΙΕΧΟΜΕΝΑ 1. Εισαγωγή στην Δομή, Οργάνωση, Λειτουργία και Αξιολόγηση Υπολογιστών 1.1. Υλικό και Λογισμικό.. 1 1.2 Αρχιτεκτονική Υπολογιστών.. 3 1.3 Δομή, Οργάνωση και Λειτουργία Υπολογιστών 6 1.3.1 Δομή

Διαβάστε περισσότερα

Μάθημα 4: Κεντρική Μονάδα Επεξεργασίας

Μάθημα 4: Κεντρική Μονάδα Επεξεργασίας Μάθημα 4: Κεντρική Μονάδα Επεξεργασίας 4.1 Γενικά Ο υπολογιστής επεξεργάζεται δεδομένα ακολουθώντας βήμα βήμα, τις εντολές ενός προγράμματος. Το τμήμα του υπολογιστή, που εκτελεί τις εντολές και συντονίζει

Διαβάστε περισσότερα

Μάθημα 8: Διαχείριση Μνήμης

Μάθημα 8: Διαχείριση Μνήμης Μάθημα 8: Διαχείριση Μνήμης 8.1 Κύρια και δευτερεύουσα μνήμη Κάθε μονάδα ενός υπολογιστή που χρησιμεύει για τη μόνιμη ή προσωρινή αποθήκευση δεδομένων ανήκει στην μνήμη (memory) του υπολογιστή. Οι μνήμες

Διαβάστε περισσότερα

Εισαγωγικά & Βασικές Έννοιες

Εισαγωγικά & Βασικές Έννοιες Εισαγωγικά & Βασικές Έννοιες ΙΙΙ 1 lalis@inf.uth.gr Γιατί πολλές διεργασίες/νήματα; Επίπεδο εφαρμογής Καλύτερη δόμηση κώδικα Αποφυγή μπλοκαρίσματος / περιοδικών ελέγχων Φυσική έκφραση παραλληλισμού Επίπεδο

Διαβάστε περισσότερα

Προγραμματισμός GPUs μέσω του περιβάλλοντος CUDA

Προγραμματισμός GPUs μέσω του περιβάλλοντος CUDA Προγραμματισμός GPUs μέσω του περιβάλλοντος CUDA Κωνσταντινίδης Ηλίας Υποψήφιος Διδάκτωρ Τμήμα Πληροφορικής & Τηλεπικοινωνιών Εθνικό και Καποδιστριακό Πανεπιστήμιο Αθηνών Νόμος Moore density doubles/18m

Διαβάστε περισσότερα

Εισαγωγή στην Επιστήμη των Υπολογιστών

Εισαγωγή στην Επιστήμη των Υπολογιστών Εισαγωγή στην Επιστήμη των Υπολογιστών Ενότητα 3: Χειρισμός δεδομένων, 2ΔΩ Τμήμα: Αγροτικής Οικονομίας & Ανάπτυξης Διδάσκων: Θεόδωρος Τσιλιγκιρίδης Μαθησιακοί Στόχοι Η Ενότητα 3 διαπραγματεύεται θέματα

Διαβάστε περισσότερα

ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2009 ΑΠΑΝΤΗΣΕΙΣ

ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2009 ΑΠΑΝΤΗΣΕΙΣ ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2009 Μάθημα : Μικροϋπολογιστές Τεχνολογία Τ.Σ. Ι, Θεωρητικής κατεύθυνσης Ημερομηνία

Διαβάστε περισσότερα

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

Αρχιτεκτονική υπολογιστών 1 Ελληνική Δημοκρατία Τεχνολογικό Εκπαιδευτικό Ίδρυμα Ηπείρου Αρχιτεκτονική υπολογιστών Ενότητα 3 : Μια άποψη του κορυφαίου επιπέδου λειτουργίας και διασύνδεσης του υπολογιστή Καρβούνης Ευάγγελος Η έννοια

Διαβάστε περισσότερα

Συστήματα Παράλληλης & Κατανεμημένης Επεξεργασίας

Συστήματα Παράλληλης & Κατανεμημένης Επεξεργασίας Τμήμα Μηχανικών Πληροφορικής & Τηλεπικοινωνιών Συστήματα Παράλληλης & Κατανεμημένης Επεξεργασίας Ενότητα 2: Αρχιτεκτονικές Von Neuman, Harvard. Κατηγοριοποίηση κατά Flynn. Υπολογισμός απόδοσης Συστημάτων

Διαβάστε περισσότερα

Συστήματα σε Ολοκληρωμένα Κυκλώματα

Συστήματα σε Ολοκληρωμένα Κυκλώματα Συστήματα σε Ολοκληρωμένα Κυκλώματα Κεφάλαιο 4: Αρχιτεκτονική των Embedded Μικροεπεξεργαστών Διδάσκων: Καθηγητής Οδυσσέας Κουφοπαύλου Τμήμα Ηλεκτρολόγων Μηχανικών και Τεχνολογίας Υπολογιστών ΕΙΣΑΓΩΓΗ Παρουσιάζεται

Διαβάστε περισσότερα

Οργάνωση και Αρχιτεκτονική Υπολογιστών. Κεφάλαιο 7.4

Οργάνωση και Αρχιτεκτονική Υπολογιστών. Κεφάλαιο 7.4 Οργάνωση και Αρχιτεκτονική Υπολογιστών Κεφάλαιο 7.4 Ε/Ε Οδηγούμενη από Διακοπές Το πρόβλημα με την προγραμματιζόμενη Ε/Ε είναι ότι ο επεξεργαστής πρέπει να περιμένει ώστε η μονάδα Ε/Ε που τον ενδιαφέρει

Διαβάστε περισσότερα

Αναφορά (1/2) Μπορούμε να ορίσουμε μια άλλη, ισοδύναμη αλλά ίσως πιο σύντομη, ονομασία για ποσότητα (μεταβλητή, σταθερή, συνάρτηση, κλπ.

Αναφορά (1/2) Μπορούμε να ορίσουμε μια άλλη, ισοδύναμη αλλά ίσως πιο σύντομη, ονομασία για ποσότητα (μεταβλητή, σταθερή, συνάρτηση, κλπ. ΤΡΙΤΗ ΔΙΑΛΕΞΗ Αναφορά (1/2) Μπορούμε να ορίσουμε μια άλλη, ισοδύναμη αλλά ίσως πιο σύντομη, ονομασία για ποσότητα (μεταβλητή, σταθερή, συνάρτηση, κλπ.): Σύνταξη τύπος όνομαα; τύπος όνομαβ{όνομαα}; όνομαβ

Διαβάστε περισσότερα

ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2014

ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2014 ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 201 ΤΕΧΝΟΛΟΓΙΑ (Ι) ΤΕΧΝΙΚΩΝ ΣΧΟΛΩΝ ΘΕΩΡΗΤΙΚΗΣ ΚΑΤΕΥΘΥΝΣΗΣ Μάθημα : Μικροϋπολογιστές

Διαβάστε περισσότερα

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

Λειτουργικά Συστήματα Η/Υ Λειτουργικά Συστήματα Η/Υ Κεφάλαιο 7 «Διαχείριση Μνήμης» Διδάσκων: Δ. Λιαροκάπης Διαφάνειες: Π. Χατζηδούκας 1 Κύρια Μνήμη 1. Εισαγωγή 2. Βασική διαχείριση μνήμης 3. Μνήμη και πολυπρογραμματισμός 4. Τμηματοποίηση

Διαβάστε περισσότερα

Μάθημα 3.2: Κεντρική Μονάδα Επεξεργασίας

Μάθημα 3.2: Κεντρική Μονάδα Επεξεργασίας Κεφάλαιο 3 ο Αρχιτεκτονική Υπολογιστών Μάθημα 3.: Κεντρική Μονάδα Επεξεργασίας Όταν ολοκληρώσεις το κεφάλαιο θα μπορείς: Να σχεδιάζεις την εσωτερική δομή της ΚΜΕ και να εξηγείς τη λειτουργία των επιμέρους

Διαβάστε περισσότερα

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

Multi Cycle Datapath. Αρχιτεκτονική Υπολογιστών. 5ο εξάμηνο ΣΗΜΜΥ ακ. έτος: Νεκ. Κοζύρης Αρχιτεκτονική Υπολογιστών 5ο εξάμηνο ΣΗΜΜΥ ακ. έτος: 2014-2015 Νεκ. Κοζύρης nkoziris@cslab.ece.ntua.gr Multi Cycle Datapath http://www.cslab.ece.ntua.gr/courses/comparch/ Άδεια Χρήσης Το παρόν εκπαιδευτικό

Διαβάστε περισσότερα

Στοιχεία από την αρχιτεκτονική των μικροϋπολογιστών

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

Διαβάστε περισσότερα

Παράλληλη Επεξεργασία

Παράλληλη Επεξεργασία Παράλληλη Επεξεργασία Φροντιστήριο: Εισαγωγή στο OpenMP Εργαστήριο Πληροφοριακών Συστημάτων Υψηλής Επίδοσης Parallel and Distributed Systems Group Τι είναι το OpenMP Πρότυπο Επέκταση στη C/C++ και τη Fortran

Διαβάστε περισσότερα

Μάθημα 5: Χαρακτηριστικά της Κ.Μ.Ε.

Μάθημα 5: Χαρακτηριστικά της Κ.Μ.Ε. Μάθημα 5: Χαρακτηριστικά της Κ.Μ.Ε. 5.1 Το ρολόι Κάθε μία από αυτές τις λειτουργίες της Κ.Μ.Ε. διαρκεί ένα μικρό χρονικό διάστημα. Για το συγχρονισμό των λειτουργιών αυτών, είναι απαραίτητο κάποιο ρολόι.

Διαβάστε περισσότερα

εισαγωγικές έννοιες Παύλος Εφραιμίδης Δομές Δεδομένων και

εισαγωγικές έννοιες Παύλος Εφραιμίδης Δομές Δεδομένων και Παύλος Εφραιμίδης 1 περιεχόμενα ενθετική ταξινόμηση ανάλυση αλγορίθμων σχεδίαση αλγορίθμων 2 ενθετική ταξινόμηση 3 ενθετική ταξινόμηση Βασική αρχή: Επιλέγει ένα-έναταστοιχείατηςμηταξινομημένης ακολουθίας

Διαβάστε περισσότερα

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

9. Συστολικές Συστοιχίες Επεξεργαστών Κεφάλαιο 9: Συστολικές συστοιχίες επεξεργαστών 208 9. Συστολικές Συστοιχίες Επεξεργαστών Οι συστολικές συστοιχίες επεξεργαστών είναι επεξεργαστές ειδικού σκοπού οι οποίοι είναι συνήθως προσκολλημένοι σε

Διαβάστε περισσότερα

Εργαστήριο 3 ΟΡΓΑΝΩΣΗ ΤΗΣ ΚΜΕ. Εισαγωγή

Εργαστήριο 3 ΟΡΓΑΝΩΣΗ ΤΗΣ ΚΜΕ. Εισαγωγή Εισαγωγή Εργαστήριο 3 ΟΡΓΑΝΩΣΗ ΤΗΣ ΚΜΕ Σκοπός του εργαστηρίου είναι να γνωρίσουµε την εσωτερική δοµή και αρχιτεκτονική της κεντρικής µονάδας επεξεργασίας, να κατανοήσουµε τον τρόπο µε τον οποίο λειτουργεί

Διαβάστε περισσότερα

Τμήμα Λογιστικής. Εισαγωγή στους Ηλεκτρονικούς Υπολογιστές. Μάθημα 8. 1 Στέργιος Παλαμάς

Τμήμα Λογιστικής. Εισαγωγή στους Ηλεκτρονικούς Υπολογιστές. Μάθημα 8. 1 Στέργιος Παλαμάς ΤΕΙ Ηπείρου Παράρτημα Πρέβεζας Τμήμα Λογιστικής Εισαγωγή στους Ηλεκτρονικούς Υπολογιστές Μάθημα 8 Κεντρική Μονάδα Επεξεργασίας και Μνήμη 1 Αρχιτεκτονική του Ηλεκτρονικού Υπολογιστή Μονάδες Εισόδου Κεντρική

Διαβάστε περισσότερα

Πρόβλημα 37 / σελίδα 207

Πρόβλημα 37 / σελίδα 207 Πρόβλημα 37 / σελίδα 207 2.5. Ôåóô áõôïáîéïëüãçóçò Δίνονται οι παρακάτω ομάδες προτάσεων. Σε κάθε μία από αυτές, να κάνετε τις απαραίτητες διορθώσεις ώστε να ισχύουν οι προτάσεις 1. Η αναπαράσταση

Διαβάστε περισσότερα

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

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

Διαβάστε περισσότερα

Λειτουργικά. Τεχνολογικό Εκπαιδευτικό Ίδρυμα Δυτικής Μακεδονίας Σιώζιος Κων/νος - Πληροφορική Ι

Λειτουργικά. Τεχνολογικό Εκπαιδευτικό Ίδρυμα Δυτικής Μακεδονίας Σιώζιος Κων/νος - Πληροφορική Ι Λειτουργικά Συστήματα 1 Λογισμικό του Υπολογιστή Για να λειτουργήσει ένας Η/Υ εκτός από το υλικό του, είναι απαραίτητο και το λογισμικό Το σύνολο των προγραμμάτων που συντονίζουν τις λειτουργίες του υλικού

Διαβάστε περισσότερα

Πολυπύρηνοι επεξεργαστές Multicore processors

Πολυπύρηνοι επεξεργαστές Multicore processors Πολυπύρηνοι επεξεργαστές Multicore processors 1 Μετάβαση στους πολυπύρηνους(1) Απόδοση των µονοεπεξεργαστών 25% ετήσια βελτίωση της απόδοσης από το 1978 έως το 1986 Κυρίως από την εξέλιξη της τεχνολογίας

Διαβάστε περισσότερα

ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2013

ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2013 ΥΠΟΥΡΓΕΙΟ ΠΑΙΔΕΙΑΣ ΚΑΙ ΠΟΛΙΤΙΣΜΟΥ ΔΙΕΥΘΥΝΣΗ ΑΝΩΤΕΡΗΣ ΚΑΙ ΑΝΩΤΑΤΗΣ ΕΚΠΑΙΔΕΥΣΗΣ ΥΠΗΡΕΣΙΑ ΕΞΕΤΑΣΕΩΝ ΠΑΓΚΥΠΡΙΕΣ ΕΞΕΤΑΣΕΙΣ 2013 ΤΕΧΝΟΛΟΓΙΑ (Ι) ΤΕΧΝΙΚΩΝ ΣΧΟΛΩΝ ΘΕΩΡΗΤΙΚΗΣ ΚΑΤΕΥΘΥΝΣΗΣ Μάθημα : Μικροϋπολογιστές

Διαβάστε περισσότερα

Λιβανός Γιώργος Εξάμηνο 2017Β

Λιβανός Γιώργος Εξάμηνο 2017Β Λιβανός Γιώργος Εξάμηνο 2017Β Υπολογιστικό σύστημα Υλικό (hardware) Λογισμικό (Software) Ολοκληρωμένα κυκλώματα, δίσκοι, οθόνη, κλπ. Λογισμικό συστήματος Προγράμματα εφαρμογών Χρειάζονται ένα συντονιστή!!!

Διαβάστε περισσότερα

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

ΑΕΠΠ Ερωτήσεις θεωρίας ΑΕΠΠ Ερωτήσεις θεωρίας Κεφάλαιο 1 1. Τα δεδομένα μπορούν να παρέχουν πληροφορίες όταν υποβάλλονται σε 2. Το πρόβλημα μεγιστοποίησης των κερδών μιας επιχείρησης είναι πρόβλημα 3. Για την επίλυση ενός προβλήματος

Διαβάστε περισσότερα

Κατανεμημένα Συστήματα

Κατανεμημένα Συστήματα Κατανεμημένα Συστήματα Σημειώσεις εργαστηρίου Lab#7 - Διεργασίες, Nήματα, Πολυνημάτωση στη Python Νεβράντζας Βάιος-Γερμανός Λάρισα, Φεβρουάριος 2013 Lab#7 - Διεργασιές, Νη ματα, Πολυνημα τωση στη Python,

Διαβάστε περισσότερα

ΣΥΣΤΗΜΑΤΑ ΠΑΡΑΛΛΗΛΗΣ ΕΠΕΞΕΡΓΑΣΙΑΣ 9o εξάμηνο ΗΜΜΥ, ακαδημαϊκό έτος

ΣΥΣΤΗΜΑΤΑ ΠΑΡΑΛΛΗΛΗΣ ΕΠΕΞΕΡΓΑΣΙΑΣ 9o εξάμηνο ΗΜΜΥ, ακαδημαϊκό έτος ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ ΣΧΟΛΗ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΜΗΧΑΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΤΟΜΕΑΣ ΤΕΧΝΟΛΟΓΙΑΣ ΠΛΞΡΟΦΟΡΙΚΗΣ ΚΑΙ ΥΠΟΛΟΓΙΣΤΩΝ ΕΡΓΑΣΤΗΡΙΟ ΥΠΟΛΟΓΙΣΤΙΚΩΝ ΣΥΣΤΗΜΑΤΩΝ http://www.cslab.ece.ntua.gr ΣΥΣΤΗΜΑΤΑ

Διαβάστε περισσότερα

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

Δομές Δεδομένων & Αλγόριθμοι Θέματα Απόδοσης Αλγορίθμων 1 Η Ανάγκη για Δομές Δεδομένων Οι δομές δεδομένων οργανώνουν τα δεδομένα πιο αποδοτικά προγράμματα Πιο ισχυροί υπολογιστές πιο σύνθετες εφαρμογές Οι πιο σύνθετες εφαρμογές απαιτούν

Διαβάστε περισσότερα

Εισαγωγή στα Συστήματα Ψηφιακής Επεξεργασίας Σήματος

Εισαγωγή στα Συστήματα Ψηφιακής Επεξεργασίας Σήματος ΕΣ 08 Επεξεργαστές Ψηφιακών Σημάτων Εισαγωγή στα Συστήματα Ψηφιακής Επεξεργασίας Σήματος Κλήμης Νταλιάνης Λέκτορας Π.Δ.407/80 Τμήμα Επιστήμη και Τεχνολογίας Τηλεπικοινωνιών Πανεπιστήμιο Πελοποννήσου Αρχιτεκτονική

Διαβάστε περισσότερα

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

Οργάνωση επεξεργαστή (1 ο μέρος) ΜΥΥ-106 Εισαγωγή στους Η/Υ και στην Πληροφορική Οργάνωση επεξεργαστή (1 ο μέρος) ΜΥΥ-106 Εισαγωγή στους Η/Υ και στην Πληροφορική Κώδικας μηχανής (E) Ο επεξεργαστής μπορεί να εκτελέσει το αρχιτεκτονικό σύνολο εντολών (instruction set architecture) Οι

Διαβάστε περισσότερα