Γενικά Βελτιστοποίηση Κώδικα Γιώργος Μανής το πρόβληµα της βελτιστοποίησης είναι µη αποκρίσιµο στις περισσότερες µορφές του δεν έχουµε καµία εγγύηση ότι ο παραγόµενος κώδικας µετά από βελτιστοποίηση είναι ο καλύτερος δυνατός οι αλγόριθµοι βελτιστοποίησης που εφαρµόζονται είναι συνήθως υπέρ του δέοντος συντηρητικοί Κριτήρια εφαρµογής µετασχηµατισµών βελτιστοποίσης Βελτιώσεις από τον προγραµµατιστή και τον µεταφραστή ένας µετασχηµατισµός πρέπει να διατηρεί τη σηµασία των προγραµµάτων ένας µετασχηµατισµός πρέπει κατά µέσο όρο να επιταχύνει τα προγράµµατα ένας µετασχηµατισµός πρέπει να αξίζει την προσπάθεια για την υλοποίησή του σχέση προσπάθειας, επιτάχυνσης και ταχύτητας µεταφραστή αρχικός κώδικας εµπρόσθιο τµήµα ενδιάµεσος κώδικας οπίσθιο τµήµα τελικός κώδικας Ο µεταγλωττιστής µπορεί: να βελτιώσει βρόχους να αντικαθιστά κλήσεις σχέση κόστους υλοποίησης και ωφέλειας χρήσης να υπολογίσει διευθύνσεις σχέση δυσκολίας και συχνότητας χρήσης του προγράµµατος να διεχειρίζεται καταχωρητές να επιλέγει εντολές να κάνει µετασχηµατισµούς Οργάνωση ενός βελτιστοποιητικού µεταφραστή Ανάλυση ροής ελέγχου και δεδοµένων εµπρόσθιο τµήµα ενδιάµεσος κώδικας βελτιστοποιητής κώδικα ενδιάµεσος κώδικας οπίσθιο τµήµα Ανάλυση ροής ελέγχου είναι η µελέτη του προγράµµατος, όσoν αφορά τη σειρά εκτέλεσης των τµηµάτων του Ανάλυση ροής δεδοµένων είναι η µελέτη όσoν αφορa την επεξεργασία και διακίνηση δεδοµένων ανάλυση ροής ελέγχου ανάλυση ροής δεδοµένων βελτιστοποιητικοί µετασχηµατισµοί 1
Βασικές ενότητες Οκώδικαςχωρίζεταισετµήµατα που ονοµάζονται βασικές ενότητες (basic blocks) Βασική ενότητα είναι µια ακολουθία εντολών η είσοδος στην οποία µπορεί να γίνει µόνο από την αρχή και η έξοδος µόνο από το τέλος Προϋποθέσεις: Αν η ακολουθία περιέχει τετράδες άλµατος, κλήσεις υποπρογράµµατος ή επιστροφής αυτές βρίσκονται υποχρεωτικά στο τέλος της ακολουθίας Αν η ακολουθία περιέχει µια τετράδα προς την οποία γίνεται άλµα, τότε αυτή βρίσκεται υποχρεωτικά στην αρχή. Η πρώτη τετράδα κάθε βασικής ενότητας ονοµάζεται οδηγός Βελτιστοποιητικοί µετασχηµατισµοί Μετασχηµατισµοί υψηλού επιπέδου Κατατάσσονται σε 4 κατηγορίες: Μετασχηµατισµοί υψηλού επιπέδου Μετασχηµατισµοί βρόχων Μετασχηµατισµοί χαµηλού επιπέδου Μετασχηµατισµοί υποπρογραµµάτων Σε αυτή την κατηγορία ανήκουν οι παρακάτω µετασχηµατισµοί: Αποτίµηση σταθερών εκφράσεων Αλγεβρικοί µετασχηµατισµοί Απαλοιφή κοινών υποεκφράσεων ιάδοση αντιγράφων Ενοποίηση κώδικα Αποτίµηση σταθερών εκφράσεων Αλγεβρικοί µετασχηµατισµοί Ένα παράδειγµα: const int ROWS=100; const int COLUMNS=50; int *p=(int *)malloc(rows*columns*sizeof(int)); Ο υπολογισµός της έκφρασης ROWS*COLUMNS*sizeof(int) µπορεί να γίνει κατά τη µεταγλώττιση του προγράµµατος, καθώς όλα τα συστατικά της είναι σταθερές, χωρίς να χρειαστεί η παραγωγή κώδικα για αυτό το σκοπό Ο παραπάνω κώδικας θα µετασχηµατιστεί σε: const int ROWS=100; const int COLUMNS=50; int *p=(int *)malloc(20000); Εφαρµόζουν αλγεβρικές ιδιότητες τελεστών της αρχικής ή της ενδιάµεσης γλώσσας για την απλοποίηση των εκφράσεων Ένας µετασχηµατισµός δεν είναι έγκυρος όταν οδηγεί σε εµφάνιση εξαιρέσεων κατά την εκτέλεση των πράξεων, που δεν υπήρχαν αρχικά Σπάνια εφαρµόζονται αλγεβρικοί µετασχηµατισµοί σε εκφράσεις κινητής υποδιαστολής, γιατί είναι δύσκολο να εξασφαλιστεί η εγκυρότητα τους σε κάθε περίπτωση 2
Αλγεβρικοί µετασχηµατισµοί Αλγεβρικοί µετασχηµατισµοί αν x είναι ακέραια µεταβλητή τότε: x + 0 -> x 0 + x -> x x * 0 -> 0 0 * x -> 0 x * 1 -> x 1 * x -> x -(-x)-> x αν b είναι boolean µεταβλητή τότε: b or true -> true b or flase -> b not (not b) -> b b and true -> b b and false -> false Είναι όµως όλες έγκυρες πάντοτε; Είναι όµως όλες έγκυρες πάντοτε; Αλγεβρικοί µετασχηµατισµοί Αλγεβρικοί µετασχηµατισµοί αν x είναι δείκτης τότε: ( & x )^ -> x Είναι έγκυρo; x + y x -> y Είναι έγκυρo; Απαλοιφή κοινών υποεκφράσεων Ηεµφάνιση µιας έκφρασης λέγεται κοινή υποέκφραση αν η τιµή τηςε έχει προηγουµένως υπολογιστεί και οι τιµές των µεταβλητών που εµφανίζονται στην Ε δεν έχουν αλλάξει από τον προηγούµενο υπολογισµό της Αν ο υπολογισµός της Ε δεν επηρεάζει τις τιµές κάποιων µεταβλητών τότε µπορούµε νααποφύγουµε τον επανυπολογισµό τηςεκαινα χρησιµοποιήσουµε τηντιµή που έχει ήδη υπολογιστεί 3
ιάδοση αντιγράφων Οι τετράδες της µορφής :=, y, _, x ονοµάζονται εντολές αντιγραφής Η ιδέα είναι να χρησιµοποιούνται τα πρωτότυπα στη θέση του αντίγραφου, µετά την εντολή αντιγραφής Προϋπόθεση να µην έχουν µεσολαβήσει άλλες αναθέσεις στις µεταβλητές x ή y ίνεται η δυνατότητα να εφαρµοστούν άλλοι µετασχηµατισµοί ιάδοση αντιγράφων Αντίστροφη διάδοση αντιγράφων Παράδειγµα :=, y, _, x +, x, 3, $1 :=, $1, _, z par, V, _, z call, _, _, p :=, y, _, x +, y, 3, $1 :=, $1, _, z par, V, _, $1 call, _, _, p Ο αντίστροφος µετασχηµατισµός µπορεί να γίνει σε τετράδες της µορφής +, i, 1, $1 :=, $1, _, i Η προσωρινή µεταβλητή $1 δεν χρησιµοποιείται πουθενά παρακάτω στον κώδικα, αντί λοιπόν να αντικατασταθεί η i από την $1 καλύτερο θα ήταν να γίνει η αντίστροφη αντικατάσταση Το αποτέλεσµα θα ήταν η τετράδα +, i, 1, i 4
Ενοποίηση κώδικα Μετασχηµατισµοί βρόχων Αποσκοπεί στον εντοπισµό υπολογισµών που εκτελούνται ανεξάρτητα από την εκάστοτε ροή εκτέλεσης και τη µετακίνηση τους όσο το δυνατόν νωρίτερα ή αργότερα στο πρόγραµµα Είναι ιδιαίτερης σηµασίας εξαιτίας του χρόνου που καταναλώνεται στην εκτέλεση των βρόχων if (i > 0) { x := 2*i; s := s + x*i; i := i 1; } { s := 0; x := 2*i; i := i 1; } x := 2*i; if (i > 0) s := s + x*i; s := 0; i := i 1; Περιλαµβάνουν Μετακίνηση κώδικα Απαλοιφή επαγωγικών µεταβλητών και υποβιβασµόισχύος Αναδιοργάνωση βρόχων Απαλοιφή ελέγχου ορίων πίνακα Μετακίνηση κώδικα Μετακίνηση κώδικα Μετακινούνται υπολογισµοί που while i<=limit-2 do begin βρίσκονται στο εσωτερικό του βρόχου s:=s+a[i]-4*n; ητιµή τους είναι αναλλοίωτη κατά την εκτέλεση του βρόχου i:=i+1 η εκτέλεσή τους µια µόνο φορά, πριν το βρόχο, δεν αλλοιώνει τη σηµασία end του προγράµµατος t1:=limit-2 t2:=4*n while i<=t1 do begin s:=s+a[i]-t2; i:=i+1; end Μετακίνηση κώδικα while i<=limit-2 do begin s:=s+a[i]-4*n; i:=i+1 end t1:=limit-2 if i<=t1 then t2:=4*n while i<=t1 do begin s:=s+a[i]-t2; i:=i+1; end 5
Απαλοιφή επαγωγικών µεταβλητών Είναι µεταβλητές που χρησιµοποιούνται στο εσωτερικό των βρόχων και οι τιµές τους ορίζουν µια αριθµητική πρόοδο for i:=1 to 100 do Ητιµή µιας επαγωγικής µεταβλητής µεταξύ δύο διαδοχικών επαναλήψεων xi+1:=f(xi) H ύπαρξη δύο επαγωγικών µεταβλητών σε ένα βρόχο οδηγεί στο να απαλειφθεί η µία και η τιµή της να υπολογίζεται µέσω της άλλης y=ax+b x,y σχετίζονται µε γραµµικό τρόπο Υποβιβασµός ισχύος Ένας µετασχηµατισµός που συνεργάζεται καλά µε την απαλοιφή επαγωγικών µεταβλητών είναι ο υποβιβασµός ισχύος(strength reduction) Σύµφωνα µε αυτόν οι λειτουργίες που είναι δαπανηρές κατά την εκτέλεσή τους αντικαθίστανται µε άλλες οικονοµικότερες Αναδιοργάνωση βρόχων Απαλοιφή βρόχου µε κενόσώµα Ξετύλιγµα βρόχων (loop unrolling) Κατάργηση του βρόχου αν επαναλαµβάνεται λίγες φορές και επανάληψη του σώµατος του βρόχου τον ίδιο αριθµό φορών for i:=1 to 3 do a:=a+1; a:=a+1; a:=a+1; 6
Αναδιοργάνωση βρόχων Αναδιοργάνωση βρόχων Αντιστροφή βρόχου(loop inversion) Μετασχηµατισµός ενός βρόχου while ένα βρόχο repeat Μεταφορά της συνθήκης από την αρχή του βρόχου στο τέλος αυτού Για την εξασφάλιση της εγκυρότητας του µετασχηµατισµού θα πρέπει ο βρόχος να εκτελεστεί µια φορά ή να παραχθεί κώδικας που να ελέγχει την µη εκτέλεση του βρόχου if x>0 then if x>0 then for i:=1 to 100 do begin s:=s+a[i]; i:=1; repeat s:=s-100; s:=s+a[i]; i:=i+1; until i>100 s:=s-100; Αποδιακλάδωση(unswitching) Μεταφορά µιας εντολής if από το εσωτερικό ενός βρόχου εκτός αυτού, όταν η συνθήκη παραµένει αναλλοίωτη εντός αυτού for i:=1 to 100 do if x>0 then s:=s+a[i]; s:=s-1; if x>0 then for i:=1 to 100 do s:=s+a[i]; for i:=1 to 100 do s:=s-1; Όρια πίνακα Όρια πίνακα Έλεγχος ορίων πίνακα Αναφέρεται στη δηµιουργία πρόσθετου τελικού κώδικα για κάθε προσπέλαση σε στοιχείο πίνακα Στόχος είναι να ελεγχθεί αν η τιµή τουi σε µια έκφραση a[i] βρίσκεται εντός των ορίων του πίνακα Απαλοιφή του ελέγχου ορίων πίνακα Ένας βελτιστοποιητικός µεταγλωττιστής φροντίζει για την απαλοιφή του ελέγχου ορίων πίνακα αν είναι περιττός, δηλαδή αν είναι γνωστό ότι η τιµή του i είναι εντός των ορίων Πολλές γλώσσες αγνοούν την υπέρβαση των ορίων και µεταφέρουν την ευθύνη στον προγραµµατιστή Με προσθήκη πρόσθετου ενδιάµεσου κώδικα για την πραγµατοποίηση του ελέγχου - ήλωση a:array[100] of integer -Ενδιάµεσος κώδικας <,i,0,9999 >=,i,100,9999 array,a,i,$1 :=0,_,[$1] Υποθέτουµε ότι στη τετράδα 9999 είναι τοποθετηµένος ο κώδικας που πρέπει να εκτελείται στην περίπτωση της υπέρβασης των ορίων του πίνακα Η απαλοιφή του ελέγχου θα έχει ως αποτέλεσµα την αφαίρεση των δύο πρώτων τετράδων της ακολουθίας Μετασχηµατισµοί χαµηλού επιπέδου Απαλοιφή άχρηστου κώδικα Απαλοιφή άχρηστου κώδικα Ευθυγράµµιση Απλοποίηση συνθηκών και αλµάτων Οάχρηστοςκώδικας(dead code) είναι µια ακολουθία εντολών που υπολογίζουν τιµές που ποτέ δεν χρησιµοποιούνται -Ο εντοπισµός τέτοιων µεταβλητών ονοµάζεται ανάλυση χρόνου ζωής -Στην κατηγορία του άχρηστου κώδικα ανήκει ο κώδικας που δεν εκτελείται ποτέ(unreachable code) Ο µετασχηµατισµός που µπορεί να εφαρµοστεί όταν εισάγεται άχρηστος κώδικας είναι η απαλοιφή του(dead code elimination) 7
Ευθυγράµµιση Απλοποίηση συνθηκών και αλµάτων Ο µετασχηµατισµός της ευθυγράµµισης(straightening) αποσκοπεί στη συνένωση ζευγών βασικών ενοτήτων. Οι ενότητες Bi και Bj µπορούν να συνενωθούν αν: -Από την ενότητα Βi εξέρχεται µόνο µια ακµή στο γράφο ροής του προγράµµατος και κατευθύνεται προς την ενότητα Βj -Στην ενότητα Βj εισέρχεται µόνο µια ακµήστογράφοτου προγράµµατος η οποία προέρχεται από την ενότητα Bi Γιανασυµβαίνουν τα παραπάνω πρέπει είτε η Bj να βρίσκεται αµέσως µετά την Bi, είτε η Βi να τερµατίζεται µε έναάλµα προς την Bj -Η πρώτη περίπτωση απαιτεί απλώς τη συνένωση των ενοτήτων ενώ η δεύτερη την απαλοιφή του άλµατος µε αναδιάταξη περισσότερων ενοτήτων Ο µετασχηµατισµός της απλοποίησης συνθηκών(if simplification) βασίζεται στην ανάλυση της ροής ελέγχου και εφαρµόζεται όταν: -το ένα σκέλος της δοµής if είναι κενό -τιµή της συνθήκης είναι ήδη γνωστή Στην πρώτη περίπτωση το κενό σκέλος µπορεί να απαλειφθεί, ενώ στη δεύτερη η δοµή if αντικαθίσταται από το σκέλος που πρόκειται να ακολουθηθεί Ο µετασχηµατισµός της απλοποίησης των αλµάτων(jump simplification) βασίζεται στην ανάλυση της ροής ελέγχου και αποσκοπεί: -στην απαλοιφή ή το µετασχηµατισµό αλµάτων που οδηγούν σε αµέσως επόµενηεντολήήσεάλληεντολήάλµατος 8
Μετασχηµατισµοί υποπρογραµµάτων ενσωµάτωση υποπρογράµµατος κλήσεις ουράς και συνένωση Ενσωµάτωση υποπρογράµµατος Ενσωµάτωση υποπρογράµµατος Ο µετασχηµατισµός της ενσωµάτωσης υποπρογράµµατος(inline expansion) αποσκοπεί στην απαλοιφή των κλήσεων -Κάθε κλήση υποπρογράµµατος µπορεί να αντικατασταθεί µε ένα αντίγραφο του σώµατος του υποπρογράµµατος Στην πράξη χρησιµοποιείται µόνο για την αντικατάσταση κλήσεων υποπρογραµµάτων που -καλούνται λίγες φορές µέσα στο πρόγραµµα -το σώµα τους περιέχει λίγες εντολές Είναι ιδιαίτερα χρήσιµος γιατί εκτός του ότι αλλάζει το κόστος των κλήσεων και τη δοµή του προγράµµατος, ενεργοποιεί και άλλους µετασχηµατισµούς function max (a, b: integer): integer; begin if a>=b then result:=a; result:=b; end; y:=max(x,3) Στη θέση της κλήσης max(x,3) µπορεί να ενσωµατωθεί o κώδικας If x>=3 then y:=x; y:=3; Κλήσεις ουράς και συνένωση Κλήσεις ουράς και συνένωση - αναδροµή Μια κλήση του υποπρογράµµατος g από το υποπρόγραµµα f ονοµάζεται κλήση ουράς(tail call) αν είναι το τελευταίο πράγµα που κάνει το σώµα της f πριν επιστρέψει. Aν ένα υποπρόγραµµα περιέχειµια κλήση ουράς προς τον εαυτό του, τότε αυτή η αναδροµή ονοµάζεται αναδροµή ουράς(tail recursion) Ο µετασχηµατισµός βελτιστοποίησης κλήσεων ουράς(tail call optimization) κάνει το εξής: αντί να κατασκευάσει ένα νέο εγγράφηµα δραστηριοποίησης για την κλήση ουράς, µετασχηµατίζει το εγγράφηµα του καλούντος υποπρογράµµατος και φροντίζει ώστε να ενηµερωθεί σωστά η διεύθυνση επιστροφής. Η κλήση υλοποιείται µε ένα άλµα στον κώδικα του καλούµενου υποπρογράµµατος Ο µετασχηµατισµός της απαλοιφής αναδροµής ουράς επιφέρει ακόµα µεγαλύτερη βελτιστοποίηση: µετασχηµατίζει το σώµα του υποπρογράµµατος αντικαθιστώντας τις αναδροµικές κλήσεις µε άλµατα και ουσιαστικά η αναδροµή µετασχηµατίζεται σε επανάληψη Καιοιδύοαναδροµικές κλήσεις που περιέχονται είναι κλήσεις ουράς function gcd(n,m: integer):integer; begin if (n==0) or (m=0) then result:=n+m if n>m then result:=gcd(m,n mod m) result:=gcd(n,m mod n) end; 9
Quick-sort, πριν και µετά 10