Transcript
Page 1: Técnicas de Programação: Profiling de Programas

J P Sauvé - DSC/UFPb

1

Técnicas de Programação: Técnicas de Programação: Profiling de ProgramasProfiling de Programas

Jacques Philippe Sauvé

Page 2: Técnicas de Programação: Profiling de Programas

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

Page 3: Técnicas de Programação: Profiling de Programas

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)

Page 4: Técnicas de Programação: Profiling de Programas

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

Page 5: Técnicas de Programação: Profiling de Programas

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

Page 6: Técnicas de Programação: Profiling de Programas

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

Page 7: Técnicas de Programação: Profiling de Programas

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"); }}

Page 8: Técnicas de Programação: Profiling de Programas

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

Page 9: Técnicas de Programação: Profiling de Programas

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!

Page 10: Técnicas de Programação: Profiling de Programas

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

Page 11: Técnicas de Programação: Profiling de Programas

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()

Page 12: Técnicas de Programação: Profiling de Programas

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; } ...}

Page 13: Técnicas de Programação: Profiling de Programas

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!

Page 14: Técnicas de Programação: Profiling de Programas

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!

Page 15: Técnicas de Programação: Profiling de Programas

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

Page 16: Técnicas de Programação: Profiling de Programas

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; }

Page 17: Técnicas de Programação: Profiling de Programas

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?

Page 18: Técnicas de Programação: Profiling de Programas

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; }

Page 19: Técnicas de Programação: Profiling de Programas

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

Page 20: Técnicas de Programação: Profiling de Programas

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

Page 21: Técnicas de Programação: Profiling de Programas

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

Page 22: Técnicas de Programação: Profiling de Programas

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)

Page 23: Técnicas de Programação: Profiling de Programas

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)

Page 24: Técnicas de Programação: Profiling de Programas

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

Page 25: Técnicas de Programação: Profiling de Programas

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.

Page 26: Técnicas de Programação: Profiling de Programas

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!)

Page 27: Técnicas de Programação: Profiling de Programas

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