Antes de mais nada, Benchmarking é o nome pomposo
dado às práticas adotadas na indústria (não importa qual) que
visam alcançar um desempenho/qualidade superior. Ou seja, Benchmarking é todo tipo de prática cujo objetivo é melhorar/maximizar/amplificar os resultados.
No artigo de hoje vamos abordar benchmarking focado em código,
desempenho especificamente. Não é meu objetivo neste artigo aprofundar
sobre profiling etc. Existem muitas práticas de benchmarking que podem
ser utilizadas para poder alcançar algum tipo de amplificação na sua
área, porém não daria para falarmos detalhadamente de todas em um
único artigo.
Quando desenvolvemos aplicações para Internet e o stress da
aplicação será muito grande (como um todo), temos que ter cuidado,
muito cuidado. Conhecer mais de uma forma de se implementar uma única
solução pode ser o que vai salvar o seu pescoço.
Benchmarking de código – na prática
Muitas vezes, quando estamos meditando para solucionar um problema,
precisamos saber se aquela implementação é rápida. Mas, como assim,
rápida? Como eu vou saber se uma determinada implementação de
código/função é rápida o suficiente de forma que supra as necessidades
sem comprometer a qualidade de modo geral do sistema?
Vale lembrar que o método que vou mostrar aqui faz com que os
resultados variem de máquina para máquina, pois é tudo uma
questão de processamento. Os resultados aqui apresentados são apenas para
ILUSTRAR e SIMULAR para atingirmos resultados significantes.
Este é o seu momento “MythBusters”.
Descobrir qual é a forma mais rápida de se resolver um problema é
uma tarefa simples (nem sempre), em determinados casos, não temos um
parâmetro para saber se devemos seguir pelo caminho A ou B, tudo o que
nos resta é saber várias formas de se codificar/implementar uma
determinada solução e medir o desempenho de todas elas, cada trecho,
para assim, resolver o problema.
Vou dar um exemplo de como se medir o desempenho de uma
implementação de código, levando em conta um problema que tivemos aqui
no trabalho.
Tivemos que desenvolver um framework adhoc (free style) onde nós conseguíssemos obter resultados semelhantes a alguns frameworks bem comuns na Internet como Code Igniter, Cake PHP etc. MVC, Inflector, ActiveRecord, Rotas de Urls etc.
Agora vem a pergunta na cabeça de vocês:
– Por que re-inventar a roda?
A empresa que pediu para que a gente re-inventasse a roda,
alegou que estes são frameworks não “oficiais”, e são feitos/mantidos por
pessoas em que eles não confiam (untrusted or non certified application).
Resumindo? Eles queriam ver como a “coisa” era implementada, para
poder, assim, atingir o melhor desempenho e retirando tudo o que eles
não precisam da aplicação, tornando-a mais leve.
A grande questão?
O aquivo ActiveRecord.class.php está demorando muito para
terminar o seu serviço, quando você instanciava uma nova classe, a dita
cuja tinha um modelo, que, por sua vez, também possuía um controle onde possuía todas as regras do negócio.
Sempre que executássemos uma método da classe por ex ->find,
findFirst ou findAll ele retornava um array de objetos, ou seja, era um
array e todas as suas posições eram compostas por objetos e suas
respectivas propriedades (atributos).
Exemplo
Array<br />(<br /> [0] => Usuarios Object<br /> (<br /> [nome] => Igor Escobar 1<br /> [email] => blog@igorescobar.com<br /> )<br /><br /> [1] => Usuarios Object<br /> (<br /> [nome] => Igor Escobar 2<br /> [email] => blog@igorescobar.com<br /> )<br /><br /> [2] => Usuarios Object<br /> (<br /> [nome] => Igor Escobar 3<br /> [email] => blog@igorescobar.com<br /> )<br /><br />)Era mais ou menos assim que ele retornava os usuários de uma
determinada tabela. Este é um array de objetos com apenas 3 elementos,
mas para você ter um resultado para que possamos simular um resultado
no benchmarking mais expressivo, você precisa de um array de objetos
com mais elementos, pois iria exigir mais do processador, etc. No nosso
experimento vou aumentar este array de objetos para 10.000 elementos e
veremos quanto tempo ele demora para fazer o trabalho.
Medindo o desempenho (do trecho)
Criei uma class de exemplo, apenas para conseguirmos a estrutura vista acima:
<?php<br />public class Usuarios {<br /> var $nome;<br /> var $email;<br />}<br />?>Agora, eu crio a implementação para atingir o resultado esperado:
$arraUsuarios = array();<br />for($i = 1 ; $i <= 10000; $i++):<br /> $obUsuarios = new Usuarios();<br /> $obUsuarios->nome = "Igor Escobar {$i}";<br /> $obUsuarios->email = "blog@igorescobar.com";<br /> $arraUsuarios[] = $obUsuarios;<br />endfor;Para medir o desempenho exatamente no trecho que queremos, eu utilizo a função microtime() do php.
$time_start = microtime(true);<br />$arraUsuarios = array();<br />for($i = 1 ; $i <= 10000; $i++):<br /> $obUsuarios = new Usuarios();<br /> $obUsuarios->nome = "Igor Escobar {$i}";<br /> $obUsuarios->email = "blog@igorescobar.com";<br /> $arraUsuarios[] = $obUsuarios;<br />endfor;<br />$time_end = microtime(true);<br />$Benchmarking1 = ($time_end - $time_start);<br />echo "Array de Objetos levou: " . $Benchmarking1 . " microsecondos<br />\n";<br />//Output: Array de Objetos levou: 0.04233980178833 microsecondosAgora vamos criar uma outra implantação, ao invés de retornarmos uma
array de objetos, vamos retornar um array de arrays, ou seja, seria uma
array com n posições e todas as suas propriedades seriam INDICES do
array e não mais atributos do objeto.
Exemplo
Array<br />(<br /> [0] => Array<br /> (<br /> [nome] => Igor Escobar 1<br /> [email] => blog@igorescobar.com<br /> )<br /><br /> [1] => Array<br /> (<br /> [nome] => Igor Escobar 2<br /> [email] => blog@igorescobar.com<br /> )<br /><br /> [2] => Array<br /> (<br /> [nome] => Igor Escobar 3<br /> [email] => blog@igorescobar.com<br /> )<br /><br />)O mesmo script, apenas montando de maneira diferente (array de arrays), ficou assim:
$time_start = microtime(true);<br />$arraUsuarios = array();<br />for($i = 1 ; $i <= 10000; $i++):<br /> $arraUsuarios[] = array (<br /> \\'nome\\' => "Igor Escobar {$i}",<br /> \\'email\\' => \\'blog@igorescobar.com\\'<br /> );<br />endfor;<br />$time_end = microtime(true);<br />$Benchmarking2 = ($time_end - $time_start);<br />echo "Array de Arrays levou: " . ($Benchmarking2) . " microsecondos<br />\n";<br />//Output: Array de Arrays levou: 0.036391973495483 microsecondosQual método é mais rápido?
// Se resultado for negativo: Método 1 é mais rápido<br />// Se resultado for positivo: Método 2 é mais rápido<br />echo "Resultado: " . ($Benchmarking1 - $Benchmarking2);<br />//Output: Resultado: 0.007580041885376Viram? Duas formas de fazer a mesma coisa e a segunda forma é 0.007580041885376 microsegundos mais rápida.
Conclusão
Neste simples teste, podemos tirar a seguinte conclusão: neste caso,
utilizar a minha estrutura de retorno como um array de objetos é mais
lento do que trabalhar com um array de arrays. Este é um teste fora da
realidade, quanto mais próximo da realidade a complexidade do
dia-a-dia, este número aumenta, tende a ser cada vez maior.
Veja que este exemplo não envolve conexão com banco de dados,
acrescente todo o stress que envolve validação de regras de negócio,
segurança, consistência e etc., e veja este número crescer MUITO mais.
Vale lembrar que eu não estou dizendo para vocês não, nunca mais,
utilizarem objetos, porque não é isso, estou apenas ilustrando que
NESTE CASO, foi mais rápido, e mais interessante para o projeto que o
retorno desta função fosse um array de arrays, é tudo uma questão de
escolher entre mysql_fetch_assoc, mysql_fetch_array, mysql_fetch_object
ou mysql_fetch_row, cada uma tem suas características e são válidas
dependendo da sua necessidade em questão.
Você pode aplicar isso em “tudo” na sua aplicação, até para saber
quanto tempo uma determinada função demora para terminar seu trabalho
basta utilizar o microtime() no começo e no final da função e subtrair
os valores do maior para o menor.
$time_start = microtime(true);<br />//nome da função<br />$time_end = microtime(true);E é isso, pessoal, espero que tenham gostado. Certamente, se vocês
utilizarem este recurso para o seu crescimento profissional, será de
muita valia para vocês.
Fonte Completo
<?php<br />class Usuarios {<br /> var $nome;<br /> var $email;<br />}<br /><br />$time_start = microtime(true);<br />$arraUsuarios = array();<br />for($i = 1 ; $i <= 10000; $i++):<br /> $obUsuarios = new Usuarios();<br /> $obUsuarios->nome = "Igor Escobar {$i}";<br /> $obUsuarios->email = "blog@igorescobar.com";<br /> $arraUsuarios[] = $obUsuarios;<br />endfor;<br />$time_end = microtime(true);<br />$Benchmarking1 = ($time_end - $time_start);<br />echo "Array de Objetos levou: " . $Benchmarking1 . " microsecondos<br />\n";<br /><br />$time_start = microtime(true);<br />$arraUsuarios = array();<br />for($i = 1 ; $i <= 10000; $i++):<br /> $arraUsuarios[] = array (<br /> \\'nome\\' => "Igor Escobar {$i}",<br /> \\'email\\' => \\'blog@igorescobar.com\\'<br /> );<br />endfor;<br />$time_end = microtime(true);<br />$Benchmarking2 = ($time_end - $time_start);<br />echo "Array de Arrays levou: " . ($Benchmarking2) . " microsecondos<br />\n";<br /><br />// Se resultado for negativo: Método 1 é mais rápido<br />// Se resultado for positivo: Método 2 é mais rápido<br />echo "Resultado: " . ($Benchmarking1 - $Benchmarking2);<br /><br />?>[]\\’s
Igor.
UPDADE: Este artigo foi reformulado devido a
muitas dúvidas e questionamentos referentes a este meu método. Obrigado
a todos que ajudaram no amadurecimento do conteúdo.







