File: massif.page

package info (click to toggle)
gnome-devel-docs 40.3-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 79,188 kB
  • sloc: javascript: 2,514; xml: 2,407; ansic: 2,229; python: 1,854; makefile: 805; sh: 499; cpp: 131
file content (131 lines) | stat: -rw-r--r-- 14,405 bytes parent folder | download | duplicates (2)
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
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" type="guide" style="task" id="massif" xml:lang="pt-BR">
    <info>
      <link type="guide" xref="index#massif"/>
    
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Enrico Nicoletto</mal:name>
      <mal:email>liverig@gmail.com</mal:email>
      <mal:years>2012</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Rafael Fontenelle</mal:name>
      <mal:email>rafaelff@gnome.org</mal:email>
      <mal:years>2013-2019</mal:years>
    </mal:credit>
  </info>
      <title>Utilizando <app>Massif</app> para perfilar utilização de memória em software do GNOME</title>

    <p>Este artigo descreve como utilizar o perfilador (profiler) de heap <app>Massif</app> com aplicativos do GNOME. Nós descrevemos como invocar, interpretar e agir sobre a saída do <app>Massif</app>. O jogo <app>Swell Foop</app> é utilizado como exemplo.</p>
   <section id="optimization-massif-TBL-intro">
        <title>Introdução</title>
        <p><app>Massif</app> é um membro da suíte de ferramentas de perfilamento de memória <link href="http://valgrind.org/">valgrind</link>. Seu propósito é fornecer uma visão detalhada da utilização de memória dinâmica durante o tempo de vida do programa. Especificamente, ele grava a utilização de heap e de stack (pilha) da memória.</p>
        <p>O heap é a região da memória que é alocada com funções como malloc. Ele cresce por demanda e geralmente é a maior região de memória em um programa. A pilha (stack) é onde são armazenados todos os dados locais para as funções. Isto inclui as variáveis “automáticas” em C e os endereços de retorno para as sub-rotinas. A pilha é tipicamente bem menor e muito mais ativa que o heap. Nós não vamos considerar explicitamente como pilha já que o <app>Massif</app> o trata como se fosse apenas outra parte do heap. <app>Massif</app> também fornece informações sobre quanta memória é utilizada para gerenciar o heap.</p>
        <p>O <app>Massif</app> produz dois arquivos de saída: um resumo gráfico em um arquivo postscript e uma discriminação detalhada em um arquivo de texto.</p>
    </section>
    <section id="optimization-massif-TBL-using-massif">
        <title>Utilizando o <app>Massif</app> com o GNOME</title>
        <p><app>Massif</app> possui muitas poucas opções e para muitos programas elas não são necessárias. Entretanto para aplicativos GNOME, onde a alocação de memória pode estar enterrada nas profundezas do glib ou GTK, os descendentes do Massif precisam ser aumentados a medida que o número de níveis da pilha de chamadas abaixam. Isto é alcançado usando o parâmetro --depth. Por padrão, este valor é 3; aumentando ele para 5 irá garantir que a pilha de chamadas atinja o seu código. Mais um ou dois níveis podem ser convenientes para fornecer algum contexto ao seu código. Uma vez que o nível de detalhamento se torna rapidamente sobrecarregado, é melhor iniciar com o parâmetro de menor profundidade (depth) e apenas aumentá-lo quando se torna aparente que isto não é suficiente.</p>
        <p>Também é útil avisar o <app>Massif</app> quais funções alocam memória no glib. Isto remove uma camada desnecessária de chamadas de função dos relatórios e oferece a você uma ideia mais clara de qual código está alocando memória. As funções de alocação do glib são: g_malloc, g_malloc0, g_realloc, g_try_malloc e g_mem_chunk_alloc. Você utiliza a opção --alloc-fn para contar ao Masiff sobre elas.</p>
        <p>A partir de agora, sua linha de comando deve parecer como algo assim:</p>
        <code>
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
        </code>
        <p><app>Swell Foop</app> é o programa que estaremos utilizando como exemplo. Esteja avisado de quê, uma vez que valgrind emula a CPU, ele irá rodar <em>muito</em> lentamente. Você precisará também de muita memória.</p>
    </section>
    <section id="optimization-massif-TBL-interpreting-results">
        <title>Interpretando os resultados</title>
        <p>A saída gráfica do <app>Massif</app> é amplamente auto-explicativa. Cada faixa representa a memória alocada para uma função em relação ao tempo. Uma vez que você identifique quais faixas estão usando a maior parte da memória, geralmente a mais espessa ao topo, você terá de consultar o arquivo de texto para os detalhes.</p>
        <p>O arquivo de texto é organizado em hierarquia de seções, ao topo há uma lista dos piores usuários de memória dispostos em espaço-tempo decrescente. Abaixo disso há mais seções, cada qual decompondo os resultados em detalhes mais sutis a medida que você prossegue para a porção inferior da pilha de chamadas. Para esclarecer isto, nós utilizaremos a saída do comando supra-citado.</p>
        <figure>
            <title>Saída do <app>Massif</app> para a versão não otimizada do programa <app>Swell Foop</app>.</title>
            <media type="image" src="figures/massif-before.png"/>
         </figure>
        <p>A imagem acima mostra uma saída postscript típica do <app>Massif</app>. Este é o resultado que você obteria ao jogar um único jogo do <app>Swell Foop</app> (versão 2.8.0) e depois saindo. O arquivo postscript terá um nome como <file>massif.12345.ps</file> e o arquivo de texto será chamado <file>massif.12345.txt</file>. O número ao meio é o ID do processo do programa que foi examinado. Se você realmente tentar realizar este exemplo, você encontrará duas versões para cada arquivo com números levemente diferentes, devido o <app>Swell Foop</app> iniciar um segundo processo e o <app>Massif</app> o seguir também. Nós iremos ignorar este segundo processo, ele consome muito pouca memória.</p>
        <p>Ao topo do gráfico, nós vemos uma ampla faixa amarela rotulada gdk_pixbuf_new. Ela parece ser uma candidata ideal para otimização, mas nós iremos precisar utilizar o arquivo de texto para descobrir o que está chamando gdk_pixbuf_new. O topo do arquivo de texto irá se parecer como algo assim:</p>
        <code>
