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

Σχετικά έγγραφα
ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ

Πιο συγκεκριμένα, η χρήση του MATLAB προσφέρει τα ακόλουθα πλεονεκτήματα.

Επιστημονικοί Υπολογισμοί (ή Υπολογιστική Επιστήμη)

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

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

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

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

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

ισδιάστατοι μετασχηματισμοί ΚΕΦΑΛΑΙΟ 4: ισδιάστατοι γεωμετρικοί μετασχηματισμοί

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

Παναγιώτης Ψαρράκος Αν. Καθηγητής

Επιστημονικοί Υπολογισμοί - Μέρος ΙΙΙ: Παράλληλοι Υπολογισμοί

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

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

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

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

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

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

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

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

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

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

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

CUDA Compute Unified Device Architecture

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

Παρουσίαση συλλογών υποπρογραμμάτων για γραμμική άλγεβρα: blas lapack

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

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


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

ΕΣ 08 Επεξεργαστές Ψηφιακών Σηµάτων. Βιβλιογραφία Ενότητας

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

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

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

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

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

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

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ Σχολή Μηχανολόγων Μηχανικών Εργαστήριο Θερμικών Στροβιλομηχανών

Κεφάλαιο 8. Οπτικοποίηση Απαλοιφή

Chapter 4 (1) Αξιολόγηση και κατανόηση της απόδοσης

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

ΚΕΦΑΛΑΙΟ 10 ΥΠΟΠΡΟΓΡΑΜΜΑΤΑ

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

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

J-GANNO. Σύντοµη αναφορά στους κύριους στόχους σχεδίασης και τα βασικά χαρακτηριστικά του πακέτου (προέκδοση 0.9Β, Φεβ.1998) Χάρης Γεωργίου

Εφαρμοσμένα Μαθηματικά ΙΙ

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

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

ΓΡΑΜΜΙΚΗ ΑΛΓΕΒΡΑ (Εξ. Ιουνίου - 02/07/08) ΕΠΙΛΕΓΜΕΝΕΣ ΑΠΑΝΤΗΣΕΙΣ

1 η ΕΝΟΤΗΤΑ ΕΙΣΑΓΩΓΗ (Προγραμματισμός & MATLAB)

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

Συνήθεις διαφορικές εξισώσεις προβλήματα οριακών τιμών

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

ΑΡΙΘΜΗΤΙΚΕΣ ΜΕΘΟΔΟΙ, 5 Ο ΕΞΑΜΗΝΟ, ΠΕΡΙΕΧΟΜΕΝΑ ΠΑΡΑΔΟΣΕΩΝ. Κεφ. 1: Εισαγωγή (διάρκεια: 0.5 εβδομάδες)

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

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

Συστήματα συντεταγμένων

Γραφικά με υπολογιστές. Διδάσκων: Φοίβος Μυλωνάς. Διαλέξεις #11-#12

Chapter 4 ( ή 1 στο βιβλίο σας)

Διαφορές single-processor αρχιτεκτονικών και SoCs

ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ, ΠΟΛΥΤΕΧΝΙΚΗ ΣΧΟΛΗ ΤΜΗΜΑ ΜΗΧΑΝΙΚΩΝ ΗΛΕΚΤΡΟΝΙΚΩΝ ΥΠΟΛΟΓΙΣΤΩΝ ΚΑΙ ΠΛΗΡΟΦΟΡΙΚΗΣ ΔΙΠΛΩΜΑΤΙΚΗ ΕΡΓΑΣΙΑ. Νάκος Αλέξανδρος Α.Μ.

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

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

Δομή Ηλεκτρονικού υπολογιστή

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

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

Μαλούτα Θεανώ Σελίδα 1

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

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

ΕΘΝΙΚΟ ΜΕΤΣΟΒΙΟ ΠΟΛΥΤΕΧΝΕΙΟ. Άσκηση 5: Παράλληλος προγραμματισμός σε επεξεργαστές γραφικών

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

Η ΤΑΞΙΝΟΜΗΣΗ ΤΟΥ FLYNN!!! 1 ο ΕΠΑΛ ΡΟΔΟΥ ΤΟΜΕΑΣ ΠΛΗΡΟΦΟΡΙΚΗΣ!!!! Χατζηνικόλας Κώστας

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

Οδηγίες σχεδίασης στο περιβάλλον Blender

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

ILP (integer linear programming) βασιζόμενη εξαρτώμενη από τους πόρους μεταγλώττιση

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

HY213. ΑΡΙΘΜΗΤΙΚΗ ΑΝΑΛΥΣΗ ΕΛΑΧΙΣΤΑ ΤΕΤΡΑΓΩΝΑ AΝΑΛΥΣΗ ΙΔΙΑΖΟΥΣΩΝ ΤΙΜΩΝ

Εφαρμοσμένα Μαθηματικά ΙΙ

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

All Pairs Shortest Path

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

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

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

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

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

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

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

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

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

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

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

Εφαρμοσμένα Μαθηματικά ΙΙ

RobotArmy Περίληψη έργου

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

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

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

Ετήσια Τεχνική Έκθεση

10. Με πόσους και ποιους τρόπους μπορεί να αναπαρασταθεί ένα πρόβλημα; 11. Περιγράψτε τα τρία στάδια αντιμετώπισης ενός προβλήματος.

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

Transcript:

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

Περίληψη Το αντικείμενο της διπλωματικής εργασίας εντάσσεται στο πεδίο της ανάπτυξης αποδοτικών DLA (Dense Linear Algebra) αλγορίθμων σε πολυπύρηνες μονάδες επεξεργασίας (GPUs). Πρόκειται για αλγορίθμους που είναι απαραίτητοι σε μια πλειάδα εφαρμογών και η αποδοτική τους υλοποίηση σε νέες αρχιτεκτονικές υπολογιστικών συστημάτων ανοίγουν νέους δρόμους σε real - time επεξεργασία δεδομένων. Ειδικότερα, στόχος της διπλωματικής είναι η υλοποίηση σε GPU ενός αποδοτικού αλγορίθμου παραγοντοποίησης QR διαδοχικών πινάκων (sliding window). Ο κάθε πίνακας προκύπτει από τον προηγούμενο αφαιρώντας την πρώτη σειρά (downdating) και τοποθετώντας στο τέλος μία καινούργια γραμμή δεδομένων (updating). Ο αλγόριθμος εκμεταλλεύεται τον πλεονασμό των δεδομένων μεταξύ διαδοχικών πινάκων. Υπερτερεί έναντι άλλων στην αριθμητική πολυπλοκότητα και στην καταλληλότητα για την παραλληλοποίηση διαδοχικών παραγοντοποιήσεων. Οι υπόλοιποι αλγόριθμοι είτε αγνοούν τον πλεονασμό των δεδομένων στο κόστος των αριθμητικών πράξεων, ή τον αντιμετωπίζουν με μη αποδοτικούς τρόπους. Η παραγοντοποίηση QR γίνεται με τη χρήση ενός υβριδικού αλγορίθμου που χρησιμοποιεί παράλληλα την GPU και τη CPU με την κάθε μονάδα να αναλαμβάνει το μέρος του προβλήματος στο οποίο είναι αποδοτικότερη. Ένας επιπλέον βαθμός παραλληλίας επιτυγχάνεται στην CPU με την χρήση POSIX threads (Pthreads) όπου είναι αναγκαίο, ενώ η υλοποίηση στην GPU γίνεται με τη γλώσσα προγραμματισμού CUDA για τις κάρτες γραφικών αρχιτεκτονικής TESLA της nvidia. Ιδιαίτερα επικεντρωθήκαμε στον υπολογισμό των συντελεστών Cholesky R. Οι συντελεστές Cholesky χρησιμοποιούνται για τον υπολογισμό των προσαρμοστικών διανυσμάτων βάρους, ως έλεγχος ανάδρασης, σε διαδικασίες χωρικής και χρονικής προσαρμογής (Space Time Adaptive Processing) και σε συστήματα επισκόπησης. Για παράδειγμα στα προσαρμοστικά FIR φίλτρα όπου με τη μέθοδο των ελαχίστων τετραγώνων μπορούν να μειωθούν οι παρεμβολές στο λαμβανόμενο σήμα επειδή το προβλεπόμενο σφάλμα ελαχιστοποιείται. Η παραπάνω μέθοδος (sliding window) είναι ιδιαίτερα σημαντική στη προσέγγιση ελαχίστων τετραγώνων όταν είναι επιθυμητό να συμβάλλουν στον υπολογισμό ένας συγκεκριμένος αριθμός καινούργιων δεδομένων, ανεπιθύμητα δεδομένα να απορριφθούν ή παλιά να μην ληφθούν υπόψη. Τέτοιου είδους

απαιτήσεις συναντώνται σε χρονομεταβαλλόμενα συστήματα, σε περιπτώσεις μείωσης της επίδρασης εξωτερικών εκπομπών ή στο πρόβλημα παρακολούθησης των αλλαγών σε προσαρμοστικά φίλτρα. Η απόδοση που επιτυγχάνουμε εκτελώντας τον κώδικα στην Tesla C1060, η οποία διαθέτει 240 υπολογιστικούς πυρήνες, και στην τετραπύρηνη CPU Intel Xeon E5420 @ 2.50GHz είναι πάνω από οχτώ φορές μεγαλύτερη από τον βελτιστοποιημένο CPU - GPU αλγόριθμο QR (V.Volkov) κι έως και 13 φορές πιο γρήγορη από τον CPU αλγοριθμο QR (Intel MKL 10.0), που αγνοούν την κοινή πληροφορία στην αλληλουχία πινάκων. Η υλοποίηση αυτή επιτυγχάνει σημαντική μείωση στον χρόνο εκτέλεσης της διαδικασίας για προβλήματα αλληλουχίας πινάκων. 2

Περιεχόμενα 1 Σύγχρονες GPUs 3 1.1 Εξέλιξη των GPUs................................ 3 1.2 Γενικού Σκοπού GPUs.............................. 4 1.3 Αρχιτεκτονική των GPUs............................. 5 1.3.1 Η αρχιτεκτονική TESLA......................... 8 1.4 Εργαλεία προγραμματισμού των GPUs..................... 11 2 Βασικά χαρακτηριστικά της CUDA 13 2.1 Εισαγωγή..................................... 13 2.2 Το προγραμματιστικό μοντέλο της CUDA.................... 15 2.2.1 Η ιεραρχία των thread.......................... 15 2.2.2 Η ιεραρχία της μνήμης.......................... 16 2.2.3 Η δομή της CUDA............................ 17 2.3 Το προγραμματιστικό περιβάλλον της CUDA.................. 18 2.3.1 Περιορισμοί............................... 19 3 Εισαγωγή στους DLA αλγορίθμους 20 3.1 Προβλήματα DLA................................ 20 3.2 Βιβλιοθήκες.................................... 22 3.3 DLA και GPUs.................................. 24 3.4 Υβριδικοί αλγόριθμοι............................... 25 4 Παραγοντοποίηση QR 27 4.1 Εισαγωγή..................................... 27 4.2 Η γεωμετρία των μετασχηματισμών....................... 28 4.2.1 Μετασχηματισμός Householder..................... 29 1

4.2.2 Υπολογισμός του διανύσματος Householder.............. 30 4.3 Householder QR................................. 31 4.3.1 Block Householder............................ 33 4.4 Παραγοντοποίηση QR συστοιχίας πινάκων................... 35 5 Υλοποίηση σε CUDA 39 5.1 Μετρήσεις..................................... 47 6 Ανακεφαλαίωση - Συμπεράσματα 51 7 Παράρτημα 53 2

Κεφάλαιο 1 Σύγχρονες GPUs Η μονάδα επεξεργασία γραφικών (GPU - Graphics Processing Unit) είναι ένας ειδικευμένος μικροεπεξεργαστής που εμφανίστηκε σχεδόν ταυτόχρονα με τους σύγχρονους προσωπικούς υπολογιστές. Αποστολή της ήταν η προετοιμασία και η επεξεργασία αρχικά των δισδιάστατων (2D) και στη συνεχεία τρισδιάστατων (3D) γραφικών που εμφανίζονταν στην οθόνη, αποδεσμεύοντας την κεντρική μονάδα επεξεργασίας (CPU) από τους συγκεκριμένους υπολογισμούς, εκμεταλλευόμενη την υψηλή δυνατότητα παραλληλοποίησης τους. Τα τελευταία χρόνια με την άνοδο της επεξεργαστικής τους ισχύος η συγκεκριμένη παράλληλη αρχιτεκτονική έχει ανοίξει τον δρόμο για την χρησιμοποίηση των GPUs σε αριθμητικά απαιτητικούς (computationally intensive) αλγορίθμους πέρα από την επεξεργασία γραφικών, αντικαθιστώντας ειδικευμένες παράλληλες αρχιτεκτονικές με την αντίστοιχη μείωση κόστους λόγω της μεγάλης διάδοσης τους [16]. 1.1 Εξέλιξη των GPUs Οι πρώτες GPUs εμφανίστηκαν το 1981 από την ΙΒΜ και αποτελούνταν από 16KB μνήμης και ένα RAMDAC [12]. Απουσίαζε οποιαδήποτε δυνατότητα επεξεργασίας και rasterization των pixels (που έπρεπε να γίνουν και πάλι στην CPU) και χρησίμευε ως ένας ενδιάμεσος buffer πριν την απεικόνιση στην οθόνη. Οι GPUs με δυνατότητες επεξεργασίας εμφανίστηκαν το 1984, ενσωματώνοντας σε ένα ολοκληρωμένο ρουτίνες επεξεργασίας και απεικόνισης / rasterization, περιορίζονταν όμως μόνο σε δισδιάστατα γραφικά. Η απαίτηση για 3D παιχνίδια και εφαρμογές (CAD κ.ά.) οδήγησε στην εξέλιξη των πρώτων σύγχρονων GPUs στης αρχές του 1990, που έδιναν την δυνατότητα επιτάχυνσης των απαιτητικών τρισδιάστατων γραφικών με 3

