<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>TechTips &#187; Performance</title>
	<atom:link href="http://www.techtips.com.br/category/programacao/performance/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.techtips.com.br</link>
	<description>Dicas de Tecnologia</description>
	<lastBuildDate>Wed, 24 Jun 2009 19:06:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Otimiza&#231;&#227;o de C&#243;digo, parte II: Conhecendo Gargalos e Profilers</title>
		<link>http://www.techtips.com.br/programacao/performance/otimizacao-de-codigo-parte-ii-conhecendo-gargalos-e-profilers/</link>
		<comments>http://www.techtips.com.br/programacao/performance/otimizacao-de-codigo-parte-ii-conhecendo-gargalos-e-profilers/#comments</comments>
		<pubDate>Tue, 26 Aug 2008 18:32:03 +0000</pubDate>
		<dc:creator>Leonel Togniolli</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.techtips.com.br/programacao/performance/otimizacao-de-codigo-parte-ii-conhecendo-gargalos-e-profilers/</guid>
		<description><![CDATA[Já vi acontecer inúmeras vezes: o sistema é escrito, testado, e funciona bem. É instalado em produção e funciona por uma semana ou duas. Então começa a ficar extremamente lento, a ponto de não ser mais usável. A sequência é também muito comum: o programador vai “otimizando” o código no escuro, usando a intuição, e [...]]]></description>
			<content:encoded><![CDATA[<p>Já vi acontecer inúmeras vezes: o sistema é escrito, testado, e funciona bem. É instalado em produção e funciona por uma semana ou duas. Então começa a ficar extremamente lento, a ponto de não ser mais usável. A sequência é também muito comum: o programador vai “otimizando” o código no escuro, usando a intuição, e perde muito tempo fazendo pequenas melhorias.</p>
<p>A solução para a primeira parte do problema é simples, mas geralmente ignorada: sempre teste utilizando um volume considerável de dados. É simples testar com dois ou três registros que você mesmo cadastrou, mas é importantíssimo garantir que a performance continue adequada com um volume de dados real. Utilize uma cópia de dados de produção caso existam e estejam disponíveis, ou utilize um gerador de dados (como o exemplo&#160; que acompanha o DBX4) para popular uma quantidade de registros compatível com o que volume esperado para produção. </p>
<p>O segundo problema é mais interessante. A intuição geralmente está errada – o que torna o código lento pode ser o que você menos espera. </p>
<h3>Gargalos</h3>
<p>Geralmente a lentidão é causada por gargalos no código, trecho de uma ou duas linhas que levam uma grande porcentagem do tempo total de processamento. Muitas vezes é fácil de encontrar uma alternativa para evitar essa demora, como reordenar o código para tratar a condição mais comum primeiro. Ao resolver um gargalo geralmente outro toma seu lugar, passando a ser responsável pela maioria da demora restante. A partir daí, resolver gargalos é uma questão de um retorno decrescente: cada otimização vai diminuir cada vez menos o retorno obtido.</p>
<p>Como um exemplo, vamos imaginar um hipotético processamento de registros que leve 10 segundos. Utilizando ferramentas apropriadas, descobrimos que a abertura da query para trazer os registros leva 80% do tempo. Analisando esta consulta, descobrimos uma forma de alterá-la para abrir praticamente instantaneamente. Agora o processamento leva cerca de 2 segundos. Se conseguirmos encontrar um novo gargalo que leve 80% do novo tempo e uma forma de removê-lo, ganhamos mais 1.6 segundos. Se repetirmos o processo mais uma vez, não conseguiremos melhorar mais que meio segundo. Se a qualquer momento trabalharmos em em outro trecho do código que não ser um desses três gargalos, estaremos perdendo nosso tempo e não teremos melhorias maiores que alguns milisegundos. É necessário bom senso para determinar a hora de parar.</p>
<p>Se não existe um gargalo claro, e a performance em geral ainda não é adequada, provavelmente é hora de procurar um algoritmo completamente diferente para resolver o problema.</p>
<p>Na prática nem sempre é possível remover gargalos completamente. Mesmo assim, deve se ter em mente que otimizar o restante do código muitas vezes não trará resultados perceptíveis.</p>
<p>No exemplo acima, escrevi que descobrimos o tempo levado por cada trecho do código utilizando “ferramentas apropriadas”. O que seria isso?</p>
<h3>Profilers</h3>
<p>Profilers são ferramentas que permitem determinar o tempo gasto na execução do seu código. Existem várias formas de fazer essa medição, desde uma forma arcaica manual, salvando o horário atual em um arquivo de log por exemplo, ou bem elaborada utilizando uma ferramenta profissional como o <a href="http://www.automatedqa.com/products/aqtime/">AQTime</a>. Utilizar uma ferramenta profissional é ótimo, é claro, mas é cara e algumas vezes até um exagero para um problema mais simples.</p>
<p>Existem duas categorias de profilers: Sampling Profilers e Intrumenting Profilers. A primeira faz medições (samples) regulares em pontos aleatórios (a cada milisegundo, por exemplo). Essa forma não afeta a execução do código, mas pode ser pouco precisa. Um Intrumenting Profiler adiciona código para efetuar medições em lugares exatos dentro do seu programa. Essa categoria se subdivide em profilers que instrumentam seu código fonte inserindo essas verificações, e profilers que alteram o código de máquina conforme ele é executado para inseri-las. Uma maior precisão é conseguida desta forma, mas sob o custo de alterar sutilmente o fluxo do seu código. Como na mecânica quântica, o ato de medir pode pode estar alterando o resultado. Na prática, nenhum dos dois é extremamente superior: deve-se pesar os prós e contras, e quem sabe até utilizar os dois tipos como uma boa alternativa para encontrar diferentes tipos de gargalos.</p>
<p>A forma mais simples de determinar o tempo levado por um método é utilizar uma forma simples e manual de um source intrumenting profiler &#8211; escrever código para salvar o tempo inicial e final, e exibir/armazenar esse valor. A glanularidade é exatamente qual quiser, até cada linha. A precisão depende da forma que o tempo for medido – não recomendo a função Now, pois não é muito precisa. GetTickCount é adequada para a maioria dos casos, tendo precisão de 1ms. Pode ser usada mais ou menos assim:</p>
<pre><b>var</b>
  t: Cardinal;
<b>begin</b>
  t := GetTickCount;
  EfetuaProcessamento;
  WriteLn(GetTickCount - t);
<b>end;</b></pre>
</p>
<p>Se uma precisão maior que 1ms for necessária, recomendaria a API QueryPerformanceCounter. A precisão dela depende do processador, e é necessário chamar QueryPerformanceFrequency para determiná-la. Outra alternativa é o opcode <strong>RDTSC</strong>, mnemônico para <em>read time stamp counter</em>, que também retorna o número de ticks desde que o processador foi reiniciado, assim como GetTickCount, mas com maior resolução. Essa alternativa não é recomendada hoje em dia por potenciais problemas relacionados a processadores multicore e processadores com clock variável (geralmente em portáteis).</p>
<p>Essa forma manual de profiling é claramente adequada apenas para pequenos problemas e testes rápidos. Para casos mais elaborados, deve-se considerar utilizar algum dos profilers disponíveis:</p>
<p><a href="http://www.prodelphi.de/">ProDelphi</a> é um source instrumenting profiler, portanto ele faz (e desfaz) alterações no seu código fonte. Possui uma versão gratuita, com limitações, e uma versão comercial por 47.50 €.</p>
<p><a href="http://17slon.com/gp/gpprofile/index.htm">GpProfile</a> é outro source intrumenting profiler. Gratuito, open source, mas sem atualizações por muitos anos.</p>
<p><a href="http://www.techtips.com.br/arquivos/SamplingProfiler-1.5.0.zip">Sampling Profiler</a> é, como o nome diz, um sampling profiler, Freeware, mas sem código fonte. Uma boa alternativa pra quem não quer gastar com ferramentas comerciais e não quer alterações no seu código fonte.</p>
<p><a href="http://www.automatedqa.com/products/aqtime/index.asp">AQTime</a> é, sem dúvida, a ferramenta mais avançada e completa. É um instrumenting profiler que só altera o código de máquina ao ser executado, então não requer nenhuma alteração no código fonte. Além de tempo gasto em procedimentos e linha por linha, possui modos onde verifica uso de memória, de recursos, e diversas outras análises extremamente interessantes. Custa $599.</p>
<p>Na terceira parte desta série pretendo apresentar um exemplo prático de otimização de um projeto usando um profiler.</p>
<p><center>&copy; <a href="http://www.techtips.com.br">TechTips</a></center></p>]]></content:encoded>
			<wfw:commentRss>http://www.techtips.com.br/programacao/performance/otimizacao-de-codigo-parte-ii-conhecendo-gargalos-e-profilers/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Otimiza&#231;&#227;o de C&#243;digo, parte I: Entendendo a Complexidade de Algoritmos</title>
		<link>http://www.techtips.com.br/programacao/performance/otimizacao-de-codigo-parte-i-entendendo-a-complexidade-de-algoritmos/</link>
		<comments>http://www.techtips.com.br/programacao/performance/otimizacao-de-codigo-parte-i-entendendo-a-complexidade-de-algoritmos/#comments</comments>
		<pubDate>Wed, 26 Mar 2008 14:44:24 +0000</pubDate>
		<dc:creator>Leonel Togniolli</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.techtips.com.br/programacao/performance/otimizacao-de-codigo-parte-i-entendendo-a-complexidade-de-algoritmos/</guid>
		<description><![CDATA[A otimiza&#231;&#227;o de c&#243;digo &#233; uma tarefa muitas vezes deixada de lado, e, muito pior, feita de formas erradas muitas vezes por inexperi&#234;ncia do programador, tendo como resultado programas que funcionam bem em testes com um volume pequeno de dados e que demoram horas conforme o volume de dados aumenta. Para entender os passos necess&#225;rios [...]]]></description>
			<content:encoded><![CDATA[<p>A otimiza&#231;&#227;o de c&#243;digo &#233; uma tarefa muitas vezes deixada de lado, e, muito pior, feita de formas erradas muitas vezes por inexperi&#234;ncia do programador, tendo como resultado programas que funcionam bem em testes com um volume pequeno de dados e que demoram horas conforme o volume de dados aumenta.</p>
<p>Para entender os passos necess&#225;rios para otimizar seu c&#243;digo de uma forma que tenha um bom resultado pr&#225;tico sem comprometer a qualidade do c&#243;digo, deve-se entender alguns conceitos importantes:</p>
<ol>
<li>Quando otimizar. </li>
<li>Como reconhecer qual parte do c&#243;digo toma mais tempo. </li>
<li>E o que fazer para diminuir o tempo tomado por essas partes do c&#243;digo. </li>
</ol>
<p>Vou elaborar o primeiro item hoje, e deixar os pr&#243;ximos dois para artigos futuros.</p>
<h3>Quando Otimizar</h3>
<p>Donald Knuth <a href="http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf">escreveu</a>, em 1974, a j&#225; famosa e gasta frase: &quot;<em>Devemos esquecer das pequenas efici&#234;ncias, cerca de 97% do tempo. Otimiza&#231;&#227;o prematura &#233; a raiz de todo o mal</em>&quot;. Em seguida, ele elabora que n&#227;o se deve ignorar os 3% restantes, mas apenas ap&#243;s que eles forem identificados.</p>
<p>Mais que isso, diz que a experi&#234;ncia universal de programadores (incluindo a minha), &#233; que ap&#243;s utilizar ferramentas para medir a performance do c&#243;digo, voc&#234; percebe que o que as tentativas de adivinhar quais s&#227;o esses trechos cr&#237;ticos geralmente s&#227;o falhas. &#201; f&#225;cil perceber que tempo gasto otimizando partes do c&#243;digo que n&#227;o s&#227;o cr&#237;ticas para a performance &#233; uma perda de tempo, e acabam tendo um impacto negativo quando se considera o tempo que ser&#225; levado para depurar e manter esse c&#243;digo, agora mais complexo por otimiza&#231;&#245;es desnecess&#225;rias. </p>
<p>O retorno da otimiza&#231;&#227;o costuma seguir o <a href="http://pt.wikipedia.org/wiki/Principio_de_Pareto">Principio de Pareto</a>, que postula que apenas 20% do c&#243;digo &#233; respons&#225;vel por cerca de 80% da performance. Dessa forma, mesmo que se diminua pela metade o tempo levado por um trecho espec&#237;fico de c&#243;digo, se ele s&#243; influencia em 1% no tempo total de determinado processo, voc&#234; s&#243; teve 0.5% de ganho de performance. Por outro lado, ao identificar um trecho que leva 30% do tempo, qualquer melhoria de performance nele ter&#225; um retorno 30 vezes maior. Na pr&#225;tica, vejo porcentagens ainda maiores que essa, acentuando ainda mais a importancia de localizar corretamente os trechos que levam mais tempo, conhecidos como gargalos, ou bottlenecks, se preferir um <a href="http://rafaelrgi.wordpress.com/2007/11/27/mim-fala-portuges/">informatiqu&#234;s</a> mais elaborado.</p>
<p>Isso quer dizer, que, na pr&#225;tica, ao escrever c&#243;digo, evite tomar qualquer decis&#227;o no impacto de performance que ela vai ter. Decis&#245;es como usar uma ou outra fun&#231;&#227;o para realizar determinada tarefa devem ser tomadas com base nos m&#233;ritos de legibilidade e mantenabilidade do c&#243;digo, muito acima de qualquer suposta diferen&#231;a de performance entre elas. N&#227;o deixe a intui&#231;&#227;o de que algum ponto ser&#225; um gargalo influenciar essa decis&#227;o (lembre-se, intui&#231;&#245;es geralmente s&#227;o desbancadas por medi&#231;&#245;es de performance). Qualquer escolha errada nesse ponto &#233; facilmente adequada nas fases de medi&#231;&#227;o e otimiza&#231;&#227;o.</p>
<p>O que isso n&#227;o quer dizer, em contrapartida, &#233; que se deve ignorar a performance na hora de planejar a solu&#231;&#227;o que vai ser implementada, antes da codifica&#231;&#227;o. &#201; importante saber o que est&#225; sendo implementado e qual a complexidade dessa solu&#231;&#227;o. Deve-se estimar a quantidade de itens que o c&#243;digo deve ser capaz de processar, e adequar a complexidade do algoritmo e estruturas de dados escolhidas para obter uma performance apropriada a esse n&#250;mero. Uma das solu&#231;&#245;es na etapa de diminuir o tempo tomado por determinado gargalo &#233; substituir o algoritmo por um mais eficiente, mas tal substitui&#231;&#227;o costuma ser trabalhosa e demanda mais trabalho que um planejamento adequado tomaria.</p>
<h3>Complexidade de Algoritmos</h3>
<p>A complexidade de algoritmos &#233; uma medida da escalabilidade de um algoritmo. De quanto trabalho ele precisa fazer para resolver o problema conforme o n&#250;mero de itens aumenta. Existe uma forma padr&#227;o de analisar e representar a complexidade de algoritmos, que &#233; a nota&#231;&#227;o Big-O (raramente traduzida para Grande-O). Para determinar a complexidade de um algoritmo, conta-se o n&#250;mero de opera&#231;&#245;es que ele faz para cada item, onde opera&#231;&#227;o pode ser compara&#231;&#245;es, trocas, leituras de disco, etc. Em seguida, &#233; determinada uma express&#227;o que representa essa quantidade. Uma complexidade O(n), ou linear, representa que o n&#250;mero de opera&#231;&#245;es &#233; constante e proporcional ao n&#250;mero de itens na lista. O(n<i></i>&#178;), ou quadr&#225;tica, mostra a propor&#231;&#227;o que o n&#250;mero de opera&#231;&#245;es cresce conforme o n&#250;mero de itens aumenta.</p>
<p>&#201; muito mais f&#225;cil de entender essa representa&#231;&#227;o graficamente:</p>
<p><a href="http://www.techtips.com.br/wp-content/uploads/2008/03/bigo.png"><img height="227" alt="O(n) vs O(n&#178;)" src="http://www.techtips.com.br/wp-content/uploads/2008/03/bigo-thumb.png" width="483" border="0" /></a> </p>
<p>A figura representa o n&#250;mero de opera&#231;&#245;es efetuadas por dois algoritmos hipot&#233;ticos, um O(n) e o segundo O(n<i></i>&#178;). Algumas caracter&#237;sticas importantes:</p>
<ul>
<li>&#201; f&#225;cil de observar a falta de escalabilidade do segundo algoritmo. Conforme o n&#250;mero de itens aumenta, o n&#250;mero de opera&#231;&#245;es aumenta muito rapidamente. </li>
<li>N&#227;o &#233; um gr&#225;fico de f(x) = n e g(x)=x&#178;, apesar que esse seria um exemplo igualmente v&#225;lido (e mais simples). A express&#227;o mostrada pela nota&#231;&#227;o O n&#227;o &#233; exatamente o n&#250;mero de opera&#231;&#245;es que v&#227;o ser executadas, mas sim uma representa&#231;&#227;o de como esse n&#250;mero muda em rela&#231;&#227;o &#224; quantidade de itens processados. N&#227;o existe O(n + 10) nem O(10n). Ambos seriam representados por O(n). Dois algoritmos podem ter a mesma complexidade mas um efetuar mais opera&#231;&#245;es, ou o mesmo n&#250;mero de opera&#231;&#245;es e levar um tempo maior que o outro (se a opera&#231;&#227;o for mais demorada). </li>
<li>O algoritmo mais complexo pode levar menos tempo que o menos complexo. O tempo extra gasto no caso mais simples pode ser a gera&#231;&#227;o de uma tabela de pesquisa, por exemplo, que pode levar mais tempo do que a busca por for&#231;a em um n&#250;mero pequeno de itens. Supondo que no gr&#225;fico acima a opera&#231;&#227;o efetuada pelos dois algoritmos seja equivalente, o mais complexo vai ser mais r&#225;pido at&#233; um pouco mais de 150 itens. Se o n&#250;mero de itens vai ser baixo (por exemplo, processar as janelas abertas em um editor de imagens), a segunda op&#231;&#227;o pode ser a mais adequada. Se o n&#250;mero for alto (ou desconhecido), deve-se tomar uma extrema aten&#231;&#227;o na complexidade do algoritmo que ser&#225; usado. </li>
</ul>
<p>Sabendo desses detalhes, fica mais f&#225;cil determinar a complexidade de um algoritmo qualquer. Um exemplo seria a busca de um item em uma lista desordenada. Em pseudoc&#243;digo:</p>
<pre>resultado = -1
de i := 1 at&#233; Lista.Count
  se Lista[i] = Item
    resultado = i
retorne resultado</pre>
<p>A opera&#231;&#227;o que est&#225; sendo contada &#233; a compara&#231;&#227;o. O n&#250;mero de opera&#231;&#245;es &#233; sempre igual ao n&#250;mero de itens na lista, claramente O(n). Uma otimiza&#231;&#227;o &#243;bvia reduz o n&#250;mero de compara&#231;&#245;es:</p>
<pre>de i := 1 at&#233; Lista.Count
  se Lista[i] = Item
    retorne i
retorne -1</pre>
<p>Nesse novo exemplo, no melhor caso, o item procurado &#233; o primeiro da lista, sendo feita apenas uma compara&#231;&#227;o. No pior caso, n&#227;o &#233; encontrado, sendo feitas n compara&#231;&#245;es. O complexidade analisa o pior caso, ainda mantendo esse algoritmo como O(n). Perceba que se forem feitas diversas buscas em uma lista, o n&#250;mero m&#233;dio de compara&#231;&#245;es ser&#225; entre 1 e N, e cresce proporcionalmente a N, mantendo a caracter&#237;stica de complexidade.&#160; A m&#233;dia seria 3n/4 para uma lista n&#227;o ordenada e n/2 em uma lista ordenada, caso tenha curiosidade.</p>
<p>Um algoritmo para encontrar todos os itens duplicados em uma lista poderia ser escrito assim:</p>
<pre>de i := 1 at&#233; Lista.Count
  de j := 1 at&#233; Lista.Count
    se (i &lt;&gt; j) e (Lista[i] = Lista[j])
      Duplicados.Adiciona(i, j)</pre>
<p>Este seria um exemplo de O(n<i></i>&#178;). Para cada item, todos os outros itens s&#227;o percorridos. Otimiza&#231;&#245;es simples, como no exemplo anterior, podem ser limitar a itera&#231;&#227;o no loop interno de 1 at&#233; N-1, e no loop interno para a faixa i + 1 at&#233; N, que diminuem o n&#250;mero de compara&#231;&#245;es, mas n&#227;o alteram a complexidade. O n&#250;mero de itera&#231;&#245;es ainda &#233; proporcional ao quadrado do n&#250;mero de itens.</p>
<p>Algumas medidas Big-O comuns s&#227;o as seguintes, em ordem crescente de complexidade:</p>
<ul>
<li>O(1), ou <em>constante</em> &#8211; O tempo do algoritmo independe do n&#250;mero de itens. Claramente a melhor op&#231;&#227;o quando N pode crescer muito. &#201; a complexidade em inser&#231;&#227;o e busca em <a href="http://pt.wikipedia.org/wiki/Tabela_hash">Tabelas Hash</a> </li>
<li>O(log n), ou <em>logar&#237;tmico </em>- &#201; proporcional ao logaritmo de n, que cresce em uma taxa muito mais lenta que n. &#201; uma op&#231;&#227;o bem adequada quando n&#227;o &#233; poss&#237;vel utilizar O(1). A busca bin&#225;ria em uma lista ordenada tem essa complexidade. </li>
<li>O(n), ou <em>linear </em>- &#201; o exemplo que j&#225; vimos de uma busca de for&#231;a bruta em uma lista qualquer. Come&#231;a a ser um problema quando seu c&#243;digo tem que tratar um n&#250;mero indeterminado de itens. </li>
<li>O(n&#178;), ou <em>quadr&#225;tica</em> &#8211; O n&#250;mero de opera&#231;&#245;es &#233; proporcional ao quadrado do n&#250;mero de itens. O algoritmo intuitivo de ordena&#231;&#227;o, a <a href="http://pt.wikipedia.org/wiki/Insertion_sort">ordena&#231;&#227;o por inser&#231;&#227;o</a>, tem essa complexidade. Deve ser evitada praticamente para qualquer n&#250;mero de itens que n&#227;o estejam sobre estrito controle. </li>
<li>O(n!), ou <em>fatorial</em> &#8211; Proporcional ao fatorial de n, ou seja, cresce extremamente r&#225;pido. Mesmo que as opera&#231;&#245;es sejam r&#225;pidas, rapidamente com o crescimento de n o tempo tomado pelo algoritmo passar&#225; a ser excessivamente grande (horas, dias, ou mais). A for&#231;a bruta de uma senha tem essa essa complexidade, ou a solu&#231;&#227;o do problema do <a href="http://pt.wikipedia.org/wiki/Problema_do_caixeiro_viajante">caixeiro viajante</a>. </li>
</ul>
<h3>Conclus&#227;o</h3>
<p>A complexidade de algoritmos &#233; um ponto importante para ser considerado na escolha do design ou das estruturas usadas na codifica&#231;&#227;o, evitando introduzir problemas de escalabilidade. Para isso, &#233; importante saber estimar a complexidade de um c&#243;digo e reconhecer a complexidade de inser&#231;&#245;es e buscas em estruturas de dados comuns. Apesar disso, deve-se evitar a tenta&#231;&#227;o de intuitivamente deduzir quais ser&#227;o os gargalos no momento da codifica&#231;&#227;o e tomar decis&#227;o baseadas nessa intui&#231;&#227;o, prezando a legibilidade acima de tudo. &#201; muito mais f&#225;cil localizar e corrigir gargalos depois de ter o c&#243;digo escrito do que localizar e corrigir problemas de estilo.</p>
<p><center>&copy; <a href="http://www.techtips.com.br">TechTips</a></center></p>]]></content:encoded>
			<wfw:commentRss>http://www.techtips.com.br/programacao/performance/otimizacao-de-codigo-parte-i-entendendo-a-complexidade-de-algoritmos/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>FastMM e Delphi 2006</title>
		<link>http://www.techtips.com.br/programacao/fastmm-e-delphi-2006/</link>
		<comments>http://www.techtips.com.br/programacao/fastmm-e-delphi-2006/#comments</comments>
		<pubDate>Thu, 22 Feb 2007 16:06:39 +0000</pubDate>
		<dc:creator>Alexandre José</dc:creator>
				<category><![CDATA[Componentes]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Gerenciamento de Memória]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[Linguagem Delphi]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[Práticas]]></category>

		<guid isPermaLink="false">http://www.techtips.com.br/programacao/fastmm-e-delphi-2006/</guid>
		<description><![CDATA[Continuando um tema anteriormente publicado, neste artigo vamos começar a detectar os vazamentos de memória (memory leaks) existentes em nossos aplicativos. (isso se existirem, é claro&#8230;) A partir do&#160;Delphi 2006 o gerenciador de memória padrão que acompanha o Delphi passa a ser o FastMM. Quem não trabalha com Delphi 2006 pode baixar o FastMM aqui. [...]]]></description>
			<content:encoded><![CDATA[<p>Continuando um tema anteriormente publicado, neste artigo vamos começar a detectar os vazamentos de memória (memory leaks) existentes em nossos aplicativos. (isso se existirem, é claro&#8230;)</p>
<p>A partir do&nbsp;Delphi 2006 o gerenciador de memória padrão que acompanha o Delphi passa a ser o FastMM. Quem não trabalha com Delphi 2006 pode baixar o FastMM <a href="http://sourceforge.net/project/showfiles.php?group_id=130631">aqui</a>.</p>
<p>Se você estiver começando agora a desenvolver com Delphi deve estar se perguntando: Tá, e dai? O que é memory leak?</p>
<p><strong>Memory leak é um objeto criado pela aplicação que não foi corretamente destruído.</strong></p>
<p>Você pode simular esta situação criando qualquer objeto no OnCreate do formulário principal da sua aplicação. Exemplo:</p>
<p>
<pre>procedure TForm1.FormCreate(Sender: TObject);
var
  lstVazamento : TStrings;
begin
  lstVazamento := TStringList.Create;
  try
    lstVazamento.Add('Linha 1');
    lstVazamento.Add('Linha 2');
    lstVazamento.Add('Linha 3');
  finally

  end;
end;</pre>
</p>
<p>Execute o aplicativo e feche-o. Você notará que não acontece nada! <img src='http://www.techtips.com.br/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Esse teste ridículo foi feito só para você ver como é comum ocorrerem problemas que resultem em&nbsp;vazamentos de memória (memory leaks)&nbsp;como a falta de atenção (nesse caso),&nbsp;a pressa, Control+C / Control+V entre alguns outros.</p>
<p>Agora insira esta linha logo após o begin da sua procedure:</p>
<p>ReportMemoryLeaksOnShutdown := True;</p>
<p>Deixando o código assim:</p>
<p>
<pre>procedure TForm1.FormCreate(Sender: TObject);
var
  lstVazamento : TStrings;
begin
  ReportMemoryLeaksOnShutdown := True;

  lstVazamento := TStringList.Create;
  try
    lstVazamento.Add('Linha 1');
    lstVazamento.Add('Linha 2');
    lstVazamento.Add('Linha 3');
  finally

  end;
end;</pre>
</p>
<p>Repita a operação e veja o que acontece após fechar o aplicativo:</p>
<p><img src="http://www.waveobjects.com.br/blog/img/FastMMeDelphi2006_60DB/ML0127.png"> </p>
<p>Veja que recebemos a notificação do TStringList que criamos e das 3 linhas que adicionamos (String x 3) a ele. A&nbsp;partir dai podemos começar a evitar esse tipo de erro na hora de desenvolvermos aplicativos.</p>
<p>Uma dica importante é você utilizar o&nbsp;bloco <strong>try&#8230;finally&#8230;end;</strong> sempre que criar um objeto dinamicamente. Isto evita que ocorra um erro no meio da execução e ele não seja destruído devidamente. Veja os dois exemplos:</p>
<p><u>Método não apropriado:</u></p>
<p>
<pre>procedure TForm1.FormCreate(Sender: TObject);
var
  lstVazamento : TStrings;
begin
  ReportMemoryLeaksOnShutdown := True;

  lstVazamento := TStringList.Create;
  lstVazamento.Add('Linha 1');
  lstVazamento.Add('Linha 2');
  lstVazamento.Add('Linha 3');
  lstVazamento.Free;
end;</pre>
</p>
<p><u>Método apropridado de utilização:</u></p>
<p>
<pre>procedure TForm1.FormCreate(Sender: TObject);
var
  lstVazamento : TStrings;
begin
  ReportMemoryLeaksOnShutdown := True;

  lstVazamento := TStringList.Create;
  try
    lstVazamento.Add('Linha 1');
    lstVazamento.Add('Linha 2');
    lstVazamento.Add('Linha 3');
  finally
    lstVazamento.Free;
  end;
end;</pre>
</p>
<p>Utilizando o bloco <strong>try&#8230;finally&#8230;end;</strong> mesmo que ocorra algum erro na execução do procedimento o objeto é destruído. Pelo método não apropriado lstVazamento não seria destruído caso ocorresse algum problema na execução da linha lstVazamento.Add(&#8216;Linha 1&#8242;);, por exemplo.</p>
<p>Em tempo: utilizando ReportMemoryLeaksOnShutdown := True; a mensagem ocorrerá sempre que o aplicativo for terminado, inclusive fora do Delphi. Para evitarmos esse situação, substitua o True por DebugHook &lt;&gt; 0; deixando a linha assim:</p>
<p>ReportMemoryLeaksOnShutdown := DebugHook &lt;&gt; 0; </p>
<p>Para quem utiliza outras versões do Delphi isto deve ser configurado no arquivo FastMM4Options.inc.</p>
<p>No&nbsp;próximo artigo abordaremos mais funções do FastMM&nbsp;e configurações do arquivo FastMM4Options.inc para quem utiliza FastMM em versões mais antigas do Delphi. Até mais!</p>
<p><center>&copy; <a href="http://www.waveobjects.com.br/blog">TechTips</a></center></p>]]></content:encoded>
			<wfw:commentRss>http://www.techtips.com.br/programacao/fastmm-e-delphi-2006/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Quanto de mem&#243;ria estou utilizando?</title>
		<link>http://www.techtips.com.br/programacao/entendendo-o-gerenciamento-de-memria/</link>
		<comments>http://www.techtips.com.br/programacao/entendendo-o-gerenciamento-de-memria/#comments</comments>
		<pubDate>Fri, 26 Jan 2007 12:49:41 +0000</pubDate>
		<dc:creator>Alexandre José</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Gerenciamento de Memória]]></category>
		<category><![CDATA[Linguagem Delphi]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Programação]]></category>

		<guid isPermaLink="false">http://www.techtips.com.br/programacao/entendendo-o-gerenciamento-de-memria/</guid>
		<description><![CDATA[É comum muitos programadores, inclusive eu, há&#160;algum tempo&#160;atrás,&#160;medirem o consumo de memória de uma aplicação pelo gerenciador de tarefas do windows, o que é, na melhor das hipóteses, uma maneira incompleta de realizar essa verificação. O gerenciador de tarefas na verdade mostra a quantidade de memória que ele alocou naquele momento e não o quanto [...]]]></description>
			<content:encoded><![CDATA[<p>É comum muitos programadores, inclusive eu, há&nbsp;algum tempo&nbsp;atrás,&nbsp;medirem o consumo de memória de uma aplicação pelo gerenciador de tarefas do windows, o que é, na melhor das hipóteses, uma maneira incompleta de realizar essa verificação. O gerenciador de tarefas na verdade mostra a quantidade de memória que ele alocou naquele momento e não o quanto de memória o programa utiliza, além do que, o Windows contabiliza a memória alocada que foi utilizada e liberada até que outro programa a utilize.</p>
<p>Exemplo: seu programa utilizou 4.569K e liberou 169K, esse valor é contabilizado pelo windows até que outro programa o utilize (o gerenciador continua mostrando os 4.569K).</p>
<p>A causa desse problema com o gerenciador de tarefas é&nbsp;o gerenciador de memória&nbsp;runtime do Delphi alocar memória indiretamente. Quando o programa solicita memória para o gerenciador, ele aloca blocos de memória para o programa. Os blocos tem tamanho fixo de modo que para cada pedido de memória o gerenciador aloca a quantidade necessária de blocos contínuos para satisfazer o pedido do programa. </p>
<p>Deste ponto de vista o bloco é a menor quantidade de bytes que o gerenciador aloca.</p>
<p>Exemplo:&nbsp;Um bloco tem 16 bytes; se o programa pede 4 bytes, o gerenciador irá alocar um bloco; se o programa pedir&nbsp;18 bytes, o gerenciador irá alocar dois blocos.</p>
<p>Parece que o gerenciador está &#8220;torrando&#8221; memória a toa, mas isso é feito para evitar a fragmentação da memória. Quando o programa devolve memória ao gerenciador, este não a devolve para o Windows, pois ele deduz que o programa pode solicitar mais memória a qualquer momento e a reserva para otimizar o tempo de acesso a ela.</p>
<p>Entendendo melhor o funcionamento:</p>
<p>Imagine que você possui duas classes (TAlocaMemoriaAtoa e TAlocaMemoriaAVontade). Quando executamos um programa qualquer, por exemplo, vamos imaginar que o gerenciador aloque 10 blocos de memória. Após isso criamos a classe TAlocaMemoriaAtoa que solicita ao gerenciador mais 10 blocos. O tamanho total de memória utilizada é 20 blocos. Ao destruírmos a classe TAlocaMemoriaAtoa o sistema libera 10 blocos, mas o gerenciador os mantém disponíveis para a aplicação. Se nesse momento criarmos a classe TAlocaMemoriaAVontade e esta solicita 5 blocos de memória, o gerenciador buscará 5 blocos dos 10 reservados anteriormente, quando a classe TAlocaMemoriaAtoa fora destruída.</p>
<p><strong>BINGO!</strong> Por isso que quando eu fazia debug da minha aplicação com o gerenciador de tarefas aberto os &#8220;Free&#8221; e &#8220;FreeAndNil&#8221; quase nunca funcionavam! <img src='http://www.techtips.com.br/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>No meu próximo post falarei sobre gerenciadores de memória alternativos como o FastMM,&nbsp;adotado como gerenciador de memória padrão na versão 2006 do Delphi,&nbsp;e faremos alguns testes comparativos. Até a próxima.</p>
<p><center>&copy; <a href="http://www.waveobjects.com.br/blog">TechTips</a></center></p>]]></content:encoded>
			<wfw:commentRss>http://www.techtips.com.br/programacao/entendendo-o-gerenciamento-de-memria/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.821 seconds -->
