1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
<?xml version="1.0" encoding="utf-8"?>
<chapter>
<title>Χρησιμοποιήστε το <application>Massif</application> για το προφίλ της χρήσης της μνήμης σε εφαρμογές του GNOME</title>
<para>
This article describes how to use the <application>Massif</application> heap profiler with GNOME applications. We describe how to invoke, interpret, and act on the output of <application>Massif</application>. The <application>Swell Foop</application> game is used as an example.
</para>
<sect1 id="optimization-massif-TBL-intro">
<title>Εισαγωγή</title>
<para>Το <application>Massif</application> είναι μέλος της σουίτας εργαλείων δημιουργίας προφίλ χρήσης μνήμης <ulink type="http" url="http://valgrind.org/">valgrind</ulink>. Ο σκοπός του είναι να δίνει μια λεπτομερή προβολή της δυναμικής χρήσης της μνήμης κατά τη διάρκεια εκτέλεσης ενός προγράμματος. Συγκεκριμένα, καταγράφει τη χρήση της μνήμης από το δένδρο heap και τη στοίβα.</para>
<para>Η heap είναι η περιοχή στη μνήμη η οποία κατανέμεται με συναρτήσεις όπως η malloc. Μεγαλώνει κατά απαίτηση και είναι συνήθως η μεγαλύτερη περιοχή της μνήμης σε ένα πρόγραμμα. Η στοίβα είναι εκεί όπου αποθηκεύονται όλα τα τοπικά δεδομένα για τις συναρτήσεις. Αυτό συμπεριλαμβάνει τις «αυτόματες» μεταβλητές στη C και τη διεύθυνση επιστροφής για τις υπορουτίνες. Η στοίβα είναι τυπικά πολύ μικρότερη και πολύ πιο ενεργή από τη heap. Δε θα ασχοληθούμε ειδικά με τη στοίβα μια που το <application>Massif</application> την αντιμετωπίζει σαν να ήταν μέρος της heap. Το <application>Massif</application> παρέχει επίσης πληροφορίες για την ποσότητα της μνήμης που χρησιμοποιείται για τη διαχείριση της heap.</para>
<para>Το <application>Massif</application> παράγει δύο αρχεία εξόδου: μια γραφική επισκόπηση σε ένα αρχείο postscript και μια λεπτομερή ανάλυση σε ένα αρχείο κειμένου.</para>
</sect1>
<sect1 id="optimization-massif-TBL-using-massif">
<title>Χρήση του <application>Massif</application> με το GNOME</title>
<para>Το <application>Massif</application> έχει πολύ λίγες επιλογές και για τα περισσότερα προγράμματα δεν τις χρειάζεστε. Εντούτοις για της εφαρμογές του GNOME, όπου η κατανομή μνήμης μπορεί να θαφτεί βαθιά στο glib ή στο GTK, ο αριθμός επιπέδων κάτω από την κλήση-σωρού που το Massif κατεβαίνει θα πρέπει να αυξηθεί. Αυτό είναι γίνεται χρησιμοποιώντας την παράμετρο --depth. Εξ ορισμού αυτό είναι ίσο με 3; αυξάνοντας το σε 5 εγγυάται ότι η κλήση-σωρού θα φθάσει στον κώδικά σας. Ένα ή δύο περισσότερα επίπεδα μπορούν επίσης να είναι επιθυμητά και να παρέχουν στον κώδικά σας κάποιο πλαίσιο εφαρμογής. Δεδομένου ότι το επίπεδο λεπτομέρειας γίνεται γρήγορα συντριπτικό είναι καλύτερο να αρχίσει με μικρότερη παράμετρο βάθους και να το αυξήστε μόνο όταν γίνεται προφανές ότι αυτό δεν είναι ικανοποιητικό.</para>
<para>
It is also useful to tell <application>Massif</application> which functions allocate memory in glib. It removes an unnecessary layer of function calls from the reports and gives you a clearer idea of what code is allocating memory. The allocating functions in glib are g_malloc, g_malloc0, g_realloc, g_try_malloc, and g_mem_chunk_alloc. You use the --alloc-fn option to tell Massif about them.
</para>
<para>Η γραμμή-εντολών σας πρέπει επομένως να μοιάζει κάπως σαν:</para>
<programlisting>
valgrind --tool=massif --depth=5 --alloc-fn=g_malloc --alloc-fn=g_realloc --alloc-fn=g_try_malloc \
--alloc-fn=g_malloc0 --alloc-fn=g_mem_chunk_alloc swell-foop
</programlisting>
<para>
<application>Swell Foop</application> is the program we will be using as an example. Be warned that, since valgrind emulates the CPU, it will run <emphasis>very</emphasis> slowly. You will also need a lot of memory.
</para>
</sect1>
<sect1 id="optimization-massif-TBL-interpreting-results">
<title>Ερμηνεία των αποτελεσμάτων</title>
<para>Η γραφική έξοδος δεδομένων του <application>Massif</application> είναι κατά ένα μεγάλο μέρος αυτοεπεξηγηματικός. Κάθε ζώνη αντιπροσωπεύει τη μνήμη που διατίθεται από μια λειτουργία σε σχέση με τον χρόνο. Μόλις προσδιορίσετε ποιες ζώνες χρησιμοποιούν την περισσότερη μνήμη, συνήθως οι μεγάλες παχιές στην κορυφή, θα πρέπει να συμβουλευθείτε το αντίστοιχο αρχείο κειμένου για περισσότερες λεπτομέρειες.</para>
<para>Τα αρχεία κειμένων τακτοποιούνται ιεραρχικά βάση των τμημάτων, στην κορυφή είναι μία λίστα των χειρότερων χρηστών μνήμης που τακτοποιούνται κατά σειρά βάση την μείωση του χωροχρόνου. Κάτω από αυτά είναι και άλλα τμήματα, κάθε ένα διαιρεί τα αποτελέσματα σε μικρότερες λεπτομέρειες καθώς προχωράτε προς την κλήση-σωρού. Για να επεξηγήσουμε αυτό θα χρησιμοποιήσουμε την έξοδος δεδομένων της εντολής παραπάνω.</para>
<figure id="optimization-massif-FIG-output-unoptimized">
<title><application>Massif</application> output for the unoptimized version of the <application>Swell Foop</application> program.</title>
<mediaobject>
<imageobject>
<imagedata fileref="figures/massif-before.png" format="PNG"/>
</imageobject>
</mediaobject>
</figure>
<para>
<xref linkend="optimization-massif-FIG-output-unoptimized"/> shows a typical postscript output from <application>Massif</application>. This is the result you would get from playing a single game of <application>Swell Foop</application> (version 2.8.0) and then quitting. The postscript file will have a name like <filename>massif.12345.ps</filename> and the text file will be called <filename>massif.12345.txt</filename>. The number in the middle is the process ID of the program that was examined. If you actually try this example you will find two versions of each file, with slightly different numbers, this is because <application>Swell Foop</application> starts a second process and <application>Massif</application> follows that too. We will ignore this second process, it consumes very little memory.
</para>
<para>Στην κορυφή της γραφικής παράστασης βλέπουμε μια μεγάλη κίτρινη ζώνη με τίτλο gdk_pixbuf_new. Αυτό φαίνεται ως ένας ιδανικός υποψήφιος για βελτιστοποίηση, αλλά θα πρέπει να χρησιμοποιήσουμε το αρχείο κειμένων για να ανακαλύψουμε τι καλεί το gdk_pixbuf_new. Στην κορυφή του αρχείου κειμένων θα εμφανίζεται κάτι σαν το επόμενο:</para>
<programlisting>
Command: ./swell-foop
== 0 ===========================
Heap allocation functions accounted for 90.4% of measured spacetime
Called from:
28.8% : 0x6BF83A: gdk_pixbuf_new (in /usr/lib/libgdk_pixbuf-2.0.so.0.400.9)
6.1% : 0x5A32A5: g_strdup (in /usr/lib/libglib-2.0.so.0.400.6)
5.9% : 0x510B3C: (within /usr/lib/libfreetype.so.6.3.7)
3.5% : 0x2A4A6B: __gconv_open (in /lib/tls/libc-2.3.3.so)
</programlisting>
<para>Η γραμμή με το σύμβολο του '=' δείχνει πόσο βαθιά κάτω είμαστε στο ίχνος σωρού, σε αυτήν την περίπτωση είμαστε στην κορυφή. Μετά από αυτό απαριθμεί τους βαρύτερους χρήστες της μνήμης έτσι ώστε να μειωθεί ο χωρόχρονος. Ο χωρόχρονος είναι το λόγος του ποσού της μνήμης που χρησιμοποιείται και του χρόνου χρησιμοποίησης του. Αντιστοιχεί στον περιοχή των ζωνών στη γραφική παράσταση. Αυτό το μέρος του αρχείου μας λέει αυτό που ξέρουμε ήδη: το μεγαλύτερο μέρος του χωροχρόνου αφιερώνεται στο gdk_pixbuf_new. Για να ανακαλύψουν τι κάλεσε το gdk_pixbuf_new πρέπει να ψάξουμε περαιτέρω μέσα στο αρχείο κειμένου:</para>
<programlisting>
== 4 ===========================
Context accounted for 28.8% of measured spacetime
0x6BF83A: gdk_pixbuf_new (in /usr/lib/libgdk_pixbuf-2.0.so.0.400.9)
0x3A998998: (within /usr/lib/gtk-2.0/2.4.0/loaders/libpixbufloader-png.so)
0x6C2760: (within /usr/lib/libgdk_pixbuf-2.0.so.0.400.9)
0x6C285E: gdk_pixbuf_new_from_file (in /usr/lib/libgdk_pixbuf-2.0.so.0.400.9)
Called from:
27.8% : 0x804C1A3: load_scenario (swell-foop.c:463)
0.9% : 0x3E8095E: (within /usr/lib/libgnomeui-2.so.0.792.0)
and 1 other insignificant place
</programlisting>
<para>
The first line tells us we are now four levels deep into the stack. Below it is a listing of the function calls that leads from here to gdk_pixbuf_new. Finally there is a list of functions that are at the next level down and call these functions. There are, of course, also entries for levels 1, 2, and 3, but this is the first level to reach right down through the GDK code to the <application>Swell Foop</application> code. From this listing, we can see instantly that the problem code is load_scenario.
</para>
<para>Τώρα που ξέρουμε ποιο μέρος του κώδικά μας χρησιμοποιεί όλο χωρόχρονο μπορούμε να εξετάσουμε και να ανακαλύψουμε το γιατί. Ως αποτέλεσμα βλέπουμε ότι το load_scenario φορτώνει την pixbuf από ένα αρχείο και έπειτα δεν απελευθερώνει ποτέ την μνήμη. Αφού προσδιορίσαμε τον προβληματικό κώδικα, μπορούμε να αρχίσουμε να τον διορθώνουμε.</para>
</sect1>
<sect1 id="optimization-massif-TBL-acting-on-results">
<title>Ενέργειες πάνω στα αποτελέσματα</title>
<para>Μειώνοντας την κατανάλωση του χωροχρόνου είναι κάτι καλό, αλλά υπάρχουν δύο τρόποι για την μείωση του που δεν είναι ισότιμες. Μπορείτε είτε να μειώσετε το ποσό μνήμης που διατίθεται, ή μειώστε το χρονικό διάστημα που του διατίθεται. Εξετάστε για μια στιγμή ένα πρότυπο σύστημα με μόνο δύο διαδικασίες ενεργές. Και οι δύο διαδικασίες καταναλώνουν σχεδόν όλη την φυσική μνήμη RAM και εάν κάποια στιγμή επικαλύψει η μία την άλλη το σύστημα θα κάνει αντιμετάθεση και όλα θα επιβραδύνουν. Προφανώς εάν μειώνουμε τη χρήση μνήμης σε κάθε διαδικασία με παράγοντα του δύο θα μπορούν ειρηνικά να συνυπάρξουν χωρίς την ανάγκη για αντιμετάθεση. Εάν αντ' αυτού μειώσουμε το χρόνο που διατίθεται η μνήμη με παράγοντα του δύο τότε τα δύο προγράμματα μπορούν να συνυπάρξουν, αλλά μόνο εφ' όσον οι περίοδοι υψηλής χρήσης της μνήμης τους δεν επικαλύπτονται. Έτσι είναι καλύτερο να μειωθεί το ποσό μνήμης που διατίθεται.</para>
<para>
Unfortunately, the choice of optimization is also constrained by the needs of the program. The size of the pixbuf data in <application>Swell Foop</application> is determined by the size of the game's graphics and cannot be easily reduced. However, the amount of time it spends loaded into memory can be drastically reduced. <xref linkend="optimization-massif-FIG-output-optimized"/> shows the <application>Massif</application> analysis of <application>Swell Foop</application> after being altered to dispose of the pixbufs once the images have been loaded into the X server.
</para>
<figure id="optimization-massif-FIG-output-optimized">
<title><application>Massif</application> output for the optimized <application>Swell Foop</application> program.</title>
<mediaobject>
<imageobject>
<imagedata fileref="figures/massif-after.png"/>
</imageobject>
</mediaobject>
</figure>
<para>
The spacetime use of gdk_pixbuf_new is now a thin band that only spikes briefly (it is now the sixteenth band down and shaded magenta). As a bonus, the peak memory use has dropped by 200 kB since the spike occurs before other memory is allocated. If two processes like this were run together the chances of the peak memory usage coinciding, and hence the risk of swapping, would be quite low.
</para>
<para>Μπορούμε να το κάνουμε καλύτερα; Μια γρήγορη εξέταση της εξόδου δεδομένων του <application>Massif</application> σε κείμενο αποκαλύπτει ότι: το g_strdup είναι ο νέος σημαντικός ένοχος.</para>
<programlisting>
Command: ./swell-foop
== 0 ===========================
Heap allocation functions accounted for 87.6% of measured spacetime
Called from:
7.7% : 0x5A32A5: g_strdup (in /usr/lib/libglib-2.0.so.0.400.6)
7.6% : 0x43BC9F: (within /usr/lib/libgdk-x11-2.0.so.0.400.9)
6.9% : 0x510B3C: (within /usr/lib/libfreetype.so.6.3.7)
5.2% : 0x2A4A6B: __gconv_open (in /lib/tls/libc-2.3.3.so)
</programlisting>
<para>Εάν το εξετάσουμε ποιο ενδελεχώς θα δούμε ότι καλείται από πολλές, πολλές, διαφορετικές θέσεις.</para>
<programlisting>
== 1 ===========================
Πλαίσιο καταμέτρησης για το 7.7% του υπολογίσιμου χωροχρόνου
0x5A32A5: g_strdup (στο /usr/lib/libglib-2.0.so.0.400.6)
Called from:
1.8% : 0x8BF606: gtk_icon_source_copy (στο /usr/lib/libgtk-x11-2.0.so.0.400.9)
1.1% : 0x67AF6B: g_param_spec_internal (στο /usr/lib/libgobject-2.0.so.0.400.6)
0.9% : 0x91FCFC: (μέσα στο /usr/lib/libgtk-x11-2.0.so.0.400.9)
0.8% : 0x57EEBF: g_quark_from_string (στο /usr/lib/libglib-2.0.so.0.400.6)
και 155 άλλες ασήμαντες θέσεις
</programlisting>
<para>
We now face diminishing returns for our optimization efforts. The graph hints at another possible approach: Both the "other" and "heap admin" bands are quite large. This tells us that there are a lot of small allocations being made from a variety of places. Eliminating these will be difficult, but if they can be grouped then the individual allocations can be larger and the "heap admin" overhead can be reduced.
</para>
</sect1>
<sect1 id="optimization-massif-TBL-caveats">
<title>Caveats</title>
<para>Υπάρχουν μερικά πράγματα που θα πρέπει να προσέξετε: Αρχικά, ο χωρόχρονος εμφανίζεται μόνο ως ποσοστό, πρέπει να το συγκρίνετε με το γενικό μέγεθος του προγράμματος για να αποφασίσει εάν το ποσό μνήμης αξίζει να δαπανηθεί. Η γραφική παράσταση, με τον κάθετο άξονα σε kilobyte, είναι ιδανικό για κάτι τέτοιο.</para>
<para>
Secondly, <application>Massif</application> only takes into account the memory used by your own program. Resources like pixmaps are stored in the X server and aren't considered by <application>Massif</application>. In the <application>Swell Foop</application> example we have actually only moved the memory consumption from client-side pixbufs to server-side pixmaps. Even though we cheated there are performance gains. Keeping the image data in the X server makes the graphics routines quicker and removes a lot of inter-process communication. Also, the pixmaps will be stored in a native graphics format which is often more compact than the 32-bit RGBA format used by gdk_pixbuf. To measure the effect of pixmaps, and other X resources use the <ulink type="http" url="http://www.freedesktop.org/Software/xrestop">xrestop</ulink> program.
</para>
</sect1>
</chapter>
|