Comparação de desempenho (2)

Este post não tem introdução nem desenvolvimento: já começa com as conclusões.

Link para os testes originais


P1: Tempo para analisar 261 PDFs em 1 único processo P2: Tempo para analisar 261 PDFs abrindo seqüencialmente 1 processo para cada P2÷P1
C++ 1,65s (64 bits) 3,53s (64 bits) 2,14
D 2,75s (32 bits) 5,71s (32 bits) 2,08
F# 3,35s
(64 bits / .NET 4.0)
71,54s
(64 bits / .NET 4.0)
21,36
Java 1,91s (64 bits) 40,93s (64 bits) 21,43

Boo 10,56s
(32 bits / .NET 3.5)
28,07s
(32 bits / .NET 3.5)
2,66
C++ debug 8,87s (64 bits) 11,20s (64 bits) 1,26
Lua 10,10s (64 bits) 12,38s (64 bits) 1,23
Python 9,28s (64 bits) 22,73s (64 bits) 2,45
Ruby 10,00s (32 bits) 23,49s (32 bits) 2,35

Resultados

Dividi os resultados em 2 categorias: a primeira, das linguagens que conseguiram tempos menores que 5 segundos; a segunda das outras. Como curiosidade, incluí uma versão de C++ na versão “debug” (sem otimizações), mostrando que o desempenho acaba ficando na segunda categoria. Então, se você sempre roda seus programas em modo debug, você não está usando todo o potencial da linguagem…

Destaquei em verde os dois melhores tempos em cada categoria. Na segunda coluna, destaquei também em vermelho piores tempos de cada categoria, porque as diferenças foram enormes e merecem atenção. Não dei o mesmo destaque na primeira coluna porque as diferenças não eram tão significativas.

Embora seja fácil analisar os resultados pelas cores, reenfatizo aqui pontos interessantes: como a inicialização do processo é absurdamente custosa em Java e F# (nas versões 64 bits), bastante custosa em Boo, Python e Ruby e pouco custosa em C++, D e Lua. Destaque para Lua, que é uma linguagem dinâmica (e muitas vezes executada a partir do código fonte) como Python e Ruby, mas tem uma inicialização rápida como C++ e D. Ruby 1.8 tinha a fama de ser muito lento (não testei). Já a versão 1.9 melhorou e compete diretamente com Python, embora não o alcance ainda.

Desta vez, não incluí o uso de memória, mas coloquei a proporção entre os tempos da primeira e da segunda colunas.

Melhorias no código fonte

O código fonte foi melhorado usando um profiler. A mudança principal está na função PdfXref::add_obj, que agora não chama read_number (removida), que era a função que mais degradava o desempenho. Quase todas as diferenças de desempenho entre os testes anteriores e estes se devem a esta melhora em add_obj. A única exceção é explicada a seguir.

Na implementação em D, houve uma correção que muda a classificação final. A variável pdfFile é agora passada e armazenada em PdfTokenReader por ponteiro, como deveria ser. Isso corrige o problema que fazia o scope(exit) ser necessário (embora haja em D um certo problema em ter structs com destrutores dentro de classes, no meu caso a culpa era minha mesmo). O resultado prático dessa mudança é que agora a implementação em D é consistentemente mais rápida que a implementação em F#.

Os códigos estão nas seguintes páginas. Mantive as implementações originais em C++, D, Java, Python e F# e adicionei as novas.

Edit 2014-04-12: Todos os fontes no BitBucket (o repositório é Git):
https://bitbucket.org/marcuscf/pdfpagecount/commits/tag/v2010-10-27

Código fonte em Java

Código fonte em C++

Código fonte em D

Código fonte em F#

Código fonte em Python

Código fonte em Lua

Código fonte em Boo

Código fonte em Ruby

32 ou 64 bits?

Para simplificar a tabela, não incluí versões 32 bits onde eu tinha disponível o compilador/interpretador de 64 bits. Mas alguns testes de 32 bits eu fiz, e pude concluir que as proporções continuam as mesmas para todas as linguagens: uma pequena mas consistente vantagem de desempenho na versão 64 bits, com a exceção da inicialização do Java e do F# onde há um grande impacto na performance em 64 bits. Portanto, os resultados do teste anterior ainda podem ser usados como referência.

