Βελτιστοποίηση Κώδικα Γιώργος Μανής
Γενικά το πρόβληµα της βελτιστοποίησης είναι µη αποκρίσιµο στις περισσότερες µορφές του δεν έχουµε καµία εγγύηση ότι ο παραγόµενος κώδικας µετά από βελτιστοποίηση είναι ο καλύτερος δυνατός οι αλγόριθµοι βελτιστοποίησης που εφαρµόζονται είναι συνήθως υπέρ του δέοντος συντηρητικοί
Κριτήρια εφαρµογής µετασχηµατισµών βελτιστοποίσης ένας µετασχηµατισµός πρέπει να διατηρεί τη σηµασία των προγραµµάτων ένας µετασχηµατισµός πρέπει κατά µέσο όρο να επιταχύνει τα προγράµµατα ένας µετασχηµατισµός πρέπει να αξίζει την προσπάθεια για την υλοποίησή του σχέση προσπάθειας, επιτάχυνσης και ταχύτητας µεταφραστή σχέση κόστους υλοποίησης και ωφέλειας χρήσης σχέση δυσκολίας και συχνότητας χρήσης του προγράµµατος
Βελτιώσεις από τον προγραµµατιστή και τον µεταφραστή αρχικός κώδικας εµπρόσθιο τµήµα ενδιάµεσος κώδικας οπίσθιο τµήµα τελικός κώδικας Ο µεταγλωττιστής µπορεί: να βελτιώσει βρόχους να αντικαθιστά κλήσεις να υπολογίσει διευθύνσεις να διεχειρίζεται καταχωρητές να επιλέγει εντολές να κάνει µετασχηµατισµούς
Οργάνωση ενός βελτιστοποιητικού µεταφραστή εµπρόσθιο τµήµα ενδιάµεσος κώδικας βελτιστοποιητής κώδικα ενδιάµεσος κώδικας οπίσθιο τµήµα ανάλυση ροής ελέγχου ανάλυση ροής δεδοµένων βελτιστοποιητικοί µετασχηµατισµοί
Ανάλυση ροής ελέγχου και δεδοµένων Ανάλυση ροής ελέγχου είναι η µελέτη του προγράµµατος, όσoν αφορά τη σειρά εκτέλεσης των τµηµάτων του Ανάλυση ροής δεδοµένων είναι η µελέτη όσoν αφορa την επεξεργασία και διακίνηση δεδοµένων
Βασικές ενότητες Ο κώδικας χωρίζεται σε τµήµατα που ονοµάζονται βασικές ενότητες (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);
Αλγεβρικοί µετασχηµατισµοί Εφαρµόζουν αλγεβρικές ιδιότητες τελεστών της αρχικής ή της ενδιάµεσης γλώσσας για την απλοποίηση των εκφράσεων Ένας µετασχηµατισµός δεν είναι έγκυρος όταν οδηγεί σε εµφάνιση εξαιρέσεων κατά την εκτέλεση των πράξεων, που δεν υπήρχαν αρχικά Σπάνια εφαρµόζονται αλγεβρικοί µετασχηµατισµοί σε εκφράσεις κινητής υποδιαστολής, γιατί είναι δύσκολο να εξασφαλιστεί η εγκυρότητα τους σε κάθε περίπτωση
Αλγεβρικοί µετασχηµατισµοί αν 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;
Απαλοιφή κοινών υποεκφράσεων Ηεµφάνιση µιας έκφρασης λέγεται κοινή υποέκφραση αν η τιµή τηςε έχει προηγουµένως υπολογιστεί και οι τιµές των µεταβλητών που εµφανίζονται στην Ε δεν έχουν αλλάξει από τον προηγούµενο υπολογισµό της Αν ο υπολογισµός της Ε δεν επηρεάζει τις τιµές κάποιων µεταβλητών τότε µπορούµενααποφύγουµε τον επανυπολογισµότηςεκαινα χρησιµοποιήσουµε τηντιµή που έχει ήδη υπολογιστεί
ιάδοση αντιγράφων Οι τετράδες της µορφής :=, 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
Ενοποίηση κώδικα Αποσκοπεί στον εντοπισµό υπολογισµών που εκτελούνται ανεξάρτητα από την εκάστοτε ροή εκτέλεσης και τη µετακίνηση τους όσο το δυνατόν νωρίτερα ή αργότερα στο πρόγραµµα if (i > 0) { x := 2*i; s := s + x*i; i := i 1; } else { s := 0; x := 2*i; i := i 1; } x := 2*i; if (i > 0) s := s + x*i; else 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
Απαλοιφή επαγωγικών µεταβλητών Είναι µεταβλητές που χρησιµοποιούνται στο εσωτερικό των βρόχων καιοιτιµές τους ορίζουν µια αριθµητική πρόοδο 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;
Αναδιοργάνωση βρόχων Αντιστροφή βρόχου(loop inversion) Μετασχηµατισµός ενός βρόχου while ένα βρόχο repeat Μεταφορά της συνθήκης από την αρχή του βρόχου στο τέλος αυτού Για την εξασφάλιση της εγκυρότητας του µετασχηµατισµού θα πρέπει ο βρόχος να εκτελεστεί µια φορά ή να παραχθεί κώδικας που να ελέγχει την µη εκτέλεση του βρόχου if x>0 then for i:=1 to 100 do s:=s+a[i]; else s:=s-100; if x>0 then begin i:=1; repeat s:=s+a[i]; i:=i+1; until i>100 else s:=s-100;
Αναδιοργάνωση βρόχων Αποδιακλάδωση(unswitching) Μεταφορά µιας εντολής if από το εσωτερικό ενός βρόχου εκτός αυτού, όταν η συνθήκη παραµένει αναλλοίωτη εντός αυτού for i:=1 to 100 do if x>0 then s:=s+a[i]; else s:=s-1; if x>0 then for i:=1 to 100 do s:=s+a[i]; else 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)
Ευθυγράµµιση Ο µετασχηµατισµός της ευθυγράµµισης(straightening) αποσκοπεί στη συνένωση ζευγών βασικών ενοτήτων. Οι ενότητες Bi και Bj µπορούν να συνενωθούν αν: -Από την ενότητα Βi εξέρχεται µόνο µια ακµή στο γράφο ροής του προγράµµατος και κατευθύνεται προς την ενότητα Βj -Στην ενότητα Βj εισέρχεται µόνο µια ακµή στογράφοτου προγράµµατος η οποία προέρχεται από την ενότητα Bi Γιανασυµβαίνουν τα παραπάνω πρέπει είτε η Bj να βρίσκεται αµέσως µετά την Bi, είτε η Βi να τερµατίζεται µε έναάλµα προς την Bj -Η πρώτη περίπτωση απαιτεί απλώς τη συνένωση των ενοτήτων ενώ η δεύτερη την απαλοιφή του άλµατος µε αναδιάταξη περισσότερων ενοτήτων
Απλοποίηση συνθηκών και αλµάτων Ο µετασχηµατισµός της απλοποίησης συνθηκών(if simplification) βασίζεται στην ανάλυση της ροής ελέγχου και εφαρµόζεται όταν: -το ένα σκέλος της δοµής if είναι κενό -τιµή της συνθήκης είναι ήδη γνωστή Στην πρώτη περίπτωση το κενό σκέλος µπορεί να απαλειφθεί, ενώ στη δεύτερη η δοµή if αντικαθίσταται από το σκέλος που πρόκειται να ακολουθηθεί Ο µετασχηµατισµός της απλοποίησης των αλµάτων(jump simplification) βασίζεται στην ανάλυση της ροής ελέγχου και αποσκοπεί: -στην απαλοιφή ή το µετασχηµατισµό αλµάτων που οδηγούν επόµενη εντολή ή σε άλλη εντολή άλµατος σε αµέσως
Μετασχηµατισµοί υποπρογραµµάτων ενσωµάτωση υποπρογράµµατος κλήσεις ουράς και συνένωση
Ενσωµάτωση υποπρογράµµατος Ο µετασχηµατισµός της ενσωµάτωσης υποπρογράµµατος(inline expansion) αποσκοπεί στην απαλοιφή των κλήσεων -Κάθε κλήση υποπρογράµµατος µπορεί να αντικατασταθεί µε ένα αντίγραφο του σώµατος του υποπρογράµµατος Στην πράξη χρησιµοποιείται µόνο για την αντικατάσταση κλήσεων υποπρογραµµάτων που -καλούνται λίγες φορές µέσα στο πρόγραµµα -το σώµα τους περιέχει λίγες εντολές Είναι ιδιαίτερα χρήσιµος γιατί εκτός του ότι αλλάζει το κόστος των κλήσεων και τη δοµή του προγράµµατος, ενεργοποιεί και άλλους µετασχηµατισµούς
Ενσωµάτωση υποπρογράµµατος function max (a, b: integer): integer; begin if a>=b then result:=a; else result:=b; end; y:=max(x,3) Στη θέση της κλήσης max(x,3) µπορεί να ενσωµατωθεί o κώδικας If x>=3 then y:=x; else 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 else if n>m then result:=gcd(m,n mod m) else result:=gcd(n,m mod n) end;
Quick-sort, πριν και µετά