Introdução
Estou a 30 dias estudando Elixir, eu não tenho intenção nem de denegrir a imagem do Elixir que estou apaixonado e nem de enaltecer o PHP e minhas considerações sobre Elixir podem ser equivocadas aja visto que há muito ainda para eu aprender.
Em uma das aulas propostas pelo curso tinha a leitura de um arquivo de 300 mil linhas para gerar um relatório. Depois de gerado o relatório uma melhoria no relatório dividindo o arquivo em 3 e processando fazendo uso do concorrência.
Os resultados foi surpreendente ao fazer uso de concorrência, processando 3 arquivos ao mesmo tempo, foi aproximadamente 33% do tempo, mas aqui que começa ficar bom, eu pensei: E o PHP, quanto tempo ele levaria para fazer o mesmo processo?
Brincadeira começa aqui.
Bom para fazer comparação entre PHP e Elixir eu multipliquei as linhas fazendo o relatório ter 9 milhões de linhas.
E vou utilizar o time, para medir o início e o fim de cada resultado, não vou postar vários resultados, você poderá testar ao final cada um quantas vezes quiser e ver o resultado por você mesmo.
Versões utilizadas
Elixir 1.12-dev e 1.11.3
PHP 8.0 e 7.4
Elixir - Arquivo único
time -vvv mix run init-complete.exs
User time (seconds): 24.19
Percent of CPU this job got: 124%
Memória utilizada em todos os testes do Elixir variou de 28 a 33 megas. Usando :erlang.memory(:total) para saber a quantidade de memória utilizada.
PHP - Arquivo único teste 1 - com debug
O primeiro teste foi similar ao do Elixir com a diferença de utilizar 3,7 gigas de memória, só que eu tentei executar o mesmo no Mac que só tem 8 Gigas de memória e não executou, somente no meu PC e no meu Notebook que tem 64GB de Memória ram, porém em outro teste com arquivo menor o de 300 linhas no Mac o tempo ganhava do Elixir. Eu fiquei intrigado, descobri o motivo: no Mac o Debug não estava ativado.
Por isso não coloco o resultado do primeiro teste do php, acho irrelevante, mas acho relevante mencionar o debug ligado que pode alterar muito o resultado.
PHP - Arquivo único teste 2 - sem debug
time -vvv php reports/report.php
User time (seconds): 8.51
Percent of CPU this job got: 99%
Memória Utilizada: 3,7 Gigas
Bom PHP ganha no tempo, no uso da CPU, mas perdeu de lavada no uso da memória. Foi então que meu amigo Bruno me lembrou da existência do yield no PHP que eu já tinha lido, mas nunca usei, fui procurar como usar e fiz um novo teste.
PHP - Arquivo único teste 3 - sem debug e com yield
time -vvv php reports/yield.php
User time (seconds): 6.13
Percent of CPU this job got: 99%
O uso da CPU continuou o mesmo,porém o tempo caiu quase 3 segundos, além que o uso de memória ganhou de lavada do Elixir neste caso, pois usou apenas 2 megas de memória, isso mesmo, você leu corretamente, 2 megas. Até no Mac eu consegui executar este teste, que antes não era possível pela capacidade de memória.
Então eu dividi os arquivos em 3 e fui fazer o teste com concorrência.
Elixir concorrência - 10 arquivos
time -vvv mix run init-many10.exs
User time (seconds): 41.69
Percent of CPU this job got: 903%
Tempo aumentou muito, e o uso do processador, achei que coloquei muitos arquivos, então dividi em 3 apenas e depois com 2 arquivos vou colocar o resultado só com 2 arquivos que achei mais relevante e de menor tempo.
Elixir concorrência - 2 arquivos
time -vvv mix run init-many2.exs
(ops, usar o time para este teste pareceu injusto, visto que por algum motivo ele fica o dobro do tempo).
Então para este teste, executei via terminal:
iex -S mix
:timer.tc(fn -> ReportsGenerator.build_from_many(["outaa", "outab"]) end)
10 segundos. Memória sempre se manteve aproximadamente a mesma.
Uso de CPU caiu relacionado a processar 10 arquivos ficando em 300%, o tempo caiu pela metade, embora ainda assim o php sem concorrência ganhou no tempo.
PHP concorrência
Eu não fiz o teste com concorrência em PHP, pois é necessário usar ferramenta como Swoole, além do tempo para refatorar o código de moda a usar coroutine.
Conclusão
Os testes não são conclusivos, porém o PHP se mostrou com desempenho melhor mesmo sem usar concorrência, mas devemos considerar que eu executei o Elixir em modo de desenvolvimento, se executar em modo produção os valores podem serem alterados consideravelmente devido a bibliotecas que não serão carregadas em produção assim como aconteceu ao desativar o debug do PHP, mas ainda não coloquei nada em produção da forma correta para poder ser mais justo com Elixir, então fica a seu cargo testar.
P.S.: PHP não usou o recurso de JIT, até achei que ele estava fazendo uso na versão 8.0, porém executei o teste utilizando a versão 7.4 que não tem JIT e o tempo foi o mesmo ou com tempo insignificante de diferença.
Seria bacana ver os mesmos teste em Go e Rust, eu gostaria muito de ver os resultados, alguém se habilita?
Se você quiser fazer seus próprios testes ou até mesmo colocar o PHP em concorrência, segue os scripts que utilizei: https://github.com/emtudo/reports_generator
Top comments (3)
Será que tem algo a ver com o bug mencionado aqui: github.com/elixir-lang/elixir/issu... ?
Não me parece ser este problema, uma vez que é esperado como o José Valim diz sobre a função. Porém creio que o Elixir poderia entregar mais se tivesse em modo de produção.
It would be great to read it in English :(