ξεχωριστές κάρτες γραφικών από τις οποίες απουσίαζαν οι δισδιάστατες δυνατότητες. Μέχρι τα τέλη της δεκαετίας, οι 2D και 3D δυνατότητες επεξεργασίας και επιτάχυνσης των γραφικών συγχωνεύθηκαν και άρχισαν να προστίθενται στο ίδιο ολοκληρωμένο περισσότερες του ενός pipelines επεξεργασίας (texture pipelining, texture filtering), προάγγελοι των σύγχρονων πολυπύρηνων σχεδιάσεων. Το 2001 (Νvidia Geforce3) εμφανίστηκαν οι πρώτες κάρτες που επέτρεπαν τον προγραμματισμό των vertex και pixel shaders, υλοποιώντας ουσιαστικά μία παράλληλη αρχιτεκτονική όπου κάθε vertex και pixel επεξεργαζόταν ανεξάρτητα από τα υπόλοιπα, ανοίγοντας έτσι το δρόμο για τις GPUs γενικού σκοπού (GPGPUs - General Purpose GPUs) [12]. 1.2 Γενικού Σκοπού GPUs Χρησιμοποιώντας τις πολυπύρηνες σχεδιάσεις και την δυνατότητα προγραμματισμού των rendering pipelines των καρτών γραφικών, οι GPUs έγιναν κατάλληλες για την επίλυση προβλημάτων πέρα από την επεξεργασία γραφικών, αναλαμβάνοντας ουσιαστικά τον ρόλο ενός παράλληλου επεξεργαστή, κατάλληλου για αλγορίθμους υψηλής παραλληλοποίησης και αριθμητικού φόρτου (computationally intensive). Σε αυτές τις σχεδιάσεις οι GPGPUs μπορούν να χρησιμοποιηθούν παράλληλα με τον επεξεργαστή, αναλαμβάνοντας τον υπολογιστικό φόρτο τέτοιου είδους αλγορίθμων που δεν είναι κατάλληλοι για την σειριακή (sequential) λογική των μακρών pipelines των κεντρικών επεξεργαστών. Ένα ειδικά γραμμένο υποπρόγραμμα, ο kernel, εκτελείται ταυτόχρονα και ανεξάρτητα από όλους τους πυρήνες της GPU, με τον καθένα να επεξεργάζεται ένα δικό του set δεδομένων. Τα δεδομένα μεταφέρονται αρχικά από την κύρια μνήμη του υπολογιστή στην μνήμη της κάρτας γραφικών, επεξεργάζονται από την GPU και μεταφέρονται πάλι πίσω στην κύρια μνήμη. Αυτές οι αντιγραφές υπαγορεύουν την μεταφορά και επεξεργασία μεγάλων κομματιών μνήμης, ώστε να κρύβονται τα επικοινωνιακά κόστη από την κατά πολύ ταχύτερη επεξεργασία στην GPU. Με την μεγάλη επεξεργαστική ισχύ των GPUs, συχνά το περιοριστικό στοιχείο για την αύξηση των επιδόσεων δεν είναι η υπολογιστική ικανότητα αλλά οι μεγάλες απαιτήσεις των αλγορίθμων στη διαμεταγωγή (throughput) της μνήμης από και προς την κάρτα γραφικών και από την μνήμη της κάρτας γραφικών στην GPU (memory-bound algorithms). Τα προγράμματα πρέπει να προβλέπουν αυτόν τον περιορισμό και να κρύβουν κατάλληλα τα κόστη επικοινωνίας με τον έξυπνο σχεδιασμό των αλγορίθμων και την επικάλυψη τους με το χρόνο της επεξεργασίας. Συχνά είναι προτιμότερο να επαναλαμ- 4

βάνονται υπολογισμοί από την GPU από το να αποθηκεύονται και να επανακαλούνται δεδομένα από την μνήμη. Για την επίτευξη της μέγιστης δυνατής παραλληλοποίησης προσοχή πρέπει να δίνεται και στην κατάλληλη αποθήκευση των δεδομένων ώστε κάθε πυρήνας να επεξεργάζεται ένα ανεξάρτητο κομμάτι δεδομένων, αποθηκευμένο σε διαδοχικές θέσεις μνήμης για ταχύτερη προσπέλαση. Οι επιδόσεις από την χρήση της GPGPU για αλγορίθμους που προσφέρονται για παράλληλη επεξεργασία με κατάλληλα, σύμφωνα με τα παραπάνω, προγράμματα είναι πολλές τάξεις μεγέθους πάνω από τις επιδόσεις της CPU, προσφέροντας επιδόσεις που μέχρι πρότινος ήταν επιτεύξιμες μόνο με την χρήση ειδικών ακριβών αρχιτεκτονικών σε υπερυπολογιστικά συστήματα. 1.3 Αρχιτεκτονική των GPUs Τα στάδια επεξεργασίας γραφικών τα οποία υλοποιεί μία σύγχρονη GPU είναι [15] Vertex operations: δημιουργούνται τα vertices (ακμές των αντικειμένων) και υπολογίζονται τα χρώματα και οι φωτισμοί (pixel shading). Σχηματισμός τριγώνων από τα vertices Rasterization: Υπολογίζεται ποια τρίγωνα είναι ορατά από την γωνία θέασης του παρατηρητή. Κάθε τρίγωνο που είναι ορατό δημιουργεί ένα fragment σε κάθε εικονοστοιχείο της οθόνης που καλύπτει. Το τελικό χρώμα του εικονοστοιχείου υπολογίζεται από τον συνδυασμό όλων των fragments του συγκεκριμένου εικονοστοιχείου. Fragment operations: Στα fragments προστίθενται και πληροφορίες από το περιβάλλον, όπως αντανακλάσεις και εφαρμόζονται διάφορες υφές (textures) ώστε να υπολογιστεί το τελικό χρώμα. Υπολογιστικά είναι το πλέον απαιτητικό μέρος και υψηλά παραλληλίσιμο. Composition: Σχηματίζεται η τελική δισδιάστατη εικόνα από τα fragments, με ένα χρώμα ανά pixel. Η μετάβαση στις προγραμματιζόμενες μονάδες vertex και fragment επέτρεψε το ορισμό από τον προγραμματιστή, για παράδειγμα, του αλγορίθμου που ελέγχει το χρώμα σε κάθε vertex ανάλογα με τις ιδιότητες του και τον φωτισμό του περιβάλλοντος, σε αντίθεση με την προηγούμενη αρχιτεκτονική όπου ο προγραμματιστής μπορούσε να επηρεάσει μόνο την θέση του vertex και τις πηγές φωτισμού. Η συνεχής αύξηση των δυνατοτήτων προγραμματισμού αυτών 5

Σχήμα 1.1: Pipeline επεξεργασίας γραφικών των σταδίων οδήγησε στα σημερινά ενοποιημένα μοντέλα σκίασης (Unified Shader Model), όπου τα vertex και fragment shaders έχουν ενοποιηθεί και αποτελούν μία και μοναδική πλήρως προγραμματιζόμενη μονάδα επεξεργασίας. Αυτό οδήγησε και στην λύση ενός κύριου προβλήματος των καρτών επεξεργασίας γραφικών σχετικά με τη δυνατότητα σωστής κατανομής του φόρτου (ανάλογα με τις απαιτήσεις της κάθε εφαρμογής) στα διαφορετικά τμήματα της GPU και του προβλήματος της σχετικής σχεδίασης των ολοκληρωμένων. Οι GPUs διαφέρουν από την αρχιτεκτονική των CPUs καθώς ανταποκρίνονται σε διαφορετικές απαιτήσεις: μεγάλο πλήθος αριθμητικών πράξεων με εγγενή δυνατότητα παραλληλισμού, με έμφαση στην αυξημένη ρυθμαπόδοση (throughput), δηλαδή την ικανότητα υπολογισμών στην μονάδα του χρόνου, παρά στο χρόνο υστέρησης (latency), δηλαδή τον χρόνο από την είσοδο μέχρι την έξοδο των τελικά επεξεργασμένων δεδομένων. Αυτή η κατεύθυνση προέκυψε από την χαμηλή ικανότητα του ματιού να επεξεργαστεί ταχέως μεταβαλλόμενες εικόνες και από τα χιλιάδες έως και εκατομμύρια εικονοστοιχεία που απαιτούνται για να συνθέσουν μία 6

ψηφιακή εικόνα. Μία τυπική εφαρμογή γραφικών λοιπόν χρειάζεται να εφαρμόσει μία σειρά διαδοχικών υπολογισμών (graphical pipeline), όπως τα vertex και fragment operations, σε ένα μεγάλο πλήθος δεδομένων εισόδου. Για να εκτελεστεί αυτό το έργο, μία CPU θα έπαιρνε μία ομάδα στοιχείων των δεδομένων εισόδου και θα εκτελούσε πρώτα τους υπολογισμούς για τα vertices, μετά τους υπολογισμούς για τα fragments κλπ, με όλους τους υπολογισμούς να γίνονται σειριακά για το κάθε σετ δεδομένων σε πολύ μικρό χρόνο (χαμηλό latency για το κάθε σετ δεδομένων). Το graphical pipeline δηλαδή διαιρείται στον χρόνο, με ένα στάδιο να εκτελείται σειριακά κάθε φορά. Αντίθετα, οι GPUs θυσιάζουν το χαμηλό latency και την λειτουργικότητα ενός επεξεργαστή γενικού σκοπού, για να εκμεταλλευθούν δύο άλλα χαρακτηριστικά: την δυνατότητα για παράλληλη επεξεργασία πολλών δεδομένων (data parallelism) και την δυνατότητα παραλληλοποίησης του graphical pipeline, με όλα τα στάδια του να επεξεργάζονται ταυτόχρονα διαφορετικά σετ δεδομένων (task parallelism), αν και το τελευταίο χαρακτηριστικό τα τελευταία χρόνια έχει εξαλειφθεί με την έλευση των Unified Shader Models στις σύγχρονες GPGPUs. Είσοδος λοιπόν στην GPU είναι ένα σετ γεωμετρικών αντικειμένων, τυπικά τρίγωνα σε έναν τρισδιάστατο χώρο, τα οποία μετά από μια σειρά αλγορίθμων επεξεργασίας απεικονίζονται σε μία δισδιάστατη εικόνα. Το graphical pipeline διαιρείται στον χώρο και η GPU επεξεργάζεται ταυτόχρονα διαφορετικά στάδιά του, με το τμήμα του επεξεργαστή που εκτελεί το κάθε στάδιο να εξάγει τα δεδομένα του στο τμήμα του επεξεργαστή που εκτελεί το επόμενο τμήμα του pipeline. Με αυτόν τον τρόπο τα τμήματα του επεξεργαστή που αναλαμβάνουν ένα συγκεκριμένο κομμάτι του pipeline μπορεί να υλοποιηθούν κατάλληλα σε επίπεδο υλικού ώστε να είναι αποδοτικά για το είδος του υπολογισμού που αναλαμβάνουν. Επίσης, πέρα από την ταυτόχρονη εκτέλεση των διαφορετικών σταδίων, το κάθε στάδιο μπορεί να δεχθεί πολλαπλά δεδομένα εισόδου και να εφαρμόσει τον ίδιο υπολογισμό ταυτόχρονα, εκμεταλλευόμενο ότι οι υπολογισμοί είναι οι ίδιοι. Αυτό το χαρακτηριστικό σημαίνει ότι οι GPUs είναι μια υλοποίηση τύπου SIMD (Single Input-Multiple Data) όπου η παραλληλοποίηση επιτυγχάνεται με την ταυτόχρονη εκτέλεση των ίδιων πράξεων σε πολλαπλά στοιχεία. Με την εισαγωγή των προγραμματιζόμενων σταδίων των pixel και vertex shaders που περιγράψαμε παραπάνω, το ειδικευμένο υλικό των σταδίων αντικαταστάθηκε από προγραμματιζόμενες μονάδες χωρίς όμως να αλλάξει η οργάνωση και η λογική του pipeline. Το αποτέλεσμα είναι μία μακρά feed-forward graphical pipeline με πολλά ειδικευμένα στάδια, όπου κάθε υπολογισμός μπορεί να χρειαστεί χιλιάδες κύκλους μηχανής για να εκτελεστεί 7

(υψηλό latency), αλλά λόγω του task και data parallelism οι υπολογισμοί εκτελούνται σε ένα πλήθος στοιχείων επιτυγχάνοντας υψηλή ρυθμαπόδοση (throughput). Στη CPU αντίθετα, κάθε πράξη χρειάζεται λίγους κύκλους ρολογιού, αλλά μόνο ένα στοιχείο ή μία ομάδα στοιχείων θα έχει επεξεργαστεί. 1.3.1 Η αρχιτεκτονική TESLA H αρχιτεκτονική TESLA αποτελεί την πρόταση της nvidia για την υλοποίηση μιας GPGPU. Η nvidia αποκαλεί τους πολυεπεξεργαστές της μονάδας επεξεργασίας ως Streaming Multiprocessors (SMs). Ένα κατάλληλα γραμμένο πρόγραμμα σε CUDA όταν εκτελείται στη CPU δημιουργεί μια πληθώρα από νήματα (threads), καθένα από το οποία ανήκει σε ένα block του grid. Τα blocks χαρακτηρίζονται από έναν αύξων αριθμό και διανέμονται στους SMs, με το κάθε block να εκτελείται εξολοκλήρου σε έναν SM παράλληλα και ανεξάρτητα από τα υπόλοιπα. Σε κάθε SM είναι έτοιμα προς επεξεργασία παραπάνω του ενός block (πρέπει να δίνεται μέριμνα ώστε ο αριθμός των block να υπερκαλύπτει τον αριθμό των SMs της κάθε κάρτας για βέλτιστη απόδοση), έτσι ώστε μόλις ένα block ολοκληρώσει τους υπολογισμούς του να αντικατασταθεί άμεσα από ένα άλλο. Ο κάθε πολυεπεξεργαστής (SΜ) αποτελείται από 8 βαθμωτούς πυρήνες (SP - Scalar Processor) μονής ακρίβειας MAD (Multiply-and-Add), έναν πυρήνα διπλής ακριβείας MAD, δύο ειδικές μονάδες για χειρισμό transcendentals όπως πράξεις με ημίτονα (SFU - Special Function Unit), μία μονάδα που αναλαμβάνει την οργάνωση των threads (instruction unit) και τέλος 16KB on-chip κοινής μνήμης για όλα τα νήματα του block (shared memory) [2]. Το overhead για την δημιουργία, τον προγραμματισμό και την εκτέλεση των νημάτων είναι πρακτικά μηδενικό, επιτρέποντας την δημιουργία block με μεγάλο αριθμό από threads. Επίσης η εντολή συγχρονισμού των threads ενός block syncthreads() απαιτεί μόνο μία εντολή μηχανής. Αυτά τα δύο χαρακτηριστικά επιτρέπουν την υψηλή διακριτοποίηση της παράλληλης διαδικασίας με την δημιουργία πληθώρας ανεξάρτητων νημάτων, αντιστοιχώντας ακόμα και ένα στοιχείο των δεδομένων προς επεξεργασία σε κάθε thread (πχ. ένα εικονοστοιχείο μιας εικόνας). Η nvidia ονομάζει αυτόν τον τρόπο επεξεργασίας SIMT (Single Instruction - Multiple Threaded) [20]. O κάθε πυρήνας δημιουργεί και χειρίζεται τα νήματα του σε συνεχόμενες ομάδες των 32, οι οποίες αποκαλούνται warps. Τα threads ενός warp αρχίζουν την εκτέλεση τους από μία κοινή διεύθυνση της μνήμης, με το καθένα να έχει την δική του διεύθυνση εκτέλεσης (instruction address) και τους δικούς του καταχωρητές, και συνεχίζουν την εκτέλεση τους 8

