quarta-feira, 6 de março de 2013

Eficiência Matemática

Vou mostrar-lhes dois trechos de código que fazem exatamente a mesma coisa:






Analisem os trechos de código e tentem descobrir o que eles fazem. Uma dica: a essência de tudo está na função calc.


Já analisou? Entendeu o que está acontecendo? Bom, estes trechos de código calculam 1 milhão de vezes a soma do intervalo de números entre 1 e um número aleatório entre 1 e 500. Ao mesmo tempo ele toma nota de quanto tempo este processo leva usando a função microtime.


Você conseguiu descobrir o que estava acontecendo antes de eu ter explicado? Se você não conseguiu, não tem problema, eu vou explicar. Comparando os dois trechos de código, qual você diria que apresenta com mais clareza o comportamento que expliquei anteriormente?


O primeiro trecho usa funções cujos nomes dão uma certa ideia sobre o que está acontecendo. "array_sum" retorna a soma dos valores dentro de um array. "range" retorna um array preenchido com números em um intervalo que vai do primeiro parâmetro passado até o segundo parâmetro passado. Então array_sum soma o conteúdo de um array, range retorna um array de números, bingo! Tudo o que o loop faz é somar os números naturais de 1 até um intervalo superior que varia de 1 até 500. Um milhão de vezes!


E o segundo trecho? Não há chamadas de funções nesta versão da função calc. Ela mais parece uma fórmula matemática. E realmente é, é a seguinte fórmula:
E ela simplesmente retorna a soma entre os números naturais de 1 até n.

Certo! O que eu quero demonstrar com tudo isso? Primeiro eu convido vocês a executar os trechos de código em seus computadores e comparar o tempo gasto nos cálculos tanto pelo trecho usando array_sum quanto pelo outro usando a fórmula matemática, no meu computador um core i7 2600 3,40 GHz os tempos em segundos, cada trecho foi executado três vezes, foram os seguintes:

array_sum:
  • 42,979010105133
  • 42,527684926987
  • 43,008407831192
fórmula matemática:
  • 9,8508331775665
  • 9,889456987381
  • 9,9301979541779
Não é um benchmark rigoroso ou bem feito, mas como vocês podem ver a diferença de tempo entre as duas abordagens rodando no meu computador é enorme. E acredito que aqueles que testarem o código também observarão grandes diferenças de tempo de execução dos dois trechos.

Eu acredito que a grande diferença de eficiência de execução se deve principalmente a dois pontos principais. A função range cria um array dinamicamente e isso envolve alocação de memória por parte do interpretador PHP, o que toma tempo. Mas é o segundo ponto que deve realmente impactar na performance, a função array_sum deve percorrer cada elemento do array gerado pela função range e laços de repetição afetam a performance significativamente.

Eu não tenho dúvidas de que o código usando array_sum é mais fácil de se ler, mais elegante e usa todo o poder do dinamismo da linguagem PHP. Mas a eficiência da abordagem matemática é inegável, e alguns até poderiam dizer que ela é tão elegante quanto a outra, sempre podemos adicionar um comentário informando o que a fórmula faz, não é? Acho que preciso voltar a estudar matemática.