J P Sauvé - DSC/UFPb
1
Técnicas de Programação: Técnicas de Programação: Profiling de ProgramasProfiling de Programas
Jacques Philippe Sauvé
J P Sauvé - DSC/UFPb 2
Introdução Introdução
Problemas freqüentes de programas:– Lentidão devido a consumo de CPU– Alocação excessiva de memória (memory
leak)– Deadlocks entre threads
Este seminário mostra como resolver esses problemas usando ferramentas chamadas perfiladores
J P Sauvé - DSC/UFPb 3
ProgramaçãoProgramação
Visão geral de profiling– (5 minutos)
Profiling para descobrir gargalos de CPU– (45 minutos)
Profiling para descobrir memory leaks– (20 minutos)
Profiling para descobrir deadlocks– (30 (minutos)
J P Sauvé - DSC/UFPb 4
Visão geral Visão geral
Os três problemas relacionados são freqüentes
Não se deve resolvê-los no chute!A chave é
instrumentar o programa para que ele mesmo nos diga o que está errado!
CPUDeadlock
Instrumentação
Deadlock
MEMMEMCPU
J P Sauvé - DSC/UFPb 5
VocabulárioVocabulário
Perfilador: programa que fornece informação sobre os detalhes da execução de um outro programa
hprof: Perfilador da JVM padrão– java –Xrunhprof:[opções] classe
J P Sauvé - DSC/UFPb 6
Profiling de CPU – Número Profiling de CPU – Número PrimosPrimosO programa imprime o número de primos
menores que um certo número máximoExecute o programa:
– java Primo1 100001229481 milissegundos
J P Sauvé - DSC/UFPb 7
Primeira VersãoPrimeira Versãopublic class Primo1 { public static boolean primo(int n) { for(int i = 2; i < n; i++) { if(n % i == 0) { return false; } } return true; }
public static void main(String[] args) { int max = Integer.parseInt(args[0]); int númeroPrimos = 0; long horaInicial = System.currentTimeMillis(); for(int i = 2; i <= max; i++) { if(primo(i)) { númeroPrimos++; } } System.out.println(númeroPrimos); System.out.println((System.currentTimeMillis() –
horaInicial)+" milissegundos"); }}
J P Sauvé - DSC/UFPb 8
Primeira Versão: Tempo de Primeira Versão: Tempo de ExecuçãoExecuçãoMax 10000: 0,481 segundosMax 100000: 36,392 seg.Max 1000000: ?
– (não tive paciência de esperar)O programa é obviamente CPU-bound
J P Sauvé - DSC/UFPb 9
Primeira Versão: Como Primeira Versão: Como Melhorar?Melhorar?Vamos conseguir melhorias de pelo menos
200 vezes nesse programaComo fazer?Não chute ... Instrumente!
J P Sauvé - DSC/UFPb 10
Primeira versão: ProfilingPrimeira versão: Profiling
Comando:– java -Xrunhprof:cpu=times,depth=10 Primo5
100000Cpu=times pede a contagem de chamadas
de métodosDepth=10 pede no máximo profundidade
10 no stack backtrace
J P Sauvé - DSC/UFPb 11
Resultado do ProfilingResultado do ProfilingExamine o arquivo java.hprof.txtrank self accum count trace method 1 98.44% 98.44% 99999 8 Primo1.primo 2 1.28% 99.72% 1 6 Primo1.main 3 0.03% 99.75% 1 13 java.security.Policy$1.run...
Nosso problema é obviamente tempo demais sendo gasto no método primo()
Primeira lição: Para diminuir o gargalo de primo, pode-se:– Chamar o método menos– Tornar o método mais rápido
Aqui, decidimos não “fuçar” main(): concentre-se em primo()
J P Sauvé - DSC/UFPb 12
Segunda versãoSegunda versão Como rodar primo() mais rapidamente? Que tal rodar menos no laço?
– Basta testar fatores até sqrt(n)public class Primo2 { public static int raiz(int n) { return (int)Math.sqrt((double)n); }
public static boolean primo(int n) { for(int i = 2; i <= raiz(n); i++) { if(n % i == 0) { return false; } } return true; } ...}
J P Sauvé - DSC/UFPb 13
Segunda versão - profilingSegunda versão - profiling
É mais rápida que a primeira:– P1: 10K/100K/1M: 0,481/36,392/?
– P2: 10K/100K/1M: 0,060/1,132/27,189 Onde está o gargalo?rank self accum count trace method 1 57.10% 57.10% 2755286 15 Primo2.raiz 2 40.99% 98.09% 99999 17 Primo2.primo 3 1.36% 99.45% 1 12 Primo2.main
Veja quantas vezes raiz() foi chamada!
J P Sauvé - DSC/UFPb 14
Terceira versão: chame sqrt Terceira versão: chame sqrt menosmenos public static boolean primo(int n) { int limite = raiz(n); for(int i = 2; i <= limite; i++) { if(n % i == 0) { return false; } } return true; }Os tempos passam para:
– P3: 10K/100K/1M: 0,020/0,311/6,720– Já está 100 vezes mais rápido!
J P Sauvé - DSC/UFPb 15
Terceira versão: perfilTerceira versão: perfil
rank self accum count trace method 1 50.02% 50.02% 99999 20 Primo3.primo 2 25.37% 75.38% 99999 14 Primo3.raiz 3 17.48% 92.86% 1 10 Primo3.main
raiz() caiu muitoTemos que melhorar primo ainda
J P Sauvé - DSC/UFPb 16
Quarta versão: tentativaQuarta versão: tentativa Com casos especiais, poderemos reduzir o tempo? public static boolean primo(int n) { if(n % 2 == 0) return false; if(n % 3 == 0) return false; if(n % 5 == 0) return false; int limite = raiz(n); for(int i = 7; i <= limite; i += 2) { if(n % i == 0) return false; } return true; }
J P Sauvé - DSC/UFPb 17
Quarta versão: bugQuarta versão: bug
A saída de Primo1 10000 é1129
A saída de Primo4 10000 é1126
O tempo melhorou:– P3: 10K/100K/1M: 0,020/0,311/6,720– P4: 10K/100K/1M: 0,020/0,151/2,924
Tem um bug!– Mesmo com o programa mais rápido, uma regra básica é
mantê-lo funcionando enquanto se o deixa mais rápido!– Cadê o bug?
J P Sauvé - DSC/UFPb 18
Quinta versão: outra tentativaQuinta versão: outra tentativa Consertamos o bug e retiramos sqrt() public static boolean primo(int n) { if(n % 2 == 0) return n == 2; if(n % 3 == 0) return n == 3; if(n % 5 == 0) return n == 5; for(int i = 7; i * i <= n; i += 2) { if(n % i == 0) return false; } return true; }
J P Sauvé - DSC/UFPb 19
Quinto programa: tempo e Quinto programa: tempo e profileprofileTempo não melhorou:
– P4: 10K/100K/1M: 0,020/0,151/2,924
– P5: 10K/100K/1M: 0,020/0,151/3,285
Não entendi bem por quê!Último perfil:rank self accum count trace method 1 53.77% 53.77% 99999 17 Primo5.primo 2 34.67% 88.44% 1 9 Primo5.main
J P Sauvé - DSC/UFPb 20
Profiling para Achar Memory Profiling para Achar Memory LeaksLeaks
Memory Leaks são bugs de programas que esquecem de liberar memória alocada– O programa fica cada vez maior– Problema muito sério para programas servidores que
ficam sempre no ar Menos freqüente para Java do que C/C++ devido
ao garbage collection– Em Java, há memory leak se você continuar a
referenciar um objeto
J P Sauvé - DSC/UFPb 21
O Programa MemoryLeak.javaO Programa MemoryLeak.java
Veja aqui10000 objetos MemoryConsumer de
1KBytes cada são alocadosExecute o programa:
– java -Xrunhprof:heap=sites,depth=10 MemoryLeak
J P Sauvé - DSC/UFPb 22
O perfil de MemoryLeak.javaO perfil de MemoryLeak.java percent live alloc'ed stack class rank self accum bytes objs bytes objs trace name 1 88.67% 88.67% 2600000 10000 2600000 10000 185 [B 2 5.10% 93.77% 149524 157 149524 157 1 [I 3 1.36% 95.14% 40000 10000 40000 10000 175 MemoryConsumer 4 0.72% 95.86% 21140 1057 200000 10000 169 [C 5 0.66% 96.52% 19466 741 19626 754 1 [C
[B significa “Array de bytes”, etc. O que causou a alocação toda?
– Veja o trace 185: vem de main que chama o construtor de MemoryConsumer
TRACE 185:MemoryConsumer.<init>(MemoryLeak.java:36)MemoryLeak.main(MemoryLeak.java:16)
J P Sauvé - DSC/UFPb 23
Um programa de deadlockUm programa de deadlock
Veja o programa de deadlock aqui Em ClassicDeadlock:
– Dois objetos equivalentes são criados (left e right)– Dois threads pegam locks nesses objetos numa ordem
particular Execute o programa
– java -Xdebug ClassicDeadlock
– Eventualmente, um deadlock vai ocorrer em que cada thread espera por um lock que o outro thread detém
– Mate o programa com CTRL-\ (UNIX) ou CTRL-BREAK (Windows)
J P Sauvé - DSC/UFPb 24
Diagnóstico de DeadlocksDiagnóstico de Deadlocks
Found one Java-level deadlock:============================="RightToLeft": waiting to lock monitor 0x8092e34 (object 0x44493a10, a
SynchronizedObject), which is held by "LeftToRight""LeftToRight": waiting to lock monitor 0x8092dfc (object 0x44493a20, a
SynchronizedObject), which is held by "RightToLeft"
O deadlock pode ser identificado, acima
J P Sauvé - DSC/UFPb 25
Diagnóstico de DeadlocksDiagnóstico de DeadlocksJava stack information for the threads listed above:==================================================="RightToLeft":
at SynchronizedObject.synchronizedMethod(ClassicDeadlock.java:30)- waiting to lock <0x44493a10> (a SynchronizedObject)at RightToLeftGrabber.grabRightToLeft(ClassicDeadlock.java:68)- locked <0x44493a20> (a SynchronizedObject)at RightToLeftGrabber.run(ClassicDeadlock.java:62)
"LeftToRight":at SynchronizedObject.synchronizedMethod(ClassicDeadlock.java:30)- waiting to lock <0x44493a20> (a SynchronizedObject)at LeftToRightGrabber.grabLeftToRight(ClassicDeadlock.java:88)- locked <0x44493a10> (a SynchronizedObject)at LeftToRightGrabber.run(ClassicDeadlock.java:82)
Found 1 deadlock.
J P Sauvé - DSC/UFPb 26
ResumoResumo
Aprendemos que:– Três problemas freqüentes de execução de programas são:
Excesso de consumo de CPU Memory leak Deadlocks
– É importante usar ferramentas adequadas para diagnosticar esses problemas
– Uma boa ferramenta é um “perfilador”– Ele adquire estatísticas do programa durante sua execução
e apresenta os resultados de forma legível (mais ou menos!)
J P Sauvé - DSC/UFPb 27
Onde obter mais informaçõesOnde obter mais informações
Livros– Programming Pearls, Bentley– More Programming Pearls, Bentley
Artigo na Web– Diagnose Common Runtime Problems with
Hprof, Pierce, JavaWorld, dezembro 2001
Top Related