Σχήμα 1.2: Διάταξη Grid και Blocks [20] ανεξάρτητα. Εάν κάποιο νήμα ακολουθήσει κάποιο διαφορετικό κομμάτι του κώδικα μετά από εντολή διακλάδωσης, η εκτέλεση του θα γίνει ανεξάρτητα και επιπλέον (όχι παράλληλα) από τα υπόλοιπα, οπότε και τέτοιες διακλαδώσεις πρέπει να αποφεύγονται με τον έξυπνο διαχωρισμό του προβλήματος στα blocks (ή και σε warps, αφού κάθε warp εκτελείται αυτόνομα). Σε αντίθεση με τις συνήθεις αρχιτεκτονικές SIMD που απαιτούν το μέγεθος του διανύσματος παράλληλης επεξεργασίας (vector length) για να μεταγλωττιστούν (compile) για μία συγκεκριμένη αρχιτεκτονική, στην SIMT το μέγεθος του warp δεν απαιτείται, οπότε και ένα πρόγραμμα γραμμένο σε CUDA θα εκτελεστεί σε οποιαδήποτε GPU, με τους SM να αναλαμβάνουν τον διαχωρισμό και τον προγραμματισμό των threads. Πρακτικά o προγραμματιστής ορίζει το μήκος του vector length με τον ορισμό του μεγέθους του block. Επιπρόσθετα, τα νήματα κάθε warp στην αρχιτεκτονική SIMT ακολουθούν ανεξάρτητες μεταξύ τους αλληλουχίες εντολών, αντίθετα με τις διανυσματικές αρχιτεκτονικές (vector machines) SIMD όπου ο προγραμματιστής πρέπει να προβλέψει και να χειριστεί τις παρεκκλίσεις από την κοινή ακολουθία εντολών. Σε κάθε χρόνο εκτέλεσης εντολής ο SM επιλέγει ένα warp που είναι έτοιμο προς εκτέλεση και εκτελεί την εντολή ταυτόχρονα για όλα τα νήματα του warp. Απόρροια αυτού είναι ότι τα 9

Σχήμα 1.3: Αρχιτεκτονική TESLA [20] block πρέπει να περιέχουν πάντα περισσότερο από 32 νήματα, με δοκιμές να δείχνουν ότι 64 είναι αρκετά για να υπάρχει συνέχεια μία πληθώρα στοιχείων έτοιμων για επεξεργασία χωρίς να εξαντλείται η on-chip μνήμη από τα πολλά warps που είναι έτοιμα προς εκτέλεση [2]. H ρυθμαπόδοση σε έναν SM που είναι πλήρως κατειλημμένος από νήματα (32) προκύπτει για μονή ακρίβεια, transcendental και διπλή ακρίβεια 32/8, 32/2 και 32 κύκλοι ρολογιού ανά εντολή αντίστοιχα. Η μνήμη που υπάρχει πάνω (on-chip) σε κάθε SM είναι Ένα set 32μπιτων καταχωρητών (registers). Μία κοινή μνήμη (shared memory) για όλους τους πυρήνες (SPs). Μία μνήμη μόνο ανάγνωσης constant cache για όλους τους SPs που επιταχύνει τις αναγνώσεις από τον χώρο constant memory της κύριας μνήμης της κάρτας γραφικών. 10

Μία μνήμη μόνο ανάγνωσης texture cache για όλους τους SPs που επιταχύνει τις αναγνώσεις από τον χώρο texture memory της κύριας μνήμης της κάρτας γραφικών. H κύρια μνήμη της κάρτας γραφικών χαρακτηρίζεται ως local ή global μνήμη και η προσπέλαση σε αυτήν δεν γίνεται διαμέσου κάποιας μνήμης cache. Σχήμα 1.4: Ιεραρχία μνήμης [20] 1.4 Εργαλεία προγραμματισμού των GPUs Παράλληλα με την εξέλιξη των σύγχρονων GPGPUs αναδύθηκε και η ανάγκη για προγραμματιστικά εργαλεία που θα διευκόλυναν την συγγραφή παράλληλου κώδικα, στοχευμένου στις καινούριες σχεδιάσεις. Η συγγραφή ενός προγράμματος γενικού σκοπού για GPUs στο παρελθόν αναγκαστικά γινόταν μέσω της χρήσης APIs για επεξεργασία γραφικών με τον κατάλληλο χειρισμό εννοιών όπως texture filtering, vertices, blending κ.ά. Η απαίτηση για χρήση APIs πέρα από τα ειδικευμένα για επεξεργασία γραφικών, όπως τα DirectX και OpenGL, οδήγησαν στην δημιουργία και χρησιμοποίηση γλωσσών υψηλότερου επιπέδου (κυρίως C), με την 11

προσθήκη επεκτάσεων που επιτρέπουν τον προγραμματισμό και την διαχείριση των παράλληλων αρχιτεκτονικών. Οι πρώτες προσπάθειες, όπως οι BrookGPU και Sh, επέτρεψαν στους προγραμματιστές να χειριστούν τον παραλληλισμό με αφηρημένες έννοιες όπως streams και kernels, χωρίς την ανάγκη γνώσης και προγραμματισμού κάποιας συγκεκριμένης αρχιτεκτονικής υλικού [15]. Στον χώρο των καρτών γραφικών σήμερα, οι επεκτάσεις που εισαγάγουν δύο διαφορετικά πρότυπα, τα OpenCL και CUDA, έχουν αποκτήσει ευρεία διάδοση και χρησιμοποιούνται για την επιτάχυνση ολοένα και περισσότερων εφαρμογών. Αν και η CUDA μπορεί να αξιοποιηθεί προς το παρών μόνο για κάρτες γραφικών της nvidia, η μεγαλύτερη της διάδοση και η ύπαρξη βιβλιοθηκών που διευκολύνουν την συγγραφή κώδικα για μαθηματικούς αλγορίθμους (BLAS, κάποιες ρουτίνες της LAPACK) οδήγησαν στην επιλογή της ως προγραμματιστική πλατφόρμα για την υλοποίηση που παρουσιάζουμε. 12

Κεφάλαιο 2 Βασικά χαρακτηριστικά της CUDA 2.1 Εισαγωγή Τα τελευταία χρόνια η κατασκευαστική εξέλιξη των ολοκληρωμένων των επεξεργαστών μέσω της συνεχής αύξησης των ρολογιών χρονισμού τους έχει αμβλυνθεί, εξαιτίας κυρίως των θερμικών περιορισμών και της αντίστοιχης αυξημένης κατανάλωσης ισχύος, όπως επίσης και λόγω της δυσκολίας περαιτέρω σμίκρυνσης των ολοκληρωμένων με τις υπάρχουσες μεθόδους επεξεργασίας (αυξάνοντας το ρολόι χρονισμού τα ηλεκτρόνια προλαβαίνουν να καλύψουν μικρότερη απόσταση και άρα οι πύλες των ολοκληρωμένων πρέπει να βρίσκονται όλο και πιο κοντά). Αν και ο νόμος του Moore που προβλέπει τον ανά 18μηνο διπλασιασμό της επεξεργαστικής ισχύος εξακολουθεί να ισχύει, οι κατασκευαστές των ολοκληρωμένων έχουν μετατοπίσει τις προσπάθειες τους στην ολοένα και μεγαλύτερη παραλληλοποίηση της επεξεργαστικής ικανότητας των επεξεργαστών τους. Οι επεξεργαστές των προσωπικών υπολογιστών ήδη την τελευταία πενταετία έχουν καταστεί πολυπύρηνοι, με τις σχεδιάσεις να στοχεύουν την ενσωμάτωση δεκάδων πυρήνων στα αμέσως επόμενα χρόνια [14]. Η τάση αυτή ακολουθείται πλέον και από τα ολοκληρωμένα που χρησιμοποιούνται σε μικρότερες υπολογιστικές μονάδες (mobile phones, PDAs, tablet PCs), όπου και εκεί οι θερμικές απαιτήσεις και η κατανάλωση ισχύος είναι ιδιαίτερα πιεστικές. Σε αυτό το υπό διαμόρφωση πεδίο, οι κάρτες γραφικών βρίσκονται στον φυσικό τους χώρο, καθώς η δυνατότητα μαζικής παράλληλης επεξεργασίας με την χρήση πολλών όμοιων πυρήνων ήταν το βασικό τους επεξεργαστικό χαρακτηριστικό, όπως υπαγορευόταν από την ανάγκη εφαρμογής των ίδιων μαθηματικών μετασχηματισμών για την δημιουργία των χιλιάδων εικονοστοιχείων που απαιτούνται σε δισδιάστατες και πολύ περισσότερο σε τρισδιάστατες εφαρμογές. 13

Σχήμα 2.1: Εξέλιξη GPUs και CPUs [20] Η εξέλιξη στις κάρτες γραφικών τα τελευταία χρόνια έχει κυρίαρχα στραφεί στην ενσωμάτωση ολοένα και περισσότερων επεξεργαστικών πυρήνων (processing cores), με τις σύγχρονες σχεδιάσεις να διαθέτουν μέχρι και 500, ενώ οι τάσεις δείχνουν σχεδόν διπλασιασμό της επεξεργαστικής τους ισχύος ανά 2 χρόνια. Αυτή η τεράστια παράλληλη επεξεργαστική ισχύς έχει δημιουργήσει την ανάγκη για κατάλληλα προγραμματιστικά εργαλεία και μεθόδους που θα διευκολύνουν την συγγραφή κώδικα κατάλληλου για παραλληλοποίηση. Η μετατροπή ενός κώδικα γραμμένου με την κλασσική σειριακή προσέγγιση σε κατάλληλα παράλληλο κώδικα δεν έχει καταστεί εφικτό να γίνεται αυτόματα σε αποδοτικό βαθμό μέσω της χρήσης τετριμμένων εργαλείων, και λόγω και των αλλαγών στο υλικό που περιγράφηκαν παραπάνω χαρακτηρίζεται πλέον ως η κυρίαρχη πρόκληση της βιομηχανίας λογισμικού [16]. Επιπλέον, οι διαφορές κάθε πλατφόρμας παραλληλισμού (CPUs, GPUs, clusters, vector machines κλπ) απαιτούν και διαφορετική υλοποίηση του παράλληλου κώδικα. Η λύση της nvidia σε αυτό το πρόβλημα είναι η CUDA (Compute Unified Device Architecture), μια πλατφόρμα λογισμικού που στόχο έχει την βελτιστοποίηση της συγγραφής κώδικα στις κάρτες γραφικών της εταιρείας. Η CUDA αντικαθιστά τις πλατφόρμες που χρησιμοποιούνται για την συγγραφή τρισδιάστατων εφαρμογών (opengl, Direct3D κ.ά.), που έκαναν την συγγραφή κώδικα για γενικού τύπου επεξεργαστικές εφαρμογές δύσκολο έως αδύνατο, με μερικές απλές προσθήκες στην γλώσσα C. Ο προγραμματιστής μπορεί να αναμίξει κλασσικό κώδικα C που τρέχει στην CPU με κώδικα CUDA που τρέχει παράλληλα στον πυρήνα με τη χρήση κάποιων επιπλέον βιβλιοθηκών, δομών και abstractions που μέσω του API (Application Programming Interface) 14

κρύβουν τις λεπτομέρειες του χαμηλού επιπέδου υλικού. Έτσι ο προγραμματιστής της εφαρμογής μπορεί εύκολα να γράψει παράλληλο κώδικα, δημιουργώντας συναρτήσεις kernels που εκτελούνται ασύγχρονα στην GPU και χρησιμοποιώντας έτοιμες βιβλιοθήκες συναρτήσεων της CUDA. 2.2 Το προγραμματιστικό μοντέλο της CUDA Στον πυρήνα της CUDA υπάρχουν τρεις σημαντικές έννοιες: η ιεραρχία των νημάτων, η ιεραρχία της μνήμης και o συγχρονισμός των πυρήνων. Ο χειρισμός αυτών γίνεται μέσω κατάλληλων κλήσεων σε συναρτήσεις του API της CUDA, ανεξάρτητα από το είδος του επεξεργαστή που χρησιμοποιείται κάθε φορά. Έτσι το εκτελέσιμο ενός προγράμματος μπορεί να εκτελεστεί με οποιονδήποτε αριθμό πυρήνων, και μόνο οι οδηγοί της κάρτας γραφικών χρειάζεται να ξέρουν τον αριθμό τους και τον συγκεκριμένο τύπο της κάρτας. 2.2.1 Η ιεραρχία των thread Η CUDA επεκτείνει τη γλώσσα C δίνοντας τη δυνατότητα στον προγραμματιστή να χειριστεί τους πυρήνες της GPU με το να γράψει ειδικές συναρτήσεις C, που ονομάζονται kernels. O κάθε kernel προσδιορίζει τον κώδικα που τρέχει το κάθε thread (σε έναν πυρήνα) της GPU, και όλα τα thread που δημιουργούνται τρέχουν παράλληλα τον ίδιο αυτό κώδικα. Ο kernel καλείται δίνοντας του δύο ορίσματα, με την παρακάτω εντολή: kernel<<<dimgrid, dimblock>>>(... parameter list...); όπου με dimgrid ορίζεται ένα τρισδιάστατο διάνυσμα για τον αριθμό των threads και με dimblock ένα δισδιάστατο διάνυσμα για τον αριθμό των blocks. Κάθε block περιέχει dimgrid αριθμό από threads, και άρα ο συνολικός αριθμός των threads που δημιουργούνται είναι dimgrid x dimblock. H κλήση ενός πυρήνα εισάγει ένα σχετικό μικρό overhead (3-7 μs) [2], ενώ η δημιουργία και η εκτέλεση των thread δεν εισάγουν κάποιο παραπάνω κόστος [20]. Τα νήματα ενός block μπορούν να συγχρονίζονται μεταξύ τους με την εντολή syncthreads(), όπως επίσης και να ανταλλάσσουν δεδομένα μεταξύ τους μέσω της κοινής γρήγορης μνήμης shared memory που διαθέτει ο κάθε πυρήνας. Αντίθετα, threads διαφορετικών blocks είναι δυνατό να συγχρονίζονται μεταξύ τους μόνο μέσω των λειτουργιών ατομικής μνήμης (atomic memory operations) πάνω στην κοινή global μνήμη, που όμως κοστίζουν πολύ, και το προγραμματι- 15

