# Usando Massif para perfila o uso de memoria en software de GNOME

Este artigo describe como usar o perfilador de pila Massif con aplicativos de GNOME. Describirémoslle como invocalo, interpretar e actual sobre a saída de Massif. Usaremos o xogo Swell Foop para este exemplo.

## Introdución

Massif é un membro da suite de ferramentas de perfilado de memoria valgrind. O seu propósito é darlle unha visualización detallada do uso da memoria dinámica durante o ciclo de vida dun programa. Específicamente garda a memoria usada no heap e na stack.

A pila é a rexión da memoria que está reservada para funcións como malloc. Crece baixo demanda e é normalmente a rexión máis grande da memoria nun programa. A rima é onde se gardan os datos locais para as funcións. Isto inclúe as variábeis «automáticas» en C e o enderezo de devolución das subrutinas. A rima normalmente é moito máis pequena e máis activa que a pila. Non consideramos a pila explicitamente xa que Massif trátaa como se fora outra parte da rima. Massif tamén lle fornece información sobre canta memoria se usa para xestionar a pila.

Massif produce dous ficheiros de saída: unha vista gráfica xeral nun ficheiro postscript e unha explicación detallada nun ficheiro de texto.

## Usar Massif con GNOME

Massif ten varias opcións e algúns programas non as precisan. Porén para os aplicativos GNOME, a medida que baixa nos niveles da pila de chamadas os descendentes de Massif deben incrementarse. Isto pódese facer empregando o parámetro --depth. Por omisión isto é 3; incrementándoa a 5 garantizaralle que a pila de chamadas alcanza o seu código. Un ou máis niveles poden ser desexabeis para fornecerlle algún contexto ao seu código. Xa que o nivel de detalles pode ser moi rapidamente inxestionábel é mellor comezar con un valor de profundidade menos profundo e só incrementalo cando aparentemente non sexa dabondo.

Tamén é útil dicirlle a Massif que funcións reservan memoria en glib. Elimina unha capa non necesaria das chamadas das funcións desde os informes e fornécelle unha idea clara de que código está reservando memoria. As funcións de reserva en glib son g_malloc, g_malloc0, g_realloc, g_try_malloc e g_mem_chunk_alloc. Usará a opción --alloc-fn para dicirlle a Massif sobre elas.

A súa liña de orde debería semellarse a esta:

    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

Swell Foop é o programa que usaremos como exemplo. Teña en conta isto, xa que valgrind emula o CPU, executarase moi lento. E tamén precisara moita memoria.

## Interpretar os resultados

A saída gráfica de Massif é moi autoexplicativa. Cada banda representa a memoria asignada a unha función ao longo do tempo. Cando teña identificada que bandas están usando a maioría da memoria, normalmente a máis ancha estará na parte superior polo que terá que consultar o ficheiro de texto para obter máis detalles.

O ficheiro de texto está ordenado como unha xerarquía por seccións, na parte superior está unha lista dos peores usuarios da memoria para reducir o espazotempo. Máis abaixo de isto están as seguintes sección, cada unha delas dividindo os resultados en maior detalle como se procedera cara abaixo na pila de chamadas. Para ilustrar isto usaremos a saída da orde de arriba.

A saída de Massif para a versión non optimizada do programa Swell GNOME.

shows a typical postscript output from Massif. This is the result you would get from playing a single game of Swell Foop (version 2.8.0) and then quitting. The postscript file will have a name like `massif.12345.ps` and the text file will be called `massif.12345.txt`. 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 Swell Foop starts a second process and Massif follows that too. We will ignore this second process, it consumes very little memory.

Na parte superior da gráfica vese unha gran banda amarela etiquetada como gdk_pixbuf_new. Semella un candidato ideal para a optimización, pero precisarase usar o ficheiro de texto para topar que está chamando a gdk_pixbuf_new. A parte superior do ficheiro de texto mostrará algo como o seguinte:

    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)

The line with the '=' signs indicates how far down the stack trace we are, in this case we are at the top. After this it lists the heaviest users of memory in order of decreasing spacetime. Spacetime is the product of the amount of memory used and how long it was used for. It corresponds to the area of the bands in the graph. This part of the file tells us what we already know: most of the spacetime is dedicated to gdk_pixbuf_new. To find out what called gdk_pixbuf_new we need to search further down the text file:

    == 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

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 Swell Foop code. From this listing, we can see instantly that the problem code is load_scenario.

Agora que sabe que parte do código usa todo o espazo de tempo pódese buscar o porque. Resulta que load_scenario está cargando un pixbuf dun ficheiro e nunca libera esa memoria. Tendo identificado o código do problema pódese comezar a arranxalo.

## Actuar sobre os resultados

Reducing spacetime consumption is good, but there are two ways of reducing it and they are not equal. You can either reduce the amount of memory allocated, or reduce the amount of time it is allocated for. Consider for a moment a model system with only two processes running. Both processes use up almost all the physical RAM and if they overlap at all then the system will swap and everything will slow down. Obviously if we reduce the memory usage of each process by a factor of two then they can peacefully coexist without the need for swapping. If instead we reduce the time the memory is allocated by a factor of two then the two programs can coexist, but only as long as their periods of high memory use don't overlap. So it is better to reduce the amount of memory allocated.

Unfortunately, the choice of optimization is also constrained by the needs of the program. The size of the pixbuf data in Swell Foop 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. shows the Massif analysis of Swell Foop after being altered to dispose of the pixbufs once the images have been loaded into the X server.

A saída de Massif para o programa optimizado Swell GNOME.

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.

Podémolo facer mellor? Unha examinación rápida mo texto de saída de Massif revela: g_strdup para ser un ofensor máis importante.

    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)

Se miramos máis cerca nas partes que vemos se chaman desde moitas partes.

    == 1 ===========================
    Context accounted for  7.7% of measured spacetime
      0x5A32A5: g_strdup (in /usr/lib/libglib-2.0.so.0.400.6)

    Called from:
        1.8% : 0x8BF606: gtk_icon_source_copy (in /usr/lib/libgtk-x11-2.0.so.0.400.9)

        1.1% : 0x67AF6B: g_param_spec_internal (in /usr/lib/libgobject-2.0.so.0.400.6)

        0.9% : 0x91FCFC: (within /usr/lib/libgtk-x11-2.0.so.0.400.9)

        0.8% : 0x57EEBF: g_quark_from_string (in /usr/lib/libglib-2.0.so.0.400.6)

      and 155 other insignificant places

Agora encárase a redución da saída para os esforzos de optimización. O gráfico suxire outra posíbel aproximación: tanto a banda «other» como a «heap admin» son bastante largas. Isto quere dicir que existen moitas pequenas reservas realizadas desde certa variedade de sitios. Eliminalos será difícil, pero se se poden agrupar, entón as reservas son individuais poden ser máis grandes e a elevada banda «heap admin» pode reducirse.

## Advertencias

Existen un par de cousas para ter en conta: Primeiramente, só se informa do espazo de tempo como porcentaxe, ten que comparalo co tamaño total do programa para decidir se merece a pena perseguir a cantidade total de memoria. O gráfico, co seu eixe vertical de kilobytes, é bo para iso.

Secondly, Massif 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 Massif. In the Swell Foop 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 xrestop program.