Esse artigo faz parte da revista Clube Delphi edição 51. Clique aqui para ler todos os artigos desta edição



Atenção: por essa edição ser muito antiga não há arquivo PDF para download. Os artigos dessa edição estão disponíveis somente através do formato HTML. 

 

Profiling na prática

Otimize a performance de sua aplicação

 

Finalmente, a aplicação está pronta, funcionando como deveria, a maior parte dos bugs já foi encontrada e corrigida. Após alguns dias de trabalho, o usuário chama e fala: "a aplicação está ok, funciona direitinho, mas está muito lenta, assim não dá para trabalhar”. Este é um problema muito comum: ao desenvolvermos uma aplicação, nos preocupamos com a interface, com os cálculos e com a robustez, mas esquecemos de uma coisa muito importante e que irá dar uma impressão geral sobre a aplicação: sua velocidade.

Nossa primeira reação é solicitar ao cliente que adquira máquinas mais rápidas ou com mais memória. Em geral isso é inviável ninguém gosta de trocar um parque de 50 máquinas de uma vez só. Então resta apenas uma solução: rever o programa, reescrevendo as partes mais lentas, para que fiquem rápidas.

Mas por onde começar? O programa é muito extenso e reescrevê-lo por inteiro iria jogar fora todo o trabalho feito até agora. Uma idéia é executar o programa e verificar as partes mais lentas, reescrevendo os módulos correspondentes à elas. Isso, além de tedioso, pode levar a reescrever partes que não deveriam ser reescritas - o verdadeiro gargalo pode estar longe de onde parece estar.

Este artigo mostrará a metodologia de "profiling” para verificar os pontos em que o programa é mais lento e, assim, reescrever apenas as partes realmente importantes, minimizando esforço e tempo. Após uma introdução à medida do tempo de execução, serão apresentadas três ferramentas de profiling, que irão auxiliar na tarefa de detectar os verdadeiros gargalos do programa.

 

Detectando os gargalos da aplicação

Quando uma aplicação está lenta, reescrevê-la não é uma solução, pois com certeza apenas algumas poucas rotinas causam toda a lentidão do programa. Normalmente, 10 a 20% do código ocupam 90% do tempo de execução, portanto não é necessário reescrever toda a aplicação, apenas tornar mais rápidos aqueles 10 a 20% do código.

A primeira idéia que se tem desse problema é: "devo reescrever estas partes em assembly, pois o Delphi não irá dar a velocidade que necessito': Isto, em geral, é completamente falso: o problema normalmente está em um algoritmo ineficiente que, quando modificado, pode dar ganhos de produtividade da ordem de 10 vezes.

Outras vezes, a lentidão do programa é devido a uma sentença SQLmal escrita. Raríssimos casos requerem que uma rotina seja escrita em assembly, em geral o otimizador do Delphi irá gerar códigos bastante eficientes, dispensando o uso de assembly.

O que deve ser feito é procurar os pontos fracos, verificar e reescrever os algoritmos que causam lentidão. Uma maneira de procurar os locais mais lentos do programa é colocando medidas de tempo no programa, de maneira a ver quanto tempo o programa gasta nas rotinas.

Uma maneira de medir o tempo é usar a função da API. GetTickCount. Ela retoma o número de milissegundos passados, desde a inicialização do Windows.

Faremos um pequeno exemplo. Abra o Delphi e salve o projeto em um diretório à sua escolha. Adicione no formulário um botão e no evento OnClick digite o código:

 

procedure TForm1.Button1Click(Sender: TObject);

var

   HoraAnterior: Cardinal;

begin

   HoraAnterior := GetTickCount;

   { Rotina a ser medida }

   ShowMessage(IntToStr(GetTickCount- HoraAnterior));

end;

 

Este código mostrará o tempo que a rotina demorou para ser executada, em milissegundos. Embora GetTickCount possa dar uma medida do tempo decorrido, ela não é muito precisa, pois está ligada ao clock do PC e, em geral, tem uma resolução de 55 ms. Para medidas mais precisas, da ordem de 1 ms, é necessário usar um time r de alta resolução, obtido com as funções

QueryPerformanceFrequency e QueryPerformanceCounter.

Essas duas funções acessam o contador de hardware de alta precisão, se existir um. Para usá-las, deve-se obter a freqüência do contador, em batidas por segundo,com QueryPerformanceFrequency, e seu valor atual, com QueryPerformanceCounter:

 

procedure TForm1.ButtonlClick(Sender: TObject):

var

   Inicio, Final. Frequencia: Int64;

begin

   if not QueryPerformanceFrequency(Frequencia) then ...

Quer ler esse conteúdo completo? Tenha acesso completo