Outras condições do teste

Outras variáveis não se alteraram. Usei a mesma máquina, exatamente os mesmos 261 arquivos de entrada e repeti o teste algumas vezes para obter o menor tempo atingível. Repeti cada teste 3 vezes no mínimo e repeti algumas vezes mais enquanto estivesse conseguindo obter tempos menores (isso foi diferente no teste anterior, onde repeti sempre 3 vezes cada execução).

Implementações usadas

Foram usadas as mesmas implementações de compiladores e interpretadores dos testes anteriores.

Nas linguagens a seguir, que rodam a partir dos fontes, não foi usada pré-compilação mesmo quando ela estava disponível (.pyc, luac, etc.):

Python 3.1.2, distribuição oficial para Windows, 64 bits

Ruby 1.9.2-p0, distribuição RubyInstaller para Windows, 32 bits.

Lua 5.1.4, inicialmente usei a distribuição LuaForWindows v5.1.4-40 (32 bits), mas como era bem fácil de fazer, substituí os executáveis e DLLs por versões de 64 bits a partir do projeto LuaBinaries.

A última linguagem adicionada foi Boo 0.9.3, usando o SharpDevelop 3.2.0 (build 5777). Ela não roda a partir do fonte, como Python, mas sim compila para o .NET Framework. Infelizmente, ainda não é compatível com o .NET 4.0, então não dá pra fazer uma comparação direta com F#. O desempenho ficou comparável ao das outras linguagens dinâmicas, apesar de Boo usar tipos estáticos.

Fim

Pronto, terminei de fazer todos os testes que eu queria fazer, finalmente. Foi interessante perceber a diferença na qualidade de documentação, ferramentas e comunidade das linguagens consolidadas (C++, Java, Python, Ruby, Lua) e das em desenvolvimento (D2 e Boo, deixando F# numa categoria intermediária). Por exemplo, uma linguagem consolidada que não muda muito dá a impressão de ser “estável”: ela não sofre atualizações tão freqüentes mas todo mundo usa e fala sobre ela; enquanto que uma linguagem aspirante sem um anúncio de nova versão no grupo de discussão oficial parece “abandonada”. Uma linguagem consolidada que passa por atualizações, parece “ter futuro”, ser “moderna”. Já no caso de uma aspirante, muitas novas funcionalidades podem por descuido não ter sua documentação atualizada (ou o contrário: existe documentação sobre um recurso que não está completo) e isso faz a linguagem parecer “instável”, “problemática”.

Antes de fazer o teste eu não gostava muito de Ruby, por parecer um “Python mais complicado”, como a confusão na definição de procs e lambdas e na precedência de operadores (ver este exemplo:

irb(main):058:0> true or false and false
=> false
irb(main):059:0> true || false && false
=> true

) mas usando deu pra perceber que é uma linguagem muito prática, que procura tornar fáceis as tarefas comuns (exemplos: attr_reader, attr_accessor, case/when, regexps, interpolação de strings, uso do símbolo @, ranges, etc.), ainda que as regras da linguagem sejam um pouco mais complicadas do que o necessário. Ah, e espero que a página da linguagem em português se atualize, pois está bem atrás da versão em inglês (compare: http://www.ruby-lang.org/pt/ e http://www.ruby-lang.org/en/)

Anúncios

Um pensamento sobre “Comparação de desempenho (2)

  1. Outra coisa interessante, que eu já tinha percebido quando escrevi este post originalmente, mas por algum acaso inexplicável acabei preferindo não colocar no post é o seguinte:

    Como são diferentes as precedências das coisas em Ruby e F#!!!

    F# segue a regra de que operadores são executados DEPOIS das chamadas de função.

    Se temos uma função que multiplica por dois, chamada vezesDois:

    let vezesDois x = x*2;;

    o resultado de
    vezesDois 7 + 1
    é 15 (7×2 é 14, e então soma 1)

    Já em Ruby…

    def vezesDois x
    x*2
    end

    O resultado de
    vezesDois 7 + 1
    é 16!
    (7 + 1 é 8, e então chama vezesDois para multiplicar por dois e resultar em 16)

    Os dois fazem sentido, cada um do seu jeito, o problema é cuidar para não confundir!

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s