στικό μοντέλο της εκάστοτε εφαρμογής πρέπει να τα αντιμετωπίζει ως ανεξάρτητες, παράλληλες υπολογιστικές μονάδες. Η χρονική σειρά εκτέλεσης των blocks καθορίζεται αποκλειστικά από τις μονάδες ελέγχου του επεξεργαστή και δεν είναι δυνατόν να προσδιοριστεί από τον προγραμματιστή. Με αυτόν τον τρόπο δίνεται η δυνατότητα υλοποίησης παράλληλου κώδικα τόσο χαμηλής διακριτότητας, με την χρήση πολλών παράλληλων blocks, όσο και κώδικα υψηλότερης διακριτότητας μέσω των threads του κάθε block. Ανάμεσα σε αυτά τα threads μπορεί να υπάρξει συγχρονισμός (με την χρήση της intrinsic συνάρτησης syncthreads()) και απόκλιση από την εκτέλεση του ίδιου κώδικα μέσω της χρήσης λογικών συνθηκών στο εσωτερικό του kernel. 2.2.2 Η ιεραρχία της μνήμης Στην CUDA η μνήμη του υπολογιστή (RAM) αναφέρεται ως host memory, ενώ η μνήμη της κάρτας γραφικών ως device memory. Ο κώδικας που βρίσκεται στο εσωτερικό ενός kernel μπορεί να επεξεργαστεί δεδομένα που βρίσκονται μόνο στην device memory, ενώ αντίθετα κώδικας έξω από τον kernel δεν μπορεί να συνεργαστεί απευθείας με αυτήν. Για αυτόν τον λόγο υπάρχουν συναρτήσεις δέσμευσης, αποδέσμευσης, αντιγραφής και μεταφοράς δεδομένων ανάμεσα στην host και την device memory. Τα threads έχουν πρόσβαση σε δεδομένα από διάφορους χώρους μνήμης της κάρτας γραφικών. Σε κάθε thread αντιστοιχεί ένα σύνολο από registers προσβάσιμους μόνο από αυτό. Το συνολικό μέγεθος αυτού του register file για κάθε πυρήνα είναι 32-64ΚΒ και αποτελεί την πιο γρήγορη μνήμη που είναι διαθέσιμη. Επίσης όλα τα threads ενός block έχουν πρόσβαση σε έναν κοινό χώρο μνήμης μεγέθους 16KB ανά πυρήνα, με διάρκεια ζωής ίδια με κείνη του block. Και οι δύο αυτοί χώροι μνήμης βρίσκονται on-chip και άρα επιτυγχάνουν πολύ υψηλούς ρυθμούς διαμεταγωγής, αλλά εάν δεν υπάρχει ανάγκη για επικοινωνία δεδομένων ανάμεσα στα threads πρέπει να προτιμάται η χρήση του register file καθώς είναι γρηγορότερο [2]. Τέλος υπάρχει η global ή device memory της κάρτας γραφικών που χρησιμοποιείται για την μεταφορά και αποθήκευση δεδομένων από και προς την μνήμη host, ενώ δύο ειδικοί χώροι μνήμης της είναι οι constant και texture memory που είναι βελτιστοποιημένες για κάποιες ειδικές χρήσεις (data filtering κλπ) [20]. 16

Σχήμα 2.2: Κοινοί χώροι μνήμης. [20] 2.2.3 Η δομή της CUDA Τα threads ενός block εκτελούνται σε ομάδες των 32 που ονομάζονται warps. Κάθε επεξεργαστικός πυρήνας σε μία CUDA-enabled GPU διαθέτει 8 MAD (Multiply And Add) pipelines απλής ακρίβειας (Scalar Processors), στις οποίες εκτελούνται παράλληλα τα threads ενός warp. To κάθε thread αντιστοιχίζεται σε έναν SP. H nvidia ονομάζει αυτό το μοντέλο SIMT (Single- Instruction Multiple-Thread), τονίζοντας έτσι τη διαφορά με τη SIMD (Single-Instruction Multiple-Data) που απαιτεί από τον προγραμματιστή να δηλώσει τον αριθμό των παράλληλα εκτελούμενων threads (για παράδειγμα η SSE της Intel). Τα warps ενός block εκτελούνται ανεξάρτητα το ένα από το άλλο και για μέγιστη ταχύτητα πρέπει να αποφεύγεται η απόκλιση στον κώδικα ανάμεσα στα threads ενός warp. Σε περίπτωση κώδικα που αποκλίνει μέσω μιας συνθήκης, τα συγκεκριμένα threads εκτελούνται σειριακά επιπλέον της παράλληλης εκτέλεσης των 17

υπολοίπων. O μέγιστος αριθμός threads και warps ανά επεξεργαστικό πυρήνα είναι 1024 και 32 αντίστοιχα, αλλά ο αριθμός των ενεργών blocks ανά πυρήνα καθορίζεται από το πόσοι registers και πόση local memory χρειάζονται για έναν kernel, αφού οι συγκεκριμένοι χώροι μνήμης μοιράζονται ανάμεσα στα threads όλων των blocks. Η βελτιστοποίηση της απόδοσης συνήθως απαιτεί έναν μεγάλο αριθμό από blocks, και αριθμό threads ενός block μεγαλύτερο του 32 ή 64, αρκεί πάντα να υπάρχει έτοιμο κάποιο warp προς εκτέλεση σε όλους τους πυρήνες. Σημαντική βελτιστοποίηση της απόδοσης του κώδικα μπορεί να επιτευχθεί φροντίζοντας οι προσβάσεις στην global μνήμη να είναι κατάλληλα στοιχισμένες. Πολλοί αλγόριθμοι γραμμικής άλγεβρας περιορίζονται από τη μέγιστη διαμεταγωγή της μνήμης (memory-bound algorithms) και όχι από την επεξεργαστική ισχύ (compute-bound). Χρησιμοποιώντας κατάλληλο padding στους πίνακες που αποθηκεύουμε για την αποφυγή διενέξεων (memory access conflicts) και με την έξυπνη χρήση της shared memory ως ενός ενδιάμεσου buffer κατά τη διάρκεια της εκτέλεσης των υπολογισμών του kernel, τα κόστη που σχετίζονται με της μεταφορές από τη μνήμη στους πυρήνες και το αντίστροφο περιορίζονται αρκετά. Συχνά είναι προτιμότερο να ξαναγίνονται υπολογισμοί από δεδομένα που βρίσκονται στην shared μνήμη, παρά να γράφονται και να ανακαλούνται δεδομένα από την global μνήμη. 2.3 Το προγραμματιστικό περιβάλλον της CUDA Τα βασικά στοιχεία του προγραμματιστικού μοντέλου της CUDA είναι: Αρχικά στο CPU κομμάτι του κώδικα αντιγράφουμε τα δεδομένα προς επεξεργασία από την host memory στην device memory. Χαρακτηριστική συνάρτηση της CUDA για δέσμευση μνήμης στην device memory είναι η cudamalloc() και αντιγραφής (και προς τις δύο κατευθύνσεις) η cudamemcpy(). cudamalloc(void **devptr,site_t size) cudamemcpy(void *dst,const void *src,size_t count, enum cudamemcoykind kind) Επίσης ορίζουμε τις μεταβλητές dimgrid και dimblock τύπου dim3 που ορίζουν την οργάνωση του μοντέλου παραλληλοποίησης που υλοποιείται. Λόγω της πολύ υψηλής 18

δυνατότητας παραλληλοποίησης συνήθως σε κάθε thread αντιστοιχίζεται η επεξεργασία ενός και μόνο στοιχείου (πχ εικονοστοιχείο μιας εικόνας). Στο εσωτερικό του kernel γίνονται οι παράλληλοι υπολογισμοί από το κάθε thread και χρησιμοποιείται η shared memory για γρήγορη εγγραφή και ανάγνωση. Το τελικό αποτέλεσμα μεταφέρεται πίσω στην global memory. Μεταφορά των αποτελεσμάτων στην host memory. 2.3.1 Περιορισμοί Για μια πληρέστερη κατανόηση της CUDA, είναι σημαντικό να αναφέρουμε και τους περιορισμούς της με μερικές αναφορές στις μελλοντικές προοπτικές εξέλιξής της. Κατάλληλοι για μεταφορά στη CUDA είναι αλγόριθμοι εξαιρετικά απαιτητικοί αριθμητικά (computationally intensive) με καλές δυνατότητες παραλληλοποίησης που δεν απαιτούν συνεχείς επικοινωνίες ανάμεσα σε όλα τα threads (αν και σε μελλοντικές εκδόσεις της CUDA προβλέπεται η επικοινωνία ανάμεσα και στα διαφορετικά blocks). Η δυνατότητα κλήσης δύο kernel που να τρέχουν παράλληλα αναμένεται να υλοποιηθεί στην επόμενη έκδοση της CUDA. Αυτό θα επιτρέψει την κλήση πολλών μικρότερων (σε δεδομένα που πρόκειται να επεξεργαστούν) kernels, κάτι που σήμερα είναι ασύμφορο με την σειριακή τους εκτέλεση. Δεν υποστηρίζεται η αναδρομική (recursive) κλήση των kernels εξαιτίας των απαγορευτικών ποσοτήτων μνήμης που θα συνεπάγονταν οι στοίβες όλων των threads. Αυτό περιορίζει την δυνατότητα υλοποίησης κάποιων αλγορίθμων, όπως ο quicksort. Η απαίτηση μεταφοράς των δεδομένων από την device στη host memory με τα τεράστια κόστη που αυτή συνεπάγεται σε σύγκριση με τον χρόνο επεξεργασίας αυτών στην GPU, καθιστά μη αποδοτική τη χρήση της CUDA για μικρού μεγέθους προβλήματα. Η δυνατότητα για υπολογισμούς διπλής ακρίβειας (double precision) υποστηρίζεται μόνο από την τελευταία έκδοσή της και με κάθε πυρήνα να διαθέτει μόνο μία MAD pipeline διπλής ακρίβειας (και αυτό το χαρακτηριατικό πρόκειται να αλλάξει στις επόμενες εκδόσεις) [18]. 19

Κεφάλαιο 3 Εισαγωγή στους DLA αλγορίθμους 3.1 Προβλήματα DLA Είναι γεγονός πως σε πολλούς τομείς της επιστήμης η γραμμική άλγεβρα είναι ένα σημαντικό συστατικό μέρος της λύσης των προβλημάτων που προκύπτουν. Αυτά τα προβλήματα στην ουσία ανάγονται στην επίλυση κλασικών προβλημάτων γραμμικής άλγεβρας, όπως στη λύση γραμμικών συστημάτων, στο πρόβλημα των ελαχίστων τετραγώνων και στο πρόβλημα της εύρεσης ιδιοτιμών και ιδιοδιανυσμάτων. Στις λύσεις αυτών των προβλημάτων εμπλέκονται γνωστοί μέθοδοι παραγοντοποίησης, πολλαπλασιασμού πινάκων και πολλαπλασιασμού πίνακα με διάνυσμα. Συνήθως οι πίνακες αυτοί είναι πυκνοί (Dense matrices), δηλαδή δεν έχουν κάποια συγκεκριμένη δομή ώστε λαμβάνοντάς την υπόψη να διευκολυνθεί η διαδικασία επίλυσης. Έτσι έχουν αναπτυχθεί αλγόριθμοι και βιβλιοθήκες που επιταχύνουν την επίλυση τέτοιων προβλημάτων (Dense Linear Algebra - DLA) και την καθιστούν αποδοτική. Επίλυση του συστήματος Ax = b Στην περίπτωση που ο πίνακας A είναι τετραγωνικός (A R nxn ) και δεν έχει κάποια χαρακτηριστική δομή, η πιο αποτελεσματική μέθοδος που οδηγεί στη λύση του συστήματος είναι αυτή της απαλοιφής Gauss (Gauss Elimination). Όπως είναι γνωστό τα τριγωνικά συστήματα είναι εύκολο να επιλυθούν και σκοπός της απαλοιφής Gauss είναι η μετατροπή του συστήματος σε ένα ισοδύναμο άνω τριγωνικό. Η μετατροπή επιτυγχάνεται μέσω γραμμικών συνδυασμών των επιμέρους εξισώσεων. Πιο συγκεκριμένα η μέθοδος υπολογίζει έναν κάτω τριγωνικό πίνακα L κι έναν άνω τριγωνικό πίνακα U τέτοιους ώστε A = LU. Οπότε, η λύση του συστήματος δίνεται από τη λύση δύο τριγωνικών συστημάτων: 20

Ly = b, Ux = y Ax = LUx = Ly = b Στην περίπτωση που το σύστημα είναι υπερκαθορισμένο, συνήθως δεν επιδέχεται κάποια ακριβή λύση. Έτσι εφαρμόζεται η μέθοδος των ελαχίστων τετραγώνων για την ελαχιστοποίηση της παράστασης Ax b 2. Το πρόβλημα των ελαχίστων τετραγώνων Ας θεωρήσουμε το πρόβλημα υπολογισμού ενός διανύσματος x R n τέτοιο ώστε Ax = b, όπου A R mxn και b R m, με m n (υπερκαθορισμένο - overdetermined). Συνήθως δεν υπάρχει ακριβή λύση για το σύστημα αυτό, επειδή το b θα πρέπει να είναι στοιχείο του χώρου των στηλών (range space) του πίνακα A. Έτσι το πρόβλημα ανάγεται στον υπολογισμό ενός διανύσματος x που να ελαχιστοποιεί τη νόρμα Ax b 2. Η πιο διαδεδομένη μέθοδος λύσης του προβλήματος των ελαχίστων τετραγώνων είναι η μέθοδος των κανονικών εξισώσεων. Σύμφωνα με αυτήν, αν ο A είναι πλήρους τάξης, υπάρχει ένα μοναδικό x LS που ελαχιστοποιεί τη νόρμα Ax b 2 και λύνει το συμμετρικό θετικά ορισμένο σύστημα: A T Ax LS = A T b Έχουν αναπτυχθεί αρκετοί αλγόριθμοι για την εύρεση του x LS, ενδεικτικά αναφέρουμε τον παρακάτω [3]. 1. Υπολογισμός του συμμετρικού πίνακα C = A T A. 2. d = A T b. 3. Παραγοντοποίηση Cholesky του πίνακα C = GG T. 4. Λύση των συστημάτων Gy = d και G T x LS = y. Ένας άλλος τρόπος επίλυσης του προβλήματος ελαχίστων τετραγώνων πλήρους τάξης είναι με χρήση της παραγοντοποίησης QR. Αν A = QR, όπου ο πίνακας Q R mxm είναι ορθογώνιος και ο πίνακας R R mxn είναι άνω τριγωνικός, τότε: Q T A = R = R 1 n 0 m n Εάν 21

