Performance no Java

Todos desejamos performance nas nossas aplicações. No Java e apesar de alguns dizerem que é mais rápido que C++ isto nem sempre é verdade, mas vou falar disso noutra altura. Hoje o que queria colocar aqui é uma ajuda para quem tem que trabalhar com Java. Em vez de correr a JVM client o melhor para performance é mesmo correr a JVM server. Para isso terá que utilizar o terminal e correr os programas da seguinte forma:

java -server -jar programa.jar

Ora para não ter que andar sempre a adicionar -server no terminal, o melhor é colocar o -server como default. Para isso basta editar o ficheiro jvm.cfg (que é um ficheiro de texto simples) e onde está:

-client KNOWN
-server KNOWN

é só trocar a ordem para:

-server KNOWN
-client KNOWN

Assim tudo o que for java vai correr na JVM server e só se chamarmos explicitamente a versão client é que será corrida aí.

Ganhos de performance? Sim bastante notáveis. Estive a experimentar calcular uma série de Fibonacci e posso dizer que o tempo de execução foi em média:

-client: 540ms
-server: 397ms

o que dá para o mesmo programa um ganho de 143ms ou cerca de 26%, nada mau. O downside? Tradicionalmente o JVM -server demora um pouco mais a arrancar e come mais memória, mas se depois se tornar mais rápido… para quê queixarmo-nos…

no Mac OS X Tiger o jvm.cfg está em
/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/jvm.cfg

Bem, por curiosidade o mesmo cálculo em C sem optimizações demorou 810ms e com optimizações -O3 cerca de 260ms. Mas como disse estas discussões são para outra altura.

8 Respostas

  1. Viva,

    Outra coisa fácil de configurar e que costuma resultar em ganhos na performance é o tamanho inicial e maxímo da heap, para isso basta-te configurar -Xms (initial heap size) e -Xmx (maximum heap size). Podemos assim evitar que o GC entre em acção mesmo quando existe memória que poderia ser usada pela JVM.

    Poderias também entrar em assuntos como o tunning do GC, mas isso é daquelas “artes mágicas” algo complexas (pelo menos eu acho, mas também não sei assim tanto sobre o assunto) que ou sabemos realmente o que estamos a fazer ou então em vez de melhorarmos pioramos a performance.

    Ah já agora, talvez já saibas mas fica aqui a dica, a JVM possui um profiler muito simples que podes usar quando estás a fazer estes testes, o JConsole. Para o usares basta começares a jvm com mais um switch: -Dcom.sun.management.jmxremote. Depois enquanto o programa java estiver a correr, basta lançares o jconsole (que está no $JAVA_HOME/bin).

  2. A server-vm tem uma melhor performance do que a client-vm, mas o consumo de memória (que pode ser muito maior) e o tempo extra de arranque das aplicações pode negar esse ganho. Para aplicações que vão correr durante muito tempo, praticamente sozinhas, numa máquina com memória em grande quantidade, a server-vm é claramente melhor.

    Para aplicações desktop, se calhar não. Ganhas na performance das aplicações Java, e perdes no resto do sistema. Para o utilizador é capaz de ficar tudo na mesma.

  3. O Carlos tem toda a razão.

    Que tal colocares o resultado do:
    time java -server aplicacao
    time java -client aplicacao

  4. Pois, eu digo isso, performance vem à custa de recursos. Não há volta a dar-lhe. Ou se tem ou não se tem. Agora se alguém se dá ao trabalho de editar um ficheiro de configuração de JVM provavelmente é porque utiliza aplicações Java mais do que o simples applet ocasional. A meu ver quem estiver todo o dia enfiado num eclipse ou netbeans ou … etc… se calhar já começa a compensar a mudança. No caso do Netbeans 6 RC posso dizer que a diferença é notória.

    Claro que é preciso ter recursos, mais uma vez, não se fazem omoletes sem ovos.

  5. @Gustavo,

    Os resultados que pedes estão lá escritos no post. Ou será que não leste bem?

    Desculpa, os valores lá não eram do time… mas cá vão..

    -Client

    real 0m0.667s
    user 0m0.610s
    sys 0m0.038s

    -Server

    real 0m0.569s
    user 0m0.506s
    sys 0m0.042s

    Estes valores foram corridos hoje, apenas uma vez com uma série de tralhada diferente… mas mesmo assim continua a ver-se melhorias da versão server.

  6. To David: Está visto que não me fiz perceber. Por isso vou tentar outra vez e até incluo código:
    public class Primos {

    public static void main(String[] args) {

    int n = 100000;
    boolean primo;
    int i,z;
    long inicio, fim;
    inicio = System.currentTimeMillis();
    for (i = 1; i <= n; i++) {
    primo = true;

    for(z = 2 ; z <= i/2 && primo; z++ ){

    if(i%z == 0)
    {
    primo=false;
    }
    }
    }
    fim = System.currentTimeMillis();
    System.out.println(“Demorou ” + (fim-inicio)/(float)1000 + ” segundos de tempo total.”);

    }
    }

    Este código vai indicar o tempo que a JVM esteve efectivamente a trabalhar. Comparar os outputs com -client e -server vai mostrar se existe um aumento de performance.
    Ao usar o “time java -client Primos” e “time java -server Primos” vai nos mostrar o overhead no arranque da JVM. Os tempos que tu indicas devem ser apenas os reportados pela aplicação.

  7. @Gustavo,

    Os tempos do post são os dados pelo programa tal e qual como no teu exemplo.

    Os tempos do meu comentário são os da utilização do time java…

  8. O problema destes “benchmarks” sintéticos é que o processamento é demasiado focado para se conseguir tirar conclusões válidas. Os testes correm durante pouquíssimo tempo, não geram grandes alocações de memória, e resumem-se a umas quantas linhas de código que, com um pouco de sorte, uma vez JITadas cabem dentro da cache do CPU.

    Porque como eu dizia, se tens um Eclipse a consumir quantidades obscenas de memória, e tens um Firefox ao lado com algumas tabs e mais uma coisa ou outra e não tens 4Gb de RAM, se calhar vais começar a usar swap, quando com a client VM cabe tudo na memória e a perda de performance não se nota (porque a aplicação passa a maior parte do tempo à espera de input).

Os comentários estão fechados.

%d bloggers like this: