Artigos - Detalhes
Titulo: O Desempenho, o Programador e a Velocidade da ExecuçãoDescricao: Dicas simples sobre codificação e desempenho.
______________________________________________________________________________________________
Depois de alguns anos olhando códigos pelo mercado afora, descobri que a maioria das pessoas não percebe ou não se importa com o desempenho que uma aplicação pode sofrer, devido a maneira como foi codificada. Antigamente, os programadores de linguagens como C, não tinham tantos recursos físicos (memória RAM livre) e portanto sua preocupação era dividida entre os recursos disponíveis e o negócio ao qual sua aplicação deveria atender.
Com o passar do tempo a preocupação com os recursos deixou de existir, pois os servidores e até mesmo desktops começaram a ser vendidos com memória de sobra. Aplicações escritas em Java, não precisam se preocupar com o gerenciamento dos recursos disponíveis, pois a JVM faz esse trabalho, e o faz com excelência.
Ainda assim a JVM não faz milagres, e por isso é preciso estar atento para pontos de lentidão causados pela má codificação. Abaixo irei demonstrar os erros mais comuns e também irei dar uma alternativa, o interessante é que os maiores gargalhos acontecem durante o uso de coleções. O tempo de mediçao não foi feito com uma ferramenta de benchmark, portanto pode haver alguma divergência (pequena) nos valores.
DICA 1 - Existem diversos tipos de implementações para se trabalhar com coleções em Java, e a maioria dos casos se resume a um List ou Set. Ambas são implementações rápidas, em alguns testes, cheguei a uma coleção com 90.000 registros e iterei sobre ela até o último registro, consegui completar a iteração com o seguinte resultado. Este teste foi feito com uma lista inicializada num bloco estático, e o tempo abaixo corresponde somente ao tempo de execução da interação.
TESTE 1 - Inicio: 55m:42s:629ml Fim: 55m:42s:661ml
TESTE 2 - Inicio: 58m:59s:674ml Fim: 58m:59s:721ml
Depois disso eu adicionei uma linha dentro da interação para imprimir no console cada registro da coleção e o resultado mudou para:
TESTE 1 - Inicio: 15m:54s:340ml Fim: 16m:9s:185ml
TESTE 2 - Inicio: 16m:28s:573ml Fim: 16m:43s:543ml
Daqui podemos tirar a primeira dica, nunca use métodos de log dentro de uma lista, a escrita no IO consome muito tempo.
DICA 2 - É comum ver trechos de código interando sobre uma coleção, em busca de um registro específico. Você faz um loop, e dentro do loop vai comparando seu objeto com o objeto da coleção, até encontrar o que busca. O resultado abaixo é idêntico ao que obtemos no teste acima, interamos sobre a lista até achar nosso objeto. Uma dica interssante é sempre usar o comando break; assim que encontrar o seu objeto. Assim você reduz o tempo de execução por não percorrer a coleção inteira. Busquei o registro de número 80.000 dentro dos 90.000 registros.
Sem o uso do break:
TESTE 1 - Inicio: 27m:45s:781ml Fim: 27m:45s:813ml
TESTE 2 - Inicio: 28m:19s:577ml Fim: 28m:19s:608ml
Com o uso do break
TESTE 1 - Inicio: 37m:12s:112ml Fim: 37m:12s:112ml
TESTE 2 - Inicio: 37m:18s:819ml Fim: 37m:18s:819ml
Perceba que utilizando o break, o tempo já foi reduzido, mas a dica de número 2 não é o break e sim os comandos indexOf(Object) e get(int). Utilizando este métodos você não precisa interar sobre a sua lista, para descobrir se existe o objeto necessário dentro dela. Basta usar o comando que lhe informa a posição do objeto (indexOf) e em seguida acessa-lo via o comando get. Dessa maneira nossa procura pelo registro de número 80.000 dentro da lista com 90.000 foi executada em:
TESTE 1 - Inicio: 35m:58s:348ml Fim: 35m:58s:348ml
TESTE 2 - Inicio: 36m:32s:729ml Fim: 36m:32s:729ml
Com o uso de ambos os comandos reduzimos para "zero" o tempo de processamento da nossa coleção, além do uso ficar mais elegante no código, veja a diferença:
DICA 3 - Esta dica é complementar à dica 2, pois sem ela você não conseguirá precisão na busca pelos seus registros. Estou falando do equalshascode. O correto é escrever equals e hash code, mas eu costumo escrever tudo junto para que as pessoas entendam que nenhum ou ambos devem ser escritos, nunca somente um deles. Dicas para escrever o equalshashcode podem ser encontradas no blog da caelum, e eu não pretendo reescrever aqui tudo de novo, blog caelum
Implementando equalshashcode você consegue precisão nas suas buscas, permite que a JVM distribua seus elementos dentro de um hashmap da maneira correta. E ainda por cima aumenta a sua visão sobre o entendimento da sua aplicação, pois você será obrigado a entender o que diferencia as sua entidades e quais são os campos que comões a identidade do objeto.
DICA 4 - Não caia no mito de que código cheio de "if" é código lento ou código ruim. Muitos programadores se pegam pensando se encher o código com condições torna o desempenho da aplicação mais lento, o fato é que essa estória não passa de um mito. Nos piores cenários vi programadores trocando "if" por "switch". Quem disse que um "switch" é mais rápido que um "if" ou vice-versa. A única verdade nisso tudo é o código ruim, isto sim pode vir a acontecer, caso existam muitas condições agrupadas no seu código. Se isto está acontecendo procure usar padrões de projeto que lhe auxiliem a dividir a sua lógica, para este caso os mais comuns são os padrões de comportamento (lembrando que os padrões da GoF estão divididos em três tipos: comportamento, criação e estrutural). Recomento a leitura do Strategy, Command e Chain of responsibility.
Por hora vou parar este post aqui, para não tornar deste artigo um mini livro.