Q T b = c n d m n έχουμε: Ax b 2 2 = Q T Ax Q T b 2 2 = R 1 x c 2 2 + d 2 2 και το διάνυσμα ελαχιστοποίησης υπολογίζεται από την λύση του άνω τριγωνικού συστήματος R 1 x LS = c. 3.2 Βιβλιοθήκες Από τα όσα έχουν αναφερθεί γίνεται εύκολα αντιληπτό πως οι βασικές διαδικασίες επίλυσης προβλημάτων, όπως παραγοντοποιήσεις πινάκων, επίλυση συστημάτων, εύρεση ιδιοτιμών, αποτελούνται κατά βάση από πράξεις μεταξύ διανυσμάτων, πίνακα με διάνυσμα ή πολλαπλασιασμούς πινάκων. Το καίριο πρόβλημα σε αυτούς τους υπολογισμούς είναι η επίτευξη ισορροπίας μεταξύ του κόστους προσπέλασης των δεδομένων και του κόστους των υπολογισμών. Την λύση σε αυτό το πρόβλημα προσφέρουν οι διάφορες βιβλιοθήκες που έχουν αναπτυχθεί, όπως: BLAS - Basic Linear Algebra Subroutines. EISPACK - Ρουτίνες υπολογισμού ιδιοτιμών και ιδιοδιανυσμάτων. LINPACK - Ρουτίνες FORTRAN για ανάλυση κι επίλυση γραμμικών εξισώσεων και προβλημάτων ελαχίστων τετραγώνων. LAPACK - Ρουτίνες υπολογισμών γραμμικής άλγεβρας κατάλληλες για συστήματα διαμοιραζόμενης μνήμης. Σχεδιάστηκε ως διάδοχος των δύο παραπάνω για μοντέρνα συστήματα υψηλής απόδοσης. ScaLAPACK - Αποτελείται από ένα υποσύνολο ρουτινών της LAPACK τροποποιημένες για παράλληλα συστήματα κατανεμημένης μνήμης. BLAS Η πρώτη έκδοση της BLAS ανακοινώθηκε το 1979. Αποτέλεσε την βάση για την ανάπτυξη και άλλων βιβλιοθηκών, όπως της LAPACK. Η BLAS είναι διαθέσιμη για τις περισσότερες 22

αρχιτεκτονικές συστημάτων και εμπεριέχει συγκεκριμένες υλοποιήσεις ανάλογα με το υλικό για την επίτευξη μεγαλύτερης απόδοσης. Αποτελείται από τρία επίπεδα όπου ο διαχωρισμός γίνεται με βάση την τάξη των δεδομένων και την πολυπλοκότητα των αριθμητικών πράξεων. BLAS Level-1: Πράξεις μεταξύ διανυσμάτων πολυπλοκότητας O(n) για μεταφορά δεδομένων και υπολογισμών. Παραδείγματα ρουτινών: Επιπλέον υπάρχουν βελτιστοποιημένες εκδόσεις της BLAS για συγκεκριμένες αρχιτεκτονικές: saxpy: y i = ax i + y i sscal: y = ax sdot: s = s + n i=1 x iy i BLAS Level-2: Πράξεις πίνακα με διάνυσμα, δευτεροβάθμιας πολυπλοκότητας για μεταφορά δεδομένων και υπολογισμών. Παραδείγματα τέτοιων ρουτινών: sgemv: y = y + Ax, όπου A μη τετραγωνικός πίνακας mxn. sger (rank-one update): A = A + yx T. strsv: Επίλυση του τριγωνικού συστήματος T x = b. BLAS Level-3: Πράξεις μεταξύ πινάκων. Το κόστος της μεταφοράς δεδομένων είναι δευτεροβάθμιο ενώ των υπολογισμών O(n 3 ). Παραδείγματα: sgemm: Πολλαπλασιασμός πινάκων C = C + AB. strsm: Επίλυση τριγωνικού συστήματος T X = B, όπου ο X είναι τετράγωνος. AMD - AMD Core Math Library (ACML). Τροποποιημένη για καλύτερη απόδοση σε μηχανές x86, όπως athlon και opteron. IBM - Engineering Scientific Subroutine Library (ESSL). Intel - Math Kernel Library (MKL). Οι ρουτίνες της BLAS και της LAPACK τροποποιημένες για συστήματα Pentium. SGI - Scientific Computing Software Library (SCSL). SUN - Sun Performance Library. 23

Για τις αρχιτεκτονικές που δεν αναφέρονται παραπάνω, μέσω των λογισμικών ATLAS (Automatically Tuned Linear Algebra Software) και PHiPAC (Portable High Performance ANSI C) μπορούν να τροποποιηθούν οι ρουτίνες της BLAS έτσι ώστε να είναι βέλτιστες για την συγκεκριμένη μηχανή. 3.3 DLA και GPUs Όπως αναφέρθηκε και παραπάνω, για συγκεκριμένες αρχιτεκτονικές, DLA αλγόριθμοι έχουν υλοποιηθεί ιδιαίτερα αποτελεσματικά. Αυτό οφείλεται κυρίως στο ότι εκτελούνται, κατά πλειοψηφία, πράξεις κινητής υποδιαστολής. Για τον λόγο αυτό, μέχρι προσφάτως, δεν έγινε κάποια αποδοτική προσπάθεια για την επιτάχυνση αυτών των αλγορίθμων σε αρχιτεκτονικές όπως της GPU ή των FPGAs. Για παράδειγμα, το 2004, έγινε μία απόπειρα υλοποίησης της SGEMM σε GPU χρησιμοποιώντας shaders [4]. Μόνο η ATI X800XT παρήγαγε αποτελέσματα που μπορούσαν να συγκριθούν (περίπου 12 GFlop/s) με αυτά ενός Pentium 4 στα 3GHz [11]. Αυτή η τάση όμως τα τελευταία χρόνια έχει αλλάξει με την εμφάνιση πολυπύρηνων επεξεργαστών και τη συνεχώς αυξανόμενη διαφορά μεταξύ ταχύτητας επεξεργαστών και μνήμης (καθώς και του διαμοιραζόμενου bandwidth μεταξύ των πυρήνων). Εμπόδια που στην αρχιτεκτονική των GPUs είναι ξεπερασμένα. Έτσι αναπτύχθηκαν προγραμματιστικά εργαλεία που καθιστούν εύκολο τον προγραμματισμό της GPU και την υλοποίηση αλγορίθμων γενικότερου σκοπού. Για την επίλυση DLA προβλημάτων αναπτύχθηκε η βιβλιοθήκη CUBLAS. Περιέχει ένα υποσύνολο των ρουτινών της BLAS τροποποιημένες για την καλύτερη εκμετάλλευση της αρχιτεκτονικής ανάλογα με το προς λύση πρόβλημα. Με αυτόν τον τρόπο είναι εύκολο να επωφεληθεί κάποιος από την ισχύ των GPUs δίχως απαραίτητα να γνωρίζει εντολές χαμηλού επιπέδου και δημιουργία kernel, αλλά με απλή κλίση συναρτήσεων όπως στο περιβάλλον προγραμματισμού της C. Το 2008 αναπτύχθηκε από τον V. Volkov ένας kernel υλοποίησης της SGEMM (και άλλων συναρτήσεων της BLAS) που ξεπερνούσε σε ταχύτητα κατά πολύ την αντίστοιχη υλοποίηση της CUBLAS (125GFlop/s της CUBLAS έναντι 180GFlop/s του V.V.). Έπειτα, με χρήση των βελτιστοποιημένων αυτών συναρτήσεων (οι οποίες ενσωματώθηκαν σε μετέπειτα εκδόσεις της CUBLAS) επιτεύχθηκαν υλοποιήσεις των παραγοντοποιήσεων LU, QR και Cholesky με ιδιαίτερα σημαντική επιτάχυνση σε σχέση με προγενέστερες μεθόδους υλοποίησης [2]. Γενικά υπάρχουν τρεις βασικές ιδέες για τον προγραμματισμό DLA αλγορίθμων σε 24

CUDA [4] Χρησιμοποίηση παραλληλίσιμων συναρτήσεων επιπέδου BLAS, όπου τα δεδομένα βρίσκονται στην GPU, και στην CPU εκτελούνται οι υλοποιήσεις των συναρτήσεων της LAPACK τροποποιημένες για την GPU. Δηλαδή καλείται μία σειρά συναρτήσεων της CUBLAS, που επεξεργάζονται τα δεδομένα της GPU. Αλγόριθμοι που λόγω της δομής τους δεν εκτελούνται αποδοτικά στην GPU μπορούν να μεταφέρονται στην CPU (offload). Σε διαδικασίες κατάτμησης του φόρτου εργασίας (offload/load process), εάν είναι δυνατόν, οι GPU και CPU να χρησιμοποιούνται ασύγχρονα. Ιδιαίτερο ενδιαφέρον έχουν οι υβριδικές υλοποιήσεις, όπου οι αλγόριθμοι χωρίζονται και υλοποιούνται σε διαφορετικές αρχιτεκτονικές (CPU και GPU). 3.4 Υβριδικοί αλγόριθμοι Ο σχεδιασμός DLA αλγορίθμων, είτε προσανατολίζεται για πολυπύρηνα συστήματα είτε για GPU, πρέπει να ανταπεξέλθει στις ίδιες απαιτήσεις για μία αποδοτική υλοποίηση. Με άλλα λόγια δεν αρκεί οι αλγόριθμοι να είναι απλώς υψηλά παραλληλοποιήσιμοι, αλλά πρέπει στην εκάστοτε υλοποίηση, ο λόγος των πράξεων κινητής υποδιαστολής προς τα απαιτούμενα δεδομένα να είναι υψηλός, ώστε να καλύπτονται οι απαιτούμενες προσβάσεις στην αργή μνήμη. Επιπλέον πρέπει να δοθεί σημασία στον τρόπο κατάτμησης και διαμοιρασμού των αλγορίθμων στις δυο αρχιτεκτονικές (CPU και GPU), ώστε να επιτευχθεί ένα ισορροπημένο φόρτο εργασίας καθ όλη τη διάρκεια της εκτέλεσης και κάθε τμήμα του αλγορίθμου να αντιστοιχηθεί με την κατάλληλη αρχιτεκτονική λαμβάνοντας υπόψη τις δυνατότητες της κάθε μίας και τις απαιτήσεις των διαμοιραζομένων μερών. Γενικά δεν υπάρχει κάποιος κανόνας που να δείχνει πως πρέπει να γίνει η κατάτμηση και ο διαμοιρασμός λόγω της πολυπλοκότητας του θέματος. Παρόλα αυτά έχουν γίνει κάποιες προσπάθειες που αναφέρονται παρακάτω [4]. Ένας τρόπος παραγωγής DLA αλγορίθμων υψηλά παραλληλοποιήσιμων είναι ο διαχωρισμός των υπολογισμών σε διαδικασίες με βάση τις όποιες εξαρτήσεις υπάρχουν μεταξύ τους. Έτσι το πρόβλημα ανάγεται στην αναπαράσταση των αλγορίθμων με κατευθυνόμενους ακυκλικούς γράφους (Directed Acyclic Graphs - DAGs), όπου οι κόμβοι αντιστοιχούν στις διαδικασίες και οι ακμές στις εξαρτήσεις. 25

Ο σχεδιασμός αλγορίθμων με υψηλό λόγο πράξεων κινητής υποδιαστολής προς τον όγκο των απαιτούμενων δεδομένων είναι ένα θέμα που απασχολεί τις ερευνητικές ομάδες ανάπτυξης DLA αλγορίθμων στις μέρες μας. Ένα κλασικό παράδειγμα είναι η σταδιακή μετάβαση από τους βελτιστοποιημένους αλγόριθμους του Level - 1 BLAS (που περιέχονται στις βιβλιοθήκες LINPACK και EISPACK) σε αλγορίθμους DLA που εκτελούν πράξεις μεταξύ πινάκων, κάτι που διαμόρφωσε τη φιλοσοφία σχεδιασμού της LAPACK. Ο διαχωρισμός των αλγορίθμων σε επιμέρους διαδικασίες, είτε για υβριδικά συστήματα είτε για απλά, δημιουργεί την ανάγκη του σωστού προγραμματισμού εκτέλεσης των διαδικασιών αυτών για την αποτελεσματική περάτωση, εν τέλει, του αλγορίθμου. Η ιεραρχία των διαδικασιών, εάν δεν αθετείται η μεταξύ τους εξάρτηση, δεν χρήζει κάποιας σημαντικής ανακατάταξης. Σημαντικά οφέλη όμως μπορεί να προκύψουν εάν μέσω του προγραμματισμού εξασφαλισθεί υψηλότερος βαθμός ασύγχρονης εκμετάλλευσης των αρχιτεκτονικών ώστε λιγότερο αποδοτικές διαδικασίες να επικαλυφθούν από άλλες με μεγαλύτερη απόδοση. 26

Κεφάλαιο 4 Παραγοντοποίηση QR 4.1 Εισαγωγή Το QR factorization ενός πίνακα είναι η παραγοντοποίηση ενός πίνακα A R mxn σε έναν ορθογώνιο πίνακα Q R mxm κι έναν άνω τριγωνικό πίνακα R R mxn τέτοιοι ώστε o A να ισούται με το γινόμενο του Q επί του R (A = QR). Η παραγοντοποίηση QR χρησιμοποιείται στην επίλυση του προβλήματος των ελαχίστων τετραγώνων και σε μεθόδους υπολογισμού των ιδιοτιμών ενός πίνακα. Υπάρχουν διάφοροι αλγόριθμοι για τον υπολογισμό της παραγοντοποίησης QR, όπως η διαδικασία Gram - Schmidt, οι μετασχηματισμοί Householder και οι περιστροφές Givens. Κάθε ένας παρουσιάζει τα δικά του πλεονεκτήματα και μειονεκτήματα. Gram - Schmidt Είναι μία μέθοδος ορθοκανονικοποίησης ενός συνόλου διανυσμάτων σε έναν χώρο εσωτερικού γινομένου, συνήθως στον Ευκλείδειο χώρο R n. Είσοδος στη διαδικασία είναι ένα σύνολο γραμμικά ανεξάρτητων διανυσμάτων S = v 1,..., v k για k n και παράγει ένα ορθογώνιο σύνολο S = u 1,..., u k που ανήκει στον υποχώρο του R n διαστάσεως k όπως το σύνολο S. Το κύριο μειονέκτημα της μεθόδου είναι ότι τα διανύσματα u k δεν προκύπτουν ακριβώς ορθογώνια, κάτι που οφείλεται σε σφάλματα στρογγυλοποίησης. Εξαιτίας αυτής της απώλειας σε ορθογωνικότητα η μέθοδος αυτή χαρακτηρίζεται αριθμητικά ασταθής. Το μειονέκτημα αυτό αίρεται εάν χρησιμοποιηθεί ο τροποποιημένος Gram - Schmidt αλγόριθμος ο οποίος όμως υπολογίζει αναδρομικά τα διάνυσμα u k (κάτι που μειώνει την απόδοση στις CPU και πολλές φορές καθιστά απαγορευτική την υλοποίηση σε GPU). 27

