Comparação de performance
Com a ajuda de alguns amigos, fizemos uma comparação de performance bem simples e muito distante de ser um benchmark confiável entre algumas linguagens/versões usando o fibonacci de 35. Também foi realizado o fibonacci de 45 nas linguagens com melhor performance. Foi somente uma experiência e o resultado em minha máquina foi esse:
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) – fib.c – fib2
Compilação otimizada usando: gcc -O3 -o fib2 fib.c (contribuição de Filipe Saraiva)
./fib2 35
Tempo: 0.124780
Resultado: 9227465
./fib2 45
Tempo: 15.655936
Resultado: 1134903170
Scala 2.7.7final – Fib1.scala (contribuição do Matheus Moreira)
scala Fib1 35
Tempo: 0.137780472
Resultado: 9227465
scala Fib1 45
Tempo: 19.697318756
Resultado: 1134903170
Java(TM) SE Runtime Environment (build 1.6.0_24-b07) – fib.java – fib.class
java fib 35
Tempo: 0.146963799
Resultado: 9227465
java fib 45
Tempo: 20.793879543
Resultado: 1134903170
Free Pascal Compiler version 2.4.0-2 [2010/03/06] for x86_64 – fib.pas – fibp (contribuição do Nécio Veras)
Compilação otimizada usando: fpc -O3 fib.pas -ofibp
./fibp 35
Tempo: 0.221000001
Resultado: 9227465
./fibp 45
Tempo: 30.445999150
Resultado: 1134903170
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) – fib.c – fib
Compilação não otimizada usando: gcc -o fib fib.c
./fib 35
Tempo: 0.276678
Resultado: 9227465
./fib 45
Tempo: 32.527250
Resultado: 1134903170
Groovy Version: 1.8.0 JVM: 1.6.0_24 – fib2.groovy (contribuição do Wanderson Santos)
groovy fib2.groovy 35
Tempo: 0.290426544
Resultado: 9227465
groovy fib2.groovy 45
Tempo: 33.457977715
Resultado: 1134903170
Haskell – The Glorious Glasgow Haskell Compilation System, version 6.12.1 – fib.hs – fibh (contribuição do Diego Victor com correção da Mônica Regina da Silva)
Compilado usando: ghc –make -O3 fib.hs -o fibh
./fibh 35
9227465
0.317424
./fibh 45
1134903170
40.989142
Ruby – Rubinious 1.2.1dev (1.8.7 f4c08fbe 2010-12-21 JI) [x86_64-unknown-linux-gnu] – fib.rb
ruby fib.rb 35
Tempo: 1.339709
Resultado: 9227465
ruby fib.rb 45
Tempo: 158.686395
Resultado: 1134903170
JRuby 1.5.3 (ruby 1.8.7 patchlevel 249) (2010-09-28 7ca06d7) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_20) [amd64-java] – fib.rb
jruby fib.rb 35
Tempo: 4.371
Resultado: 9227465
Groovy Version: 1.8.0 JVM: 1.6.0_24 – fib.groovy (contribuição do Luís Bruno)
groovy fib.groovy 35
Tempo: 5.106544505
Resultado: 9227465
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio – fib.lua (contribuição da Mônica Regina da Silva)
lua5.1 fib.lua 35
Tempo: 5.36
Resultado: 9227465
Ruby 1.9.1p378 (2010-01-10 revision 26273) [x86_64-linux] – fib.rb
ruby1.9.1 fib.rb 35
Tempo: 6.81764046
Resultado: 9227465
Ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-linux] – fib.rb
ruby fib.rb 35
Tempo: 7.65983433
Resultado: 9227465
Groovy Version: 1.7.5 JVM: 1.6.0_20 – fib.groovy (contribuição do Luís Bruno)
groovy fib.groovy 35
Tempo: 8.653575988
Resultado: 9227465
Python 2.6.5 – fib.py
python fib.py 35
Tempo: 0:00:11.448263
Resultado: 9227465
Python 3.1.2 – fib.py
python3.1 fib.py 35
Tempo: 0:00:12.620807
Resultado: 9227465
Ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux] – fib.rb
ruby fib.rb 35
Tempo: 34.993339
Resultado: 9227465
Agradecimentos especiais aos amigos que contribuíram com as seguintes implementações:
Groovy – Luís Bruno / Wanderson Santos / Thiago Witt
Haskell – Diego Victor (com correção da Mônica)
Lua – Mônica Regina da Silva
Pascal – Nécio Veras
Scala – Matheus Moreira / Thiago Witt
O Thiago Witt contribuiu com versões Groovy e Scala extramamente otimizadas usando Tail-call. Foi tão otimizado que resolvi testar o fibonacci de 10000:
Scala 2.7.7final – Fib2.scala (contribuição do Thiago Witt – Tail-call)
scala Fib2 10000
Tempo: 0.09665077
Groovy Version: 1.8.0 JVM: 1.6.0_24 – fib3.groovy (contribuição do Thiago Witt – Tail-call otimizada para evitar StackOverFlow)
groovy fib3.groovy 10000
Tempo: 15.135361192
O Thiago Witt também contribuiu com uma versão em Groovy extramamente otimizada, mas que dá StackOverflow para números grandes. Assim, testei as versões Tail Call com fibonacci de 4000:
Scala 2.7.7final – Fib2.scala (contribuição do Thiago Witt – Tail-call)
scala Fib2 4000
Tempo: 0.062994021
Groovy Version: 1.8.0 JVM: 1.6.0_24 – fib4.groovy (contribuição do Thiago Witt – Tail-call)
groovy fib4.groovy 4000
Tempo: 0.14749984
Groovy Version: 1.8.0 JVM: 1.6.0_24 – fib3.groovy (contribuição do Thiago Witt – Tail-call otimizada para evitar StackOverFlow)
groovy fib3.groovy 4000
Tempo: 2.789794005
Opa Regis! Como devoto das linguagens compiladas, do C e do gcc que sou, tive que comentar este post ao ver que o desempenho da minha linguagem preferida foi menor que em java. 😉
Acontece que a compilação padrão do gcc não faz muitas otimizações no código em C (e nas demais linguagens suportadas também). Isso possibilita uma compilação mais rápida e melhor suporte ao debug e verificação de erros, mas na hora da execução o desempenho fica inferior.
Então, baixei o código e fiz a compilação padrão (gcc -o fib fib.c) e a compilação com o máximo de otimização (gcc -O3 -o fib fib.c). Executei algumas vezes o código compilado e a diferença é bem perceptível:
[filipe@localhost Downloads]$ gcc -o fib fib.c
[filipe@localhost Downloads]$ ./fib 35
Tempo: 0.388445
Resultado: 9227465
[filipe@localhost Downloads]$ gcc -O3 -o fib fib.c
[filipe@localhost Downloads]$ ./fib 35
Tempo: 0.122428
Resultado: 9227465
E quanto à java…
[filipe@localhost Downloads]$ javac fib.java
[filipe@localhost Downloads]$ java fib 35
Tempo: 0.168618716
Resultado: 9227465
Uso o linux Mandriva 2010.1 x86_64 num Intel Core 2 Duo.
Então é isso. Bem interessante esse micro teste, serve para vermos rapidamente a diferença em termos de desempenho e como isso é fortemente dependente da linguagem escolhida.
Grande abraço, Regis!
Oi, Filipe!!! Muito obrigado pela sua contribuição. Realmente fiquei surpreso ao ver o C com desempenho inferior ao Java. Agora compreendi melhor e inclusive vou atualizar o post com os novos resultados.
Olha ae em Ruby 1.9.2 🙂
[23:42:05 caironoleto] ~/Downloads (ruby-1.9.2-p0)
$ ruby fib.rb 35
Tempo: 3.470373
Resultado: 9227465
Cairo, atualizei o post com a performance do Ruby1.9.2-p0 em minha máquina. No meu caso, a performance foi um pouco inferior à performance do Ruby 1.9.1.
Olá Regis, ótimo post!
Bem legal esse teste, brinquei um pouco aqui também:
A versão do Ruby na minha máquina é 1.8.7 e resultou em:
Tempo: 17.839598
Resultado: 9227465
Fiz em Groovy:
bruno@bruno-desktop:~/Documentos/groovy$ groovy Fibonacci.groovy 35
Tempo: 19935
Resultado: 9227465
bruno@bruno-desktop:~/Downloads$ java fib 35
Tempo: 0.129493434
Resultado: 9227465
Bem, realmente não dá pra comparar a performance do Groovy com Java, até porque o Groovy roda no Java…
Percebi que a mudança das versões do Ruby teve uma visível melhora na performance. O Groovy é criança ainda, espero que com o avançar das versões também tenha uma melhora satisfatória nesse quadro.
Abraços Regis!
Luís Bruno,
Envie-me sua implementação em Groovy para eu incluir no post. É mais uma contribuição interessante.
Incluí sua contribuição! Obrigado!
Grande Régis,
Parabéns pela postagem! Realmente, gostei do post!
Tanto que também resolvi brincar um pouco!
Bom, fui buscar inspiração no memorial de Clóvis Beviláquia (grande jurista brasileiro, nascido aqui em Viçosa). Quando vierem por aqui não deixem de visitar o seu memorial, vale a pena! Enfim, quando digo que fui buscar inspiração em um “museu”, quis dizer que resolvi resgatar uma linguagem que, cientificamente, não tem tanta influência, mas serviu como base para o aprendizado de lógica em muitas gerações e impulsiona até hoje o mercado comercial com uma ferramenta poderosa! Ganhou certa “simpatia” quando foi lançada uma versão sob a licença GPL. Estou falando do bom e velho freePascal!
Vamos aos resultados:
Free Pascal Compiler version 2.4.0 (linux-x86)
Tempo: 3.45000000
Resultado: 9227465
Nem foi tão ruim assim 🙂
Abraços,
Nécio de Lima Veras.
Obrigado, Nécio,
Envie-me sua implementação em Pascal para que eu possa adicionar ao post!
Nécio, incluí sua contribuição. Obrigado, inclusive pela informação de que o Clóvis Beviláquia era de Viçosa, pois eu não sabia.
Regis, quando puder atualize para Groovy 1.8. A melhoria no desempenho é impressionante!
Confira: http://www.wiki.jvmlangsummit.com/images/0/04/Theodorou-Faster-Groovy-1.8.pdf
Em alguns casos para calculos matemáticos, se mostrou mais rápido do que Scala (!) http://bit.ly/eSiTHf.
Wanderson,
Obrigado pelas informações referentes às melhorias do Groovy. Assim, baixei a versão 1.8.0 e atualizei os resultados no post.
Obrigado Regis!
Olá Regis!
Segue o calculo fibonacci otimizado para Groovy 1.8.
//Autor: Wanderson Santos (@wanswins / wanderson.santos (arroba) gmail.com)
int fib(int num) {
if (num < 2) return num
return fib(num – 1) + fib(num – 2)
}
def numero = Integer.parseInt(args[0])
def t1 = System.nanoTime()
resultado = fib(numero)
def t2 = System.nanoTime()
println "Tempo: ${(t2-t1)/1000000000}"
println "Resultado: ${resultado}"
Postei sua contribuição, Wanderson! Muito rápido mesmo!
Muito obrigado Regis!
É muito bom ver o Groovy que já tem uma excelente legibilidade e fácil adoção dos desenvolvedores Java, também se mostrar extremamente rápido. E olha que estamos falando de uma linguagem dinâmica!
Regis, beleza? Tem como eu contribuir com uma implementação em Scala?
[]’s
Claro, Matheus! Basta me enviar o código que eu executo aqui na minha máquina e atualizo o post com sua contribuição. Abraços, Regis.
Regis, segue uma implementação simples:
object Fib {
def fib(n: Int): Int =
if (n == 0 || n == 1) n
else fib(n – 1) + fib(n – 2)
def main(args: Array[String]) {
def n = args(0).toInt
def t1 = System.nanoTime()
def resultado = fib(n)
def t2 = System.nanoTime()
println(“Tempo: ” + ((t2 – t1) / 1000000000.0)
println(“Resultado: ” + resultado)
}
}
Eu compilei esse arquivo com o comando “fsc (ou scalac) -g:none -optimise” supondo que isso garantiria um desempenho melhor. Ainda não comparei o desempenho do código compilado dessa forma com aquele compilado sem as opções -g e -optimise. Pode ser um teste interessante.
Outra observação, testei com Scala 2.8.0.final mas a 2.9.0.final já está na área.
Matheus, finalmente atualizei o post com sua contribuição em Scala! Desculpe-me pela demora. A performance realmente é excelente! A otimização sugerida não fez diferença na performance final.
Regis, seguem várias implementações em várias linguagens!
http://www.scriptol.com/programming/fibonacci.php
Faltou o LuaJIT 🙂
$ luajit-2.0.0-beta7 fib.lua 35
Tempo: 0.30
Resultado: 9227465
O post me inspirou a fazer alguns testes com groovy e scala, com os quais eu nunca tinha mexido.
Implementei o fib usando tail-call recursion, quem quiser pode ver ver os fontes aqui:
Scala: http://pastebin.com/x7yn1cpU
Groovy: http://pastebin.com/EUNG2Xpv
A versão em scala aqui foi bem mais rápida que a em groovy.
As opções de otimização comentadas pelo @Mateus Moreira não fizeram nenhuma diferença aqui na versão scala.
Thiago, em nome de todos, muito obrigado pela contribuição!
Vi que você utilizou a versão que contribui. As outras versões Groovy conseguiram resultado melhor?
Contudo como diria o Professor Tibúrcio: “‘Bem mais rápida’ não é resposta”. 😉
Compartilhe conosco os valores que você teve! Até mesmo pra gente discutir o que o conceito de “rápido”.
Hoje na maioria dos casos de sistemas de informação este conceito é relativo a sensação de rapidez que usuário final tem ao executar uma tarefa. Neste contexto milésimos, décimos ou até poucos segundos não fazem diferença. Outros contextos são processamentos em lote (jobs) de grandes quantidades de dados, dentre outros onde isso realmente faria diferença. Mas a boa notícia é que paralelizando em vários cores (com GPars, Akka, etc.) isto pode também não fazer diferença.
Grande abraço!
Oi Wanderson,
Sim, usei a sua versão, fiz uns testes com a sua e a anterior pra descobrir porque a sua era tão mais rápida. Legal ver como pequenos detalhes podem fazer uma boa diferença na performance.
Quanto aos resultados, aqui na minha máquina (Macbook Pro, Core 2 Duo 2.4Ghz rodando Mac OS 10.6.7) foram os seguintes (editei os resultados pra diminuir a poluição visual):
Versão scala (2.8.1.final):
$ scala Fib 10000
Tempo: 0.02375
Resultado: 336447648764317832666(…)6073310059947366875
Versão groovy (1.8.0):
$ ./groovy fib3.groovy 10000
Tempo: 8.188678
Resultado: 33644764876431783266621(…)66073310059947366875
O problema no groovy deve ser a implementação do trampoline(). A versão sem usar o trampoline tem velocidade muito próxima a da em scala, mas daí não dá pra calcular pra n muito maior que 4000 (aqui na minha máquina) porque estoura a pilha.
$ scala Fib 4000
Tempo: 0.017827
Resultado: 3990947343500442279(…)3025993715274875
$ ./groovy ~/Downloads/fib3.groovy 4000
Tempo: 1.104713 <– com trampoline()
Resultado: 3990947343500442279(…)3025993715274875
Tempo: 0.036333 <– sem trampoline()
Resultado: 3990947343500442279(…)3025993715274875
Se conseguir deixar mais rápido me conta.
Abraço,
Thiago
Thiago, postei suas contribuições. Obrigado!
A versão (Groovy) sem usar o trampoline tem velocidade muito próxima a da em scala….
Era isso que queria saber. Tem como postar qual foi o valor?
… mas daí não dá pra calcular pra n muito maior que 4000 (aqui na minha máquina) porque estoura a pilha.
É o preço que se paga por ser uma linguagem dinâmica e ter MOP, AST e outras coisas fantásticas, rs. A memória é o gargalo, e acredito que dos males é o menor, ainda mais no mundo paralelizado. O que você acha?
A propósito ainda tem lista de otimizações planejadas com ajustes já conhecidos abaixo que devem melhorar ainda mais o desempenho do Groovy.
Groovy 1.8 runtime performance improvements
http://jira.codehaus.org/browse/GROOVY-3951
Até mais