Δημήτρης Σπίγγος
dmtrs32@gmail.com 2013

# Τι θα βελτιστοποιήσουμε;

Όταν βελτιστοποιούμε για το GNOME, το πρώτο πράγμα που θυμόμαστε είναι ότι δεν προσπαθούμε να κάνουμε ένα πρόγραμμα καλύτερο, προσπαθούμε να κάνουμε το χρήστη που χρησιμοποιεί τον υπολογιστή πιο ευχαριστημένο.

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

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

# Εφαρμόζοντας τη βελτιστοποίηση

Η προηγούμενη ενότητα παρέλειψε ένα σημαντικό παράγοντα: για να βελτιστοποιηθεί κάτι πρέπει να είναι μετρήσιμο. Δε μπορείτε να βελτιστοποιήσετε την ευχαρίστηση. Ωστόσο, μπορείτε να μετρήση το χρόνο εκκίνησης ώστε να μπορείτε να δείτε αν τον έχετε βελτιώσει. Ευελπιστούμε ότι η ευτυχία θα επέλθει.

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

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

Συνήθως το πρόβλημα εντοπίζεται σε μικρά τμήματα του κώδικα. Διαλέξτε το χειρότερο από αυτά και συγκεντρωθείτε πρώτα σ' αυτό. Όταν θα έχετε τελειώσει μ' αυτό, τρέξτε ξανά το profiler και επαναλάβετε τη διαδικασία. Καθώς θα προχωράτε, το κέρδος από κάθε βήμα θα μειώνεται, και σε κάποιο σημείο θα πρέπει να αποφασίσετε αν τα αποτελέσματα είναι αρκετά καλά. Αν η προσπάθειές σας έχουν ως αποτέλεσμα τη βελτίωση μόνο κατά 10% κάθε φορά, τότε έχετε περάσει το σημείο όπου μπορείτε πια να σταματήσετε.

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

# Υποδείξεις

## Τα βασικά

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

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

Βελτιστοποιήστε σε ψηλό επίπεδο πριν προχωρήσετε στις λεπτομέρειες.

Χρησιμοποιήστε το σωστό αλγόριθμο. Το κλασικό παράδειγμα είναι να χρησιμοποιήσετε το quick-sort αντί το bubble-sort. Υπάρχουν πολλά ακόμα, μερικά εξοικονομούν μνήμη, μερικά εξοικονομούν επεξεργαστή. Επίσης, δείτε τι συντόμια μπορείτε να ακολουθήσετε: μπορείτε να ταξινομήσετε και γρηγορότερα από το quick-sort αν μπορείτε να κάνετε μερικούς συμβιβασμούς.

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

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

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

## Παγίδες για τους ανυποψίαστους

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

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

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

Να θυμάστε τους πόρους του εξυπηρετητή X. Η χρήση μνήμης του προγράμματός σας δεν περιλαμβάνει τα pixmap που αποθηκεύονται στις διεργασίες του εξυπηρετητή X, αλλά και πάλι χρησιμοποιούν μνήμη. Χρησιμοποιήστε το xrestop για να δείτε ποιοι πόροι χρησιμοποιούνται από το πρόγραμμά σας.

## Υποδείξεις χαμηλού επιπέδου

Όταν βελτιστοποιείτε τη χρήση της μνήμης, προσέξτε τη διαφορά ανάμεσα στη μέγιστη χρήση και στη μέση χρήση. Σχεδόν πάντα κατανέμεται κάποια ποσότητα μνήμης για χρήση από το πρόγραμμα, το οποίο είναι συνήθως κακό. Κάποια μνήμη είναι μόνο κατανεμημένη για λίγο, και αυτό μπορεί να είναι αποδεκτό. Εργαλεία όπως το massif χρησιμοποιούν την έννοια του χωροχρόνου, το γινόμενο της ποσότητας της μνήμης που χρησιμοποιείται και της διάρκειας κατά την οποία ήταν κατανεμημένη.

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

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

Δώστε στον μεταγλωττιστή όσες περισσότερες υποδείξεις μπορείτε. Χρησιμοποιήστε τη λέξη κλειδί const. Χρησιμοποιήστε τον `G_INLINE_FUNC` για συντομία, συναρτήσεων που καλούνται συχνά. Ψάξτε για τις μακροεντολές `G_GNUC_PURE`, `G_LIKELY`, και διάφορες άλλες της glib. Χρησιμοποιήστε τις μακροεντολές αντί για ειδικές λέξεις κλειδιά του gcc, για να εξασφαλίσετε τη φορητότητα.

Μη χρησιμοποιείτε τη γλώσσα assembly. Δεν είναι φορητή και ενώ μπορεί να είναι γρήγορη σε έναν επεξεργαστή, δεν είναι εγγυημένο ότι θα είναι γρήγορη και σε οποιοδήποτε άλλο επεξεργαστή που υποστηρίζει αυτήν την αρχιτεκτονική (π.χ. Athlon vs Pentium 4).

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

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

## Κόλπα υψηλού επιπέδου

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

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

Κάντε πράγματα στον ανενεργό βρόχο. Είναι ευκολότερο στον προγραμματισμό από τη χρήση πολυνηματικότητας, αλλά και πάλι επιτυγχάνει να κάνει πράγματα χωρίς να το αντιλαμβάνεται ο χρήστης. Να είστε όμως προσεκτικοί, γιατί αν σπαταλάτε πολύ χρόνο στον ανενεργό βρόχο τότε το πρόγραμμά σας γίνεται αργό. Οπότε να δίνεται τακτικά τον έλεγχο στον κύριο βρόχο.

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