Μετασχηματισμοί Householder Η διαδικασία αυτή έχει σαν είσοδο ένα διάνυσμα το οποίο ανακλάται ως προς κάποιο επίπεδο. Μείζων πλεονέκτημα αποτελεί η αριθμητική σταθερότητα του αλγορίθμου και η σχετική ευκολία υλοποίησης αν και δεν προσφέρεται για κλασική παράλληλη υλοποίηση. Εκτενέστερη ανάλυση του αλγορίθμου ακολουθεί παρακάτω. Περιστροφές Givens Η παραγοντοποίηση QR μπορεί επίσης να υπολογιστεί με μία σειρά διαδοχικών περιστροφών Givens, που λαμβάνουν χώρα σε ένα επίπεδο το οποίο ορίζεται από δυο άξονες συντεταγμένων. Κάθε περιστροφή μηδενίζει ένα στοιχείο σε μία υποδιαγώνιο του πίνακα διαμορφώνοντας σιγά σιγά τον παράγοντα R. Η αλληλουχία των περιστροφών Givens για όλα τα στοιχεία κάθε υποδιαγωνίου σχηματίζει τον ορθογώνιο πίνακα Q. Πρακτικά οι περιστροφές Givens δεν εκτελούνται σχηματίζοντας έναν ολόκληρο πίνακα και κάνοντας έναν πολλαπλασιασμό πινάκων. Έτσι μια διαδικασία Givens χρησιμοποιείται για τον υπολογισμό της παραγοντοποίησης QR ενός πολύ αραιού πίνακα χωρίς την ανάγκη της επιπλέον επεξεργασίας των αραιών στοιχείων. Το πλεονέκτημά της, πέρα από τη χρήση της στους αραιούς πίνακες, είναι η υψηλή δυνατότητα παραλληλοποίησης, ενώ ένα μειονέκτημα είναι ο υψηλότερος αριθμός πράξεων που απαιτούνται έναντι της μεθόδου Householder. 4.2 Η γεωμετρία των μετασχηματισμών Είναι χρήσιμο να εξετάσουμε την σχετική γεωμετρία με περιστροφές και ανακλάσεις σε ένα δισδιάστατο επίπεδο. Ένας ορθογώνιος πίνακας Q 2x2 είναι μία περιστροφή εάν έχει τη μορφή Q = cos(θ) sin(θ) sin(θ) cos(θ) Εάν y = Q T x, τότε το y παράγεται περιστρέφοντας το x κατά μία γωνία θ αντίθετα με τη φορά του ρολογιού. Ένας 2x2 ορθογώνιος πίνακας Q ορίζεται ως ανάκλαση εάν έχει τη μορφή Q = cos(θ) sin(θ) sin(θ) cos(θ) 28

Εάν y = Q T x = Qx, τότε το y παράγεται ανακλώντας το διάνυσμα x κατά μήκος της γραμμής που ορίζεται από S = span cos(θ/2) sin(θ/2) Οι ανακλάσεις και οι περιστροφές είναι αριθμητικά χρήσιμες επειδή κατασκευάζονται με ευκολία και μπορούν να χρησιμοποιηθούν για να εισάγουν μηδενικά σε ένα διάνυσμα ορίζοντας κατάλληλα τη γωνία περιστροφής ή το επίπεδο ανάκλασης. 4.2.1 Μετασχηματισμός Householder Για ένα μη μηδενικό διάνυσμα u R n κάθε nxn πίνακας P της μορφής P = I 2 u T u uut ονομάζεται πίνακας Householder και το διάνυσμα u διάνυσμα Householder. Ένας τέτοιος πίνακας είναι ορθογώνιος και συμμετρικός με τις παρακάτω ιδιότητες: Είναι Ερμιτιανός: P = P και ορθομοναδιαίος: P 1 = P Πολλαπλασιαζόμενος με ένα διάνυσμα x παράγει την ανάκλαση αυτού ως προς το υπερεπίπεδο που ορίζεται από το διάνυσμα u. Ας υποθέσουμε ότι δίνεται μη μηδενικό διάνυσμα x R n και θέλουμε το γινόμενο P x να είναι πολλαπλάσιο του διανύσματος e 1 = (1, 0,..., 0) T. Λαμβάνοντας υπόψη ότι P x = (I 2uuT 2ux )x = x u T u u T u u και P x span{e 1 }, u span{x, e 1 }. Θέτοντας u = x + αe 1 προκύπτει u T x = x T x + ax 1 u T u = x T x + 2αx 1 + α 2 Αντικαθιστώντας, έχουμε x T x + αx 1 P x = (1 2 x T x + 2αx 1 + α )x x 2 2uT u T u e 1. 29

Για να προκύψει ο συντελεστής του x μηδέν θέτουμε α = ± x 2. Τελικά u = x ± x 2 e 1 P x = (I 2 uut u T u )x = x 2e 1. Από τη μορφή αυτή του διανύσματος u γίνεται αντιληπτή και η χρησιμότητα των μετασχηματισμών Householder. 4.2.2 Υπολογισμός του διανύσματος Householder Κανονικοποιώντας το διάνυσμα u (u(1) = 1) και θέτοντας β = 2/u T u έτσι ώστε P = I n βuu T και P x = x 2 e 1 προκύπτει ο παρακάτω αλγόριθμος υπολογισμού των u και β [3]. function [ u b ] = house ( x ) % computes the normalized Householder vector u and the % scalar factor b such that H=I-b*u*u and H*x =[ norm(x) 0... 0] [n]= size (x,1); if(n ==1) s =0; u =1; else s=x (2: n) *x (2: n); u =[1; x (2: n )]; end if(s ==0) b =0; else m= sqrt (x (1)^2+ s); if(x (1) <=0) u (1)= x(1) -m; else u(1)= -s/(x (1)+ m); end b =2*( u (1)^2)/( s+u (1)^2); u=u/u (1); end Αυτός ο αλγόριθμος απαιτεί 3n flops. Εκμεταλλευόμενοι τη δομή των πινάκων Householder μπορούμε να μειώσουμε τον αριθμό των πράξεων που απαιτούνται για τον υπολογισμό των μετασχηματισμών. Εάν A R mxn και 30

P = I βuu T R mxm, τότε P A = (I βuu T )A = A uw T όπου w = βa T u. Έτσι ένας mxn μετασχηματισμός Householder απαιτεί έναν πολλαπλασιασμό πίνακα με διάνυσμα και τον υπολογισμό ενός εξωτερικού γινομένου συνολικού κόστους 4mn flops. Με αυτήν την υλοποίηση ο υπολογιστικός φόρτος μειώνεται κατά μία τάξη μεγέθους. Οι μετασχηματισμοί Householder δεν απαιτούν τον ακριβή σχηματισμό του ομώνυμου πίνακα. Περαιτέρω βελτίωση προκύπτει από την εκμετάλλευση της κανονικοποίησης του διανύσματος u, όπου το πρώτο στοιχείο είναι μοναδιαίο. 4.3 Householder QR Η παραγοντοποίηση QR ενός mxn πίνακα A είναι A = QR όπου ο πίνακας Q R mxm ειναι ορθογώνιος και ο R R mxn άνω τριγωνικός. Η περίπτωση που εξετάζουμε είναι για m n.εάν ο A είναι πλήρους τάξης τότε οι πρώτες n στήλες του Q διαμορφώνουν μία ορθοκανονική βάση για το range space του A(ran(A)). Η παραγοντοποίηση QR χρησιμοποιείται για την επίλυση του προβλήματος των ελαχίστων τετραγώνων και είναι η βάση για διαδικασίες υπολογισμού ιδιοτιμών. Στην περίπτωση που ο A είναι αντιστρέψιμος η παραγοντοποίηση είναι μοναδική και τα στοιχεία της κύριας διαγωνίου του R είναι θετικά. Γενικότερα η παραγοντοποίηση μπορεί να αναπαρασταθεί και ως A = QR = Q R 1 = [Q 1, Q 2 ] R 1 = Q 1 R 1 0 0 όπου R 1 είναι nxn άνω τριγωνικός, ο Q 1 είναι mxn και ο Q 2 mx(m-n). Η αναπαράσταση αυτή συνηθίζεται να αποκαλείται thin QR factorization του A. Από το παρακάτω παράδειγμα δείχνουμε την παραγοντοποίηση QR μέσω των μετασχηματισμών Householder. 31

Έστω m = 4 και n = 3 και ότι οι πίνακες Householder H 1 και H 2 έχουν υπολογισθεί έτσι ώστε x x x 0 x x H 2 H 1 A = 0 0 x 0 0 x Υπολογίζοντας τον πίνακα Householder H 3 R 2x2 για τα εναπομείναντα στοιχεία της τρίτης στήλης ώστε H 3 x = x x 0 και λαμβάνοντας υπόψη ότι H 3 = diag(i 2, H 3 ) τότε x x x 0 x x H 3 H 2 H 1 A = 0 0 x 0 0 0 Στη γενική περίπτωση μετά από n βήματα θα έχει σχηματιστεί ο άνω τριγωνικός H n H n 1... H 1 A = R και θέτοντας Q = H 1... H n παίρνουμε την τελική παραγοντοποίηση A = QR. Ένας γενικός αλγόριθμος παραγοντοποίησης δίνεται παρακάτω σε MATLAB notation, όπου ο A είναι mxn ο R είναι άνω τριγωνικός και Q = H 1... H n. Ο αλγόριθμος επιστρέφει στο άνω τριγωνικό τμήμα του A τον R και το j διάνυσμα Householder αποθηκεύεται στη θέση A(j + 1 : m, j), j m. function [A] = QRH (A) % computes the QR factorization of matrix A. The upper % triangular part of A is overwritten by the upper triangular % part of R. The j Householder vector is stored in A(j +1:m,j) [m n]= size (A); for j =1: n [u b]= house (A(j:m,j )); A(j:m,j:n)=A(j:m,j:n)-b*u*(u *A(j:m,j:n )); if(j<m) 32

end end A(j +1:m,j)=u(2:m-j +1); Ο αλγόριθμος απαιτεί 2n 2 (m n/3) flops. Ο A που επιστρέφεται είναι r 11 r 12 r 13 u (1) A = 2 r 22 r 23 u (1) 3 u (2) 3 r 33 u (1) 4 u (2) 4 u (3) 4 όπου u (j) το j διάνυσμα Householder. Το τελικό Q μπορει να ανακτηθεί από τα διανύσματα u με κατάλληλους πολλαπλασιασμούς. 4.3.1 Block Householder Για να αποφύγουμε τους υπολογισμούς επιπέδου-2 (level - 2), δηλαδή πολλαπλασιασμούς πίνακα επί διάνυσμα και υπολογισμούς εξωτερικών γινομένων μπορούμε να αναδιατάξουμε τους υπολογισμούς ώστε να προκύπτουν πολλαπλασιασμοί μεταξύ πινάκων (level - 3). Με αυτόν τον τρόπο ο αλγόριθμος καθίσταται περισσότερο computational intensive, κάτι που είναι καθοριστικό για την υψηλή απόδοση του αλγορίθμου στις σύγχρονες κάρτες γραφικών, όπως αναφέρθηκε σε προηγούμενο κεφάλαιο. Αυτή η υλοποίηση του αλγορίθμου ονομάζεται Block Householder και βασίζεται στην αναπαράσταση των μετασχηματισμών Householder με τη μορφή W Y. Σύμφωνα με αυτήν, ο πίνακας Q = H 1... H n ισούται με Q = I + W Y T, όπου οι πίνακες W και Y υπολογίζονται από τον παρακάτω αλγόριθμο [3] function [W Y]= wy(a,r) % computes the block representation of Householder % matrices such that Q=I+W*Y [m n]= size (A); [u b]= house (A (1:m,1)); A (1:m,1: n)=a(1:m,1: n)-b*u*(u *A(1:m,1: n )); Y=u; W=-b*u; 33

for j =2: r u= zeros (m,1); [u(j:m) b]= house (A(j:m,j )); A(j:m,j:n)=A(j:m,j:n)-b*u(j:m )*( u(j:m) *A(j:m,j:n )); z=-b*( eye (m)+w*y )* u; W=[W z]; Y=[Y u]; end Η όλη διαδικασία εξηγείται μέσω του παρακάτω παραδείγματος. Θεωρούμε έναν πίνακα A με n = 6 κι επιλέγουμε την παράμετρο nb = 3 (αριθμός στηλών για κάθε μπλοκ - υποπίνακα). Αφού υπολογίσουμε τους πίνακες Householder H 1, H 2 και H 3 αντί να τους πολλαπλασιάσουμε με ολόκληρο τον πίνακα A τους πολλαπλασιάζουμε μόνο με το πρώτο μπλοκ (A(:, 1 : 3)). Στη συνέχεια δημιουργούμε την W Y αναπαράσταση του γινομένου των τριών πινάκων H 1 H 2 H 3 = I + W 1 Y T 1 κι ενημερώνουμε με αυτήν τις υπόλοιπες στήλες του πίνακα (level - 3 υπολογισμός) A(:, 4 : 6) = (I + W 1 Y T 1 )A(:, 4 : 6) Η ίδια διαδικασία επαναλαμβάνεται διαδοχικά για τα υπόλοιπα μπλοκ του πίνακα. Λόγω των μηδενικών που περιέχονται στα διανύσματα Householder συνεπάγεται ότι και οι πρώτες k + nb 1 σειρές των πινάκων W k Y k θα είναι μηδέν, κάτι που μπορούμε να το εκμεταλλευτούμε στους πολλαπλασιασμούς των πινάκων. Ο αλγόριθμος χωρίζει των πίνακα A σε N επιμέρους υποπίνακες (block) A = [A 1,..., A N ] N = ceil(n/nb) και τον επεξεργάζεται σε N διαδοχικά υποβήματα. Στο k-στο βήμα μηδενίζονται τα στοιχεία του k υποπίνακα που βρίσκονται κάτω από την κύρια διαγώνιο κι έπειτα ενημερώνονται τα στοιχεία των υπόλοιπων μπλοκ του A από τους πίνακες W k Y k. 34