Comando: ./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)
        </code>
        <p>A linha com o sinal de '=' indica a que distância abaixo do rastreamento da pilha estamos, neste caso estamos ao topo. Após isto, são listados os usuários de memória mais pesados em ordem decrescente em relação ao tempo. O espaço-tempo é o produto da quantidade de memória utilizada e por quanto tempo ela foi utilizada. Isto corresponde a área das faixas no gráfico. Esta parte do arquivo nos conta o que já sabíamos: a maior parte do espaço-tempo é dedicado ao gdk_pixbuf_new. Para descobrir o que chamou o gdk_pixbuf_new nós precisamos pesquisar, logo abaixo, o arquivo de texto:</p>
        <code>
== 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
        </code>
        <p>A primeira linha nos conta que agora estamos quatro níveis dentro da pilha. Abaixo há uma listagem das chamadas de função que partem daqui até o gdk_pixbuf_new. Finalmente há uma lista de funções que estão no próximo nível inferior e chamam estas funções. Também existem, obviamente, entradas para os níveis 1, 2 e 3, mas este é o primeiro nível a alcançar logo abaixo, através do código GDK, o código do <app>Swell Foop</app>. A partir desta listagem, nós podemos ver instantaneamente que o problema do código é o load_scenario.</p>
        <p>Agora que sabemos qual parte do nosso código está utilizando todo o espaço-tempo, nós podemos olhar para ele e descobrir o porquê. Verifica-se que o load_scenario está carregando um pixbuf a partir do arquivo e então nunca liberando aquela memória. Tendo sido identificado o código problemático, nós podemos começar a consertá-lo.</p>
    </section>
    <section id="optimization-massif-TBL-acting-on-results">
        <title>Atuando sobre os resultados</title>
        <p>Reduzir o consumo de espaço-tempo é bom, porém há duas formas de reduzi-lo e elas não são iguais. Você pode tanto reduzir a quantidade de memória alocada ou reduzir o montante de tempo que é alocado para isto. Por um momento, considere um modelo de sistema com apenas dois processos em execução. Ambos os processos utilizam quase toda a memória RAM física e se eles se sobrepõem completamente, então o sistema irá utilizar a memória de troca (swap) e tudo vai ficar lento. Obviamente, se nós reduzimos a utilização de memória de cada processo por um fator de dois, então eles podem coexistir pacificamente sem a necessidade de utilizar a memória de troca (swap). Se ao invés disso, nós reduzimos o tempo o qual a memória é alocada por um fator de dois, então os dois programas podem coexistir, mas apenas enquanto seus períodos de alta utilização de memória não sobreporem-se. Logo, é melhor reduzir o montante de memória alocada.</p>
        <p>Infelizmente, a escolha de otimização também é reprimida pelas necessidades do programa. O tamanho dos dados do pixbuf no <app>Swell Foop</app> é determinado pelo tamanho dos gráficos do jogo e não pode ser reduzido facilmente. Entretanto, a quantidade de tempo que ele gasta sendo carregado para dentro da memória, pode ser drasticamente reduzido. A imagem abaixo mostra a análise feita pelo <app>Massif</app> do <app>Swell Foop</app> após se dispor do pixbufs, uma vez que as imagens foram carregadas dentro do servidor X.</p>
        <figure>
            <title>Saída do <app>Massif</app> para o programa <app>Swell Foop</app> otimizado.</title>
           <media type="image" src="figures/massif-after.png"/>
            </figure>
        <p>A utilização de espaço-tempo do gdk_pixbuf_new é agora uma faixa fina que apenas sofre picos de forma leve (é a décima-sexta faixa para baixo e sombreada de magenta). Como bônus, o pico de utilização de memória caiu 200 kB, já que o pico ocorre antes de outra memória ser alocada. Se dois processos como este são executados juntos, as chances do pico de utilização de memória coincidirem, e por isso o risco de usar memória de troca (swap), seriam bastante baixos.</p>
        <p>Nós podemos fazer melhor? Uma verificação rápida na saída de texto do <app>Massif</app> revela: g_strdup como o novo maior ofensor.</p>
        <code>
