Dev (Back & Front)ARTIGO

Benchmarking de desempenho no PHP

Atenção: este artigo sofreu uma alteração, a pedido do autor. O texto atualizado está publicado e pode ser acessado aqui.

Antes de mais nada, Benchmarking é o nome pomposo dado para as 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. 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 dá para falarmos de todas em um único post.

Quando desenvolvemos aplicações para Internet, onde o stress da aplicação será muito grande, 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 desempenho – 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?

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 implementar uma determinada solução e medir o desempenho de todas elas para, assim, resolver o problema.

Vou dar um exemplo de como se medir o desempenho de uma implementação, 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.

Agora vem a pergunta na cabeça de vocês:

– Por que reinventar a roda?

A empresa que pediu para que a gente reinventasse a roda alegou que estes são frameworks não “oficiais”, e são feitos 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 que 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.

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 de benchmarking mais expressivo, você precisa de um array de objetos com mais elementos. No nosso experimento vou aumentar este array para 10.000 elementos e veremos quanto tempo ele demora a fazer o trabalho.

Medindo o desempenho

Criei uma class de exemplo, apenas para conseguirmos a estrutura vista acima:

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, utilizamos 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 microsecondos

Agora vamos criar outra implantação, ao invés de retornarmos uma array de objetos, vamos retornar um array de arrays.

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 microsecondos

Qual método é mais rápido?

Viram? 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: Trabalhar com arrays é mais lento do que trabalhar com arrays. Este é um teste fora da realidade, quanto mais próximo da realidade a complexidade aumenta e o número tende a ser cada vez maior.

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.

Você pode aplicar isso em “tudo” na sua aplicação, até para saber quanto tempo uma determinada função demora a 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 grande valia para vocês.

Fonte Completo

<?php<br />class Usuarios {<br />	var $nome;<br />	var $email;<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 />$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 />// 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);

[]\\’s

Igor.

é formado em Desenvolvimento de Sistemas para Internet pela Faculdade Véris IBTA, Zend PHP 5 Certified Engineer, blogueiro e colunista iMasters, WebInsider e IBM DeveloperWorks.

Ver perfil