4.4 Παραγοντοποίηση QR συστοιχίας πινάκων Σε διαδικασίες όπως χωροχρονικής προσαρμογής (STAP - Space Time Adaptive Processing) και σε συστήματα επισκόπησης που απαιτούν την παραγοντοποίηση μιας συστοιχίας πινάκων μπορούμε να εκμεταλλευτούμε τη συσχέτιση των στοιχείων μεταξύ διαδοχικών πινάκων [1]. Ο αλγόριθμος που υλοποιήσαμε λαμβάνει υπόψη αυτόν τον πλεονασμό μειώνοντας τις αριθμητικές πράξεις και το κόστος της διαδικασίας ενώ ταυτόχρονα εκμεταλλεύεται την δυνατότητα παραλληλοποίησης των διαδοχικών παραγοντοποίησεων που προκύπτει από τον πλεονασμό. Η υλοποίηση στην GPU ανταποκρίνεται στην απαίτηση της υψηλής παραλληλισιμότητας μειώνοντας το χρόνο επεξεργασίας κι επιτρέποντας την real - time επεξεργασία των δεδομένων στις εφαρμογές που αυτό απαιτείται. Σχήμα 4.1: Παράλληλη υλοποίηση QR [1]. Έστω συστοιχία P διαδοχικών πινάκων, διαστάσεων mxn ο καθένας. Για κάθε k 1, ο πίνακας A k+1, μπορεί να θεωρηθεί ως η ολίσθηση του πίνακα A k όπου η τελευταία σειρά περιέχει τα πλέον πρόσφατα στοιχεία. Δηλαδή A k+1 = SA k + e m (d new a 1 ) T όπου ο πίνακας S είναι ο πίνακας κυκλικής ολίσθησης 0 1 0... 0. 0 0 1.. 0 S =... 0.. 0 0 0... 0 1 1 0 0... 0 35

e m = (0,..., 1) T, d new είναι τα καινούργια δεδομένα που τοποθετούνται στην τελευταία γραμμή του A k+1 και a 1 η πρώτη γραμμή του A k η οποία απορρίπτεται. Μία μη αποδοτική προσέγγιση θα ήταν να αγνοήσουμε την συσχέτιση των πινάκων και να εφαρμόσουμε την παραγοντοποίηση σε κάθε πίνακα ξεχωριστά ή και σειριακά. Αντίθετα χρησιμοποιώντας τα κοινά στοιχεία των πινάκων εκτελούμαι την παραγοντοποίηση αποτελεσματικά και αποδοτικά. Συγκεκριμένα αντιμετωπίζουμε κάθε P πίνακες, 1 P m, σαν ένα μπλοκ πινάκων όπου οι σειρές που είναι κοινές σε όλους τους πίνακες είναι m P + 1. Αυτές οι σειρές αντιστοιχούν στον υποπίνακα A c του πρώτου πίνακα του μπλοκ, δηλαδή A c = A 1 (p : m, 1 : n). Συνεπώς για τον σχηματισμό του δεν είναι απαραίτητη η γνώση των νέων στοιχείων των υπόλοιπων πινάκων του μπλοκ [1]. Για την υλοποίηση της παραγοντοποίησης διακρίνουμε δύο βασικά βήματα Σχήμα 4.2: Σχηματισμός και παραγοντοποίηση κοινού πίνακα [1]. Αρχικά παραγοντοποιούμε τον κοινό πίνακα A c, A c = Q c R c κι εξάγουμε τον άνω τριγωνικό πίνακα R c. Ο πίνακας R c είναι της μορφής R c = R ch n (παράγοντας Cholesky) 0 m P + 1 n Στη συνέχεια υπολογίζουμε τους άνω τριγωνικούς πίνακες R k χρησιμοποιώντας τον κοινό R c για κάθε πίνακα του μπλοκ. Συγκεκριμένα ο παράγοντας R 1 του πίνακα A 1 υπολο- 36

γίζεται από τον πίνακα [A 1 (1 : P 1, 1 : n); R c ]. Όμοια ο επόμενος παράγοντας R 2 υπολογίζεται από τον ημιεπεξεργασμένο πίνακα [A 1 (2 : P 1, 1 : n); A 2 (m, 1 : n); R c ]. Κάτι που συνεπάγεται μία σημαντική μείωση στις αριθμητικές πράξεις σε σχέση με την έναρξη της παραγοντοποίησης από την αρχή. Επιπροσθέτως ο υπολογισμός του R 2 μπορεί να αρχίσει αμέσως μόλις γίνουν διαθέσιμα τα νέα δεδομένα χωρίς να υπάρχει η ανάγκη αναμονής για την ολοκλήρωση της παραγοντοποίησης του A 1. Γενικά ο R k εξάγεται από τον πίνακα [A k (m : 1 : m k + 2, 1 : n); A 1 (k : P 1, 1 : n); R c ] [1]. Ο υπολογισμός του R k μπορεί να ξεκινήσει αμέσως μόλις γίνει διαθέσιμη η τελευταία σειρά του πίνακα A k. Με όρους αριθμητικής πολυπλοκότητας το βέλτιστο μέγεθος του μπλοκ είναι περίπου P = m. Σε αυτήν την περίπτωση ο μέγιστος αριθμός των αριθμητικών πράξεων ανά παραγοντοποίηση είναι γ mn 2 [1]. Ο αλγόριθμος εκμεταλλεύεται τη δυνατότητα συγχρονισμού ανάμεσα στις διαδοχικές παραγοντοποιήσεις και δίνει τη δυνατότητα χρησιμοποίησης είτε περιστροφών Givens είτε ανακλάσεων Householder. Η υλοποίηση για κάθε GPU μπορεί να διαφέρει σε σχέση με τους τρόπους αποθήκευσης των δεδομένων και οργάνωσης και προγραμματισμού των πράξεων. Αρχικά ας αναλογιστούμε τον υπολογισμό P διαδοχικών πινάκων μόνο με περιστροφές Givens. Υπάρχουν πολλοί διαφορετικοί τρόποι για να διατάξουμε τις περιστροφές κάθε παραγοντοποίησης. Ο υπολογισμός του κοινού παράγοντα R c, αντιστοιχεί σε έναν αριθμό διαφορετικών περιστροφών Givens για κάθε πίνακα ξεχωριστά. Αυτή η κοινή παραγοντοποίηση γλιτώνει P 1 ίδιες παραγοντοποιήσεις. Στη συνέχεια ας θεωρήσουμε την παραγοντοποίηση μόνο με ανακλάσεις Householder. Η παραγοντοποίηση του κοινού R c συνεπάγεται μία επιπλέον κατάτμηση στο μέγεθος του μετασχηματισμού Householder για τον κάθε πίνακα ξεχωριστά σε σχέση με τον βασικό αλγόριθμο παραγοντοποίησης QR με χρήση ανακλάσεων Householder. Η θέση της κατάτμησης ολισθαίνει από τον έναν πίνακα στον επόμενο. Για τον R 1 η επιπλέον κατάτμηση απαιτεί λίγες παραπάνω πράξεις σε σχέση με τον κλασικό αλγόριθμο ανακλάσεων Householder. Παρόλα αυτά εξοικονομεί P 1 παραγοντοποιήσεις για κάθε πίνακα στους επόμενους P 1 πίνακες του μπλοκ. Στην περίπτωση που όλα τα δεδομένα είναι διαθέσιμα οποιαδήποτε υποψία σειριακής εξάρτησης ανάμεσα στις διαδοχικές παραγοντοποιήσεις των πινάκων αίρεται. Επιπλέον της εξάρτησης της διαθεσιμότητας των στοιχείων ο αλγόριθμος πρέπει να συνυπολογίζει τις δυνατότητες και τους περιορισμούς της αρχιτεκτονικής του εκάστοτε συστήματος (υλικό και λογισμικό). 37

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

Κεφάλαιο 5 Υλοποίηση σε CUDA Οι κώδικες που περιγράφονται παρακάτω αποτελούν την υλοποίηση μιας νέας προσέγγισης στην παραγοντοποίηση διαδοχικών πινάκων με χρήση GPU. Αποτελούν τον συνδυασμό δύο state of the art αλγορίθμων ο καθένας από τους οποίους αντιμετωπίζει ένα ξεχωριστό πρόβλημα. Ο πρώτος την παραγοντοποίηση QR συστοιχίας πινάκων μέσω του αλγορίθμου που περιγράφηκε παραπάνω. Εκμεταλλεύεται αποτελεσματικά και αποδοτικά τον πλεονασμό των στοιχείων μεταξύ διαδοχικών πινάκων ενός μπλοκ κι επιτρέπει τον παραλληλισμό των παραγοντοποίησεων του κάθε πίνακα. Ο δεύτερος (υλοποίηση του V.Volkov που αναφέρθηκε παραπάνω) υπολογίζει την παραγοντοποίηση ενός πίνακα υλοποιώντας μια υβριδική τεχνική παράλληλης χρήσης GPU και CPU. Εκμεταλλεύεται τις δυνατότητες της κάρτας γραφικών για πολύ γρήγορους πολλαπλασιασμούς και με μία έξυπνη κατάτμηση του προβλήματος μεταφέρει την εκτέλεση μη βέλτιστων αλγορίθμων για την GPU στην CPU. Η βασική δομή του κώδικα είναι η ακόλουθη 1. Προσδιορίζονται τα δεδομένα εισόδου και τα μεγέθη των μεταβλητών και των πινάκων. 2. Δεσμεύεται η απαραίτητη μνήμη στην CPU και GPU. 3. Αρχικοποιούνται τα δεδομένα. 4. Μεταφορά των δεδομένων στην GPU. 5. Εκτελούνται παράλληλα οι απαραίτητες πράξεις στην GPU (kernel) και CPU. 6. Μεταφέρονται τα δεδομένα πίσω στην CPU. 7. Αποδέσμευση δεσμευμένης μνήμης. 39

Βοηθητικές συναρτήσεις gpu_lapack_init( ): Δεσμεύει την απαραίτητη μνήμη για την αποθήκευση του πίνακα ανάκλασης T που χρησιμοποιείται στην slarft( ). Έπειτα δεσμεύει με την εντολή cudamalloc( ) ολόκληρη τη μνήμη της κάρτας γραφικών. memcpthread( ): Συνάρτηση που εκτελείται από κάθε νήμα για παράλληλη αντιγραφή πινάκων. gpu_malloc2d(int m, int n ): Δεσμεύει συνεχόμενες θέσεις μνήμης στη μνήμη της κάρτας γραφικών. slarft( ): Υπολογίζει τον πίνακα T διάστασης nbxnb, που είναι απαραίτητος για την WY αναπαράσταση των διανυσμάτων Householder. Βοηθητικές μεταβλητές Όσες από τις μεταβλητές έχουν πρόθεμα gpu_ & cpu_ αναφέρονται σε θέσεις μνήμης της κάρτας γραφικών και RAM αντίστοιχα. A_common, A_stacted, A: Ο A_common περιέχει τα κοινά στοιχεία όλων των πινάκων του μπλοκ. Ο A_stacted περιέχει τα στοιχεία όλων των πινάκων του μπλοκ και ο A περιέχει ανεπτυγμένα τα στοιχεία των πινάκων έπειτα από την τριγωνοποίηση του κοινού πίνακα A_common. Οι πίνακες αποθηκεύονται κατά στήλη και στο τέλος του κάθε πίνακα γίνεται padding με μηδενικά ώστε να είναι βέλτιστη η προσπέλαση στη μνήμη. Το τελικό μήκος του πίνακα δίνεται κάθε φορά από τις μεταβλητές lda (leading dimension array) P: Ο αριθμός των πινάκων του μπλοκ. n, c: Ορίζουν το μέγεθος του παραθύρου nxc. n_common, n_stacted: Ο αριθμός των γραμμών των πινάκων A_common και A_stacted ( n-p+1 και n+p-1 αντίστοιχα). matrix( ): Αντικείμενο της κλάσης random_matrix_t η οποία περιέχει μεταβλητές και συναρτήσεις που επιτρέπουν την δημιουργία ενός τυχαίου πίνακα. struct p2_t: Δομή που μεταξύ άλλων αποτελείται από έναν πίνακα A και μία μεταβλητή lda. 40

gpu_matrix: Αντικείμενο της δομής p2_t που περιέχει τον pointer που δείχνει στη θέση μνήμης της κάρτας γραφικών που αντιστοιχεί στον πίνακα A. gpu_[tv][tva][t]: Αντικείμενα της δομής p2_t που χρησιμοποιούνται ως ενδιάμεσοι πίνακες στους απαραίτητους πολλαπλασιασμούς για την παραγοντοποίηση. cpu_[matrix][t]_all: Μεταβλητές τύπου p2_t που δείχνουν στις θέσεις μνήμης του A και του πίνακα ανάκλασης T. Συνάρτηση main( ) Αρχικοποιούμε την κάρτα γραφικών με την εντολή cudasetdevice( ) κι ελέγχουμε εάν είναι δυνατή η δέσμευσή της. Στη συνέχεια καλείται η συνάρτηση gpu_lapack_init(). Δεσμεύεται μνήμη για τους πίνακες A, A_common και A_stacted. Η συνάρτηση της LAPACK που υπολογίζει την παραγοντοποίηση QR είναι η SGEQRF. Αρχικά πρέπει να υπολογισθεί το μέγεθος του buffer από την ίδια τη συνάρτηση, για το συγκεκριμένο μέγεθος προβλήματος. Αυτό γίνεται θέτοντας τη μεταβλητή lwork=-1 και καλώντας την ως εξής: sgeqrf( &n, &nb, A, &lda, A, &work, &lwork, &info) Έπειτα ορίζουμε τον A_common και υπολογίζεται η παραγοντοποίηση QR μέσω της gpu_sgeqrf (αναλύεται παρακάτω). gpu_sgeqrf( n_common, c, A_common, lda_common, tau, work, lwork, info, 1) Στο πάνω μέρος του πίνακα A_common επιστρέφεται ο άνω τριγωνικός παράγοντας R_common. Στη συνέχεια σχηματίζονται οι πίνακες του μπλοκ όπου τα κοινά σε όλους στοιχεία αντικαθιστώνται από τον R_common. Στις πρώτες σειρές του κάθε πίνακα τοποθετούνται τα υπόλοιπα στοιχεία του καθενός. Οι πίνακες αυτοί αποθηκεύονται σε διαδοχικές θέσεις στον υπερ-πίνακα A. Για την επιτάχυνση της διαδικασίας δημιουργούνται P νήματα (threads), και κάθε ένα εκτελεί την παρακάτω διαδικασία αντιγραφής που περιέχεται στην memcpthread( ) 41