Comando: ./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)
        </code>
        <p>Se olhamos mais de perto, observamos que ele é chamado de muitos, muitos, lugares.</p>
        <code>
== 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
        </code>
        <p>Agora nós nos deparamos com retornos cada vez menores para os nossos esforços de otimização. O gráfico sugere outra possível abordagem: Tanto as faixas “outros” e “gerenciamento de heap” são bem grandes. Isto nos diz que há um monte de pequenas alocações sendo feitas a partir de uma variedade de lugares. Eliminá-las será difícil porém se elas puderem ser agrupadas, então as alocações individuais poderão ser maiores e a elevada faixa “gerenciamento de heap” poderá ser reduzida.</p>
    </section>
    <section id="optimization-massif-TBL-caveats">
        <title>Advertências</title>
        <p>Existem um par de coisas as quais devemos ficar atentos: Primeiramente, espaço-tempo é apenas informado como uma porcentagem, você deve compará-lo ao tamanho total do programa, para decidir se vale a pena você ir atrás de quantidade de memória. O gráfico, com seu eixo vertical em kilobytes, é bom para isto.</p>
        <p>Em segundo lugar, o <app>Massif</app> apenas leva em consideração a memória utilizada pelo seu próprio programa. Recursos como pixmaps são armazenados no servidor X e não são considerados pelo <app>Massif</app>. No exemplo do <app>Swell Foop</app> nós realmente só mudamos o consumo de memória do lado do cliente do pixbufs para o lado do servidor pixmaps. Apesar de termos trapaceado, ocorreram ganhos de performance. Manter os dados de imagem no servidor X, faz com que as rotinas dos gráficos fiquem mais rápidas e remove uma grande quantidade de comunicações entre processos (ipcs). Ademais, os pixmaps serão armazenados em um formato gráfico nativo que geralmente é mais compacto que o formato RGBA de 32-bits utilizado pelo gdk_pixbuf. Para medir o efeito de pixmaps, e outros recursos X, utilize o programa <link href="http://www.freedesktop.org/Software/xrestop">xrestop</link>.</p>
    </section>
</page>