for ( int j = 0; j < a->c ; j++ ) { memcpy (A+a-> index *a-> lda *a->c+j*a-> lda +a->n-a-> n_common, A_common +j*a-> lda_common,(j +1)* sizeof ( float )); for ( int i = 0; i < a->n-a-> n_common ; i++ ) { if( i >= a-> index ) A[ a-> index *a-> lda *a->c + i + j*a-> lda ] = A_stacted [ i + j*a-> lda_stacted ]; else A[ a-> index *a-> lda *a->c + i + j*a-> lda ] = A_stacted [ a->n + i + j*a-> lda_stacted ]; } } Τέλος καλείται και πάλι η gpu_sgeqrf έχοντας ως όρισμα τον υπερ-πίνακα A και υπολογίζει παράλληλα τις παραγοντοποιήσεις όλων των πινάκων εκμεταλλευόμενη την ύπαρξη του ήδη παραγοντοποιημένου κοινού πίνακα gpu_sgeqrf( n, c, A, lda, tau, work, lwork, info, P) Με την επιστροφή της συνάρτησης οι παραγοντοποιημένοι πίνακες εμπεριέχονται στον A και η συνάρτηση main( ) ελευθερώνει τη δεσμευμένη μνήμη σε host και device και τερματίζει. Συνάρτηση gpu_sgeqrf( ) Αρχικά δεσμεύεται μνήμη μέσω της συνάρτησης gpu_malloc2d( ) για τις μεταβλητές gpu_matrix,gpu_tv,gpu_tva και gpu_t, και αρχικοποιούνται οι μεταβλητές cpu_*. Έπειτα μεταφέρεται στην κάρτα γραφικών ο πίνακας A (η upload( ) είναι μία βοηθητική συνάρτηση που καλεί την cudamemcpy2d( )). for ( int j = 0; j < P; j ++ ) upload (n,c-nb, gpu_matrix (0, j*c+nb), CPU_matrix [j](0, nb )); Στην συνέχεια έχουμε την εκτέλεση ενός βρόχου με c/nb βήματα όπου σε κάθε βήμα παραγοντοποιούνται nb διαδοχικές στήλες κάθε πίνακα του μπλοκ. Πιο συγκεκριμένα σε κάθε βήμα για κάθε μπλοκ στηλών υπολογίζεται η παραγοντοποίηση τους στην CPU. Η παραγοντοποίηση γίνεται ταυτόχρονα για κάθε πίνακα με τη χρήση P pthreads τα οποία καλούν την 42

sgeqrf( ). Στη συνέχεια χρησιμοποιούνται τα διανύσματα Householder που έχουν επιστραφεί και μεταφέρονται στην GPU για να γίνουν οι πολλαπλασιασμοί που θα ανανεώσουν, αρχικά, τις επόμενες nb στήλες. Με το πέρας της ανανέωσης οι στήλες αυτές μεταφέρονται και πάλι στην CPU και υπολογίζεται το QR τους με τον ίδιο τρόπο, ενώ παράλληλα και ταυτόχρονα γίνονται, στην GPU, οι πολλαπλασιασμοί ανανέωσης όλων των υπόλοιπων στηλών. Οι παραπάνω πολλαπλασιασμοί γίνονται για όλους τους πίνακες με κλίση ενός και μόνο kernel κι εκτελούνται παράλληλα εκμεταλλευόμενοι πλήρως τις δυνατότητες της GPU. Η διαδικασία της παραγοντοποίησης εκτελείται μέσω της χρήσης ενός block QR αλγορίθμου, αντίστοιχου με αυτού που περιγράφηκε παραπάνω. Αρχικά υπολογίζονται τα διανύσματα Householder με την sgeqrf( ) και στη συνέχεια ο πίνακας Q αναπαρίσταται με τη μορφή Q = I V T V T, η οποία είναι ένα είδος WY αναπαράστασης που απαιτεί λιγότερο χώρο μνήμης [13], μέσω της κλίσης της συνάρτησης slarft( ). Οι πίνακες V (Ο V αποτελείται από τα διανύσματα Householder που αποθηκεύονται από την sgeqrf( ) στις θέσεις κάτω από την κύρια διαγώνιο του gpu_matrix) και T που υπολογίστηκαν μεταφέρονται στην GPU στους πίνακες gpu_matrix και gpu_t. Έπειτα υπολογίζεται το γινόμενο T V T και αποθηκεύεται στην θέση gpu_tv. Οπότε γίνεται ο πολλαπλασιασμός του T V T με τις επόμενες nb στήλες του A και αποθηκεύεται στον ενδιάμεσο πίνακα gpu_tva, ο οποίος στη συνέχεια πολλαπλασιάζεται με τις στήλες του gpu_matrix που περιέχουν τα διανύσματα V. Το αποτέλεσμα αποθηκεύεται στις επόμενες nb στήλες του gpu_matrix και μεταφέρονται στην CPU για το επόμενο βήμα. Ταυτόχρονα με την έναρξη του επόμενου βήματος (που αντιστοιχεί στην παραγοντοποίηση QR των επόμενων nb στηλών από την sgeqrf( ) ) εκτελούνται στην GPU οι απαραίτητες πράξεις (πολλαπλασιασμοί και μία αφαίρεση) για την ανανέωση των υπόλοιπων στοιχείων του πίνακα gpu_matrix. Σε αυτήν την υλοποίηση κρύβονται δυο σημαντικά χαρακτηριστικά, άξια αναφοράς, που βελτιώνουν την απόδοσή της. Πρώτον, η ανανέωση των στοιχείων του πίνακα σε δύο στάδια δίνει τη δυνατότητα της ταυτόχρονης χρήσης CPU και GPU (χαρακτηριστικό υβριδικότητας). Το άλλο χαρακτηριστικό έχει να κάνει με την προτεραιότητα των πολλαπλασιασμών για την ενημέρωση του πίνακα. Συγκεκριμένα, ο υπολογισμός της παράστασης A V ((T V T )A) εμπλέκει, προφανώς, πράξεις με πίνακες μικρότερων διαστάσεων από το κατευθείαν γινόμενο (I V T V T )A κάτι που συνεπάγεται λιγότερο υπολογιστικό κόστος. Επιπλέον το αποτέλεσμα αποθηκεύεται κατευθείαν στην σωστή θέση του gpu_matrix χωρίς την ανάγκη χρήσης ενδιάμεσου βοηθητικού πίνακα. 43

Σχήμα 5.1: Σχηματική παράσταση της παραγοντοποίησης Τα αντίστοιχα κομμάτια του κώδικα δίνονται παρακάτω. Πολλαπλασιασμός T V T gpu_sgemmnt (nb, boundary_1,nb,1,gpu_t, gpu_matrix (i,i),0, gpu_tv,nb,c,n,p); Πολλαπλασιασμός T V T A και V (T V T A) για τις επόμενες nb στήλες gpu_sgemmnn ( nb, w, boundary_1, 1, gpu_tv, gpu_matrix (i-nb,i), 0, gpu_tva, n, c, n, P ); gpu_sgemmnn ( boundary_1,w,nb,-1, gpu_matrix (i-nb,i-nb), gpu_tva,1, gpu_matrix (i-nb,i),c,n,c,p); Μεταφορά των nb στηλών στη CPU for ( int j = 0; j < P; j ++ ) download ( boundary_2,w, CPU_matrix [j](0, i), gpu_matrix (0, j*c+i )); 44

Πολλαπλασιασμός T V T A και V (T V T A) για τις υπόλοιπες στήλες του πίνακα gpu_sgemmnn (nb,h-nb, boundary_1,1, gpu_tv, gpu_matrix (i-nb,i+nb ),0, gpu_tva,n,c,n,p); gpu_sgemmnn ( boundary_1,c -(i+nb),nb,-1, gpu_matrix (i-nb,i-nb),gpu_tva,1, gpu_matrix (i-nb,i+nb),c,n,c,p); Σχήμα 5.2: Σχηματική παράσταση της υβριδικότητας kernels Η συνάρτηση gpu_sgemmnn( ) περιέχει τον ορισμό του Grid της CUDA και την κλίση του kernel sgemmnn_device<<<grid, threads>>>( ) ο οποίος έχει διαμορφωθεί κατάλληλα ώστε εκτελεί τους πολλαπλασιασμούς παράλληλα για όλους τους πίνακες. Ο ορισμός του Grid και η κλίση της kernel γίνονται με τον παρακάτω τρόπο dim3 grid (P *(( m +63)/64),( n +15)/16), threads (16,4); sgemmnn_device <<<grid, threads >>>(m,n,a.a,a.lda,b.a,b.lda, C.A,C.lda,k,alpha,beta,c_A,c_B,c_C,(m +63)/64); 45

όπου c_a, c_b και c_c ο αριθμός των στηλών των πινάκων A, B και C αντίστοιχα. Ιδιαίτερο ενδιαφέρον παρουσιάζει ο τρόπος ορισμού της διάστασης του Grid. Για κάθε πίνακα ουσιαστικά δημιουργείται ένα Grid που αναλαμβάνει την εκτέλεση του πολλαπλασιασμού για τον συγκεκριμένο πίνακα. Ο αριθμός των block που αποτελούν το Grid και των thread που αποτελούν το block επιλέγονται μετά από δοκιμές ώστε να υπάρχει πάντα μια πληθώρα από ταυτόχρονα threads προς εκτέλεση στους πυρήνες της κάρτας. Παρακάτω δίνεται ένα κομμάτι του κώδικα του kernel όπου φαίνεται ο τρόπος με τον οποίο κάθε thread προσδιορίζει τον πίνακα στον οποίο ανήκει και τις θέσεις των στοιχείων τις οποίες αναλαμβάνει να πολλαπλασιάσει. static global void sgemmnn_device ( int m, int n, const float *A,int lda, const float *B,int ldb, float * C, int ldc, int k, float alpha, float beta, int c_a, int c_b, int c_c, int grid_x ) { const int submatrix_i = blockidx. x/ grid_x ; A += submatrix_i * lda * c_a ; B += submatrix_i * ldb * c_b ; C += submatrix_i * ldc * c_c ; const int inx = threadidx. x; const int iny = threadidx. y; const int ibx = ( blockidx. x\% grid_x ) * 64; const int iby = blockidx. y * 16; const int row = ibx + inx + iny *16; } A += row ; B += inx + ( iby + iny ) * ldb ; C += row + iby * ldc ;... 46

Αντίστοιχη συνάρτηση και πυρήνας πολλαπλασιασμού υπάρχει και για τον ανάστροφο πολλαπλασιασμό gpu_sgemmnt(). Ένα μέτρο της χρήσης των υπολογιστικών πόρων της GPU είναι το occupancy που επιτυγχάνουν οι kernels πολλαπλασιασμού. Το κάθε thread χρησιμοποιεί 30 registers και η συνολική shared memory ανά block είναι 1168 bytes με κάθε block να αποτελείται από 64 threads. Συνεπώς το occupancy υπολογίζεται στο 50%, δηλαδή 16 ενεργά warps ανά multiprocessor. Μεγαλύτερο occupancy δεν σημαίνει απαραίτητα μεγαλύτερη απόδοση (Best Practises Guide - nvidia) [17], ειδικά όταν υπάρχουν δύο ενεργά warps ανά block και οχτώ ενεργά blocks. Για την αύξηση του occupancy θα έπρεπε να μικρύνει ο αριθμός των registers ανά thread και να χρησιμοποιηθεί περισσότερη shared memory, κάτι που θα σήμαινε περισσότερες μεταφορές από την shared memory [2]. 5.1 Μετρήσεις Παρακάτω δίνονται κάποιες ενδεικτικές μετρήσεις για την απόδοση της υλοποίησης. Ο χρόνος για το κάθε ένα από τα τρία εσωτερικά στάδια του αλγορίθμου μας δίνεται με διαφορετικό χρώμα. Παρουσιάζονται μετρήσεις για διάφορα μεγέθη block (nb) της παραγοντοποίησης όπως επίσης και για διάφορα μεγέθη του συνολικού αριθμού των πινάκων. Τέλος δίνονται τα γραφήματα σύγκρισης με την απλή εκτέλεση του κώδικα της παραγοντοποίησης QR του V.Volkov και της παραγοντοποίησης με τη χρήση της βιβλιοθήκης της Intel MKL 10.0, για διάφορα μεγέθη πινάκων. Ιδιαίτερο ενδιαφερον έχει το σχήμα 5.3, για την περίπτωση που nb=128 για πίνακες 128 στηλών. Στην ουσία δεν εφαρμόζεται η QR Block παραγοντοποίηση και ο αλγόριθμος χάνει την υβριδικότητά του. Εκτελείται αποκλειστικά στην CPU με άσκοπη μεταφορά δεδομένων μεταξύ GPU και CPU, κάτι που οδηγεί στην σημαντική αύξηση του χρονου επεξεργασίας σε σύγκριση με αυτόν για μικρότερες τιμές της μεταβλητής nb. Ο βέλτιστος χρόνος επιτυγχάνεται για nb=32, ενώ για μικρότερα μεγέθη ο χρόνος επεξεργασίας αυξάνεται. Αυτό συμβαίνει επειδή γίνονται πολλές αντιγραφές μεταξύ GPU και CPU ενώ αλλάζει και η οργάνωση των kernel πολλαπλασιασμού. Το διάγραμμα 5.8 δείχνει την επιτάχυνση που προσφέρει ο αλγόριθμός μας ως προς την απλή σειριακή παραγοντοποίηση όλων των πινάκων (με την χρήση του κώδικα του V.Volkov). 47

Σχήμα 5.3: Μετρήσεις για διαφορετικά μεγέθη nb και διαστάσεις προβλήματος 8192x2048, P=58 Σχήμα 5.4: Μετρήσεις για διαφορετικά μεγέθη nb και διαστάσεις προβλήματος 640x128, P=64 48

Σχήμα 5.5: Μετρήσεις για διαφορετικά μεγέθη P και διαστάσεις προβλήματος 8192x2048, nb=64 Σχήμα 5.6: Μετρήσεις για διαφορετικά μεγέθη P και διαστάσεις προβλήματος 640x128, nb=16 49

Σχήμα 5.7: Μετρήσεις για διάφορες διαστάσεις προβλήματος, με σταθερά P και nb, P=64, nb=64 Σχήμα 5.8: Σύγκριση σειριακής και παράλληλης υλοποίησης με sliding window 50