Pergunta

Disclaimer: Eu olhei através este pergunta e esta questão mas ambos se descarrilou por pequenas detalhes e geral otimização é-desnecessárias preocupações. Eu realmente preciso de todo o desempenho I pode entrar em meu aplicativo atual, que é De processamento de recepção de dados MIDI-expelindo em tempo real. Também ele precisa ampliar tão bem quanto possível.

Eu estou comparando o desempenho array em um número alto de leituras para pequenas listas para ArrayList e também para ter apenas as variáveis ??na mão. Eu estou achando que um array bate ArrayList por um fator de 2,5 e ainda bate apenas ter as referências de objeto.

O que eu gostaria de saber é:

  1. É a minha referência ok? eu mudei a ordem dos testes e número de corridas sem alteração . Eu também usado milissegundos em vez de nanossegundos sem sucesso.
  2. eu deveria estar especificando quaisquer opções Java para minimizar esta diferença?
  3. Se esta diferença é real, neste caso, não deve prefiro Test[] para ArrayList<Test> nesta situação e colocar no código necessário para convertê-los? Obviamente, eu estou lendo muito mais do que a escrita .

JVM é Java 1.6.0_17 no OSX e é definitivamente em execução no modo Hotspot.

  public class ArraysVsLists {

    static int RUNS = 100000;

    public static void main(String[] args) {
        long t1;
        long t2;

        Test test1 = new Test();
        test1.thing = (int)Math.round(100*Math.random());
        Test test2 = new Test();
        test2.thing = (int)Math.round(100*Math.random());

        t1 = System.nanoTime();

        for (int i=0; i<RUNS; i++) {
            test1.changeThing(i);
            test2.changeThing(i);
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1) + " How long NO collection");

        ArrayList<Test> list = new ArrayList<Test>(1);
        list.add(test1);
        list.add(test2);
        // tried this too: helps a tiny tiny bit 
        list.trimToSize();

        t1= System.nanoTime();

        for (int i=0; i<RUNS; i++) {
            for (Test eachTest : list) {
                eachTest.changeThing(i);
            }
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1) + " How long collection");


        Test[] array = new Test[2];
        list.toArray(array);

        t1= System.nanoTime();

        for (int i=0; i<RUNS; i++) {
            for (Test test : array) {
                test.changeThing(i);
            }
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1) + " How long array ");

    }
}

class Test {
    int thing;
    int thing2;
    public void changeThing(int addThis) {
        thing2 = addThis + thing;
    }
}
Foi útil?

Solução

microbenchmarks são muito, muito difícil de acertar em uma plataforma como o Java. Você definitivamente tem que extrair o código a ser aferido em métodos separados, executá-los alguns milhares de vezes mais aquecimento e depois medir. Eu já fiz isso (código abaixo) eo resultado é que o acesso direto através de referências é seguida, três vezes mais rápido que através de uma matriz, mas a coleção ainda mais lento é por um fator de 2.

Estes números são baseados no opções JVM -server -XX:+DoEscapeAnalysis. Sem -server, usando a coleção é drasticamente mais lento (mas, estranhamente, acesso directo e variedade são um pouco mais rápido, indicando que há algo estranho acontecendo). -XX:+DoEscapeAnalysis produz outra aceleração de 30% para a recolha, mas é muito questionabled se ele vai funcionar tão bem para o seu código de produção real.

No geral a minha conclusão seria: esquecer microbenchmarks, eles podem muito facilmente ser enganosa. Meça o mais próximo possível código de produção possível, sem ter que reescrever o aplicativo inteiro.

import java.util.ArrayList;

public class ArrayTest {

    static int RUNS_INNER = 1000;
    static int RUNS_WARMUP = 10000;
    static int RUNS_OUTER = 100000;

    public static void main(String[] args) {
        long t1;
        long t2;

        Test test1 = new Test();
        test1.thing = (int)Math.round(100*Math.random());
        Test test2 = new Test();
        test2.thing = (int)Math.round(100*Math.random());

        for(int i=0; i<RUNS_WARMUP; i++)
        {
            testRefs(test1, test2);            
        }
        t1 = System.nanoTime();
        for(int i=0; i<RUNS_OUTER; i++)
        {
            testRefs(test1, test2);            
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1)/1000000.0 + " How long NO collection");

        ArrayList<Test> list = new ArrayList<Test>(1);
        list.add(test1);
        list.add(test2);
        // tried this too: helps a tiny tiny bit 
        list.trimToSize();

        for(int i=0; i<RUNS_WARMUP; i++)
        {
            testColl(list);
        }
        t1= System.nanoTime();

        for(int i=0; i<RUNS_OUTER; i++)
        {
            testColl(list);
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1)/1000000.0 + " How long collection");


        Test[] array = new Test[2];
        list.toArray(array);

        for(int i=0; i<RUNS_WARMUP; i++)
        {
            testArr(array);            
        }
        t1= System.nanoTime();

        for(int i=0; i<RUNS_OUTER; i++)
        {
            testArr(array);
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1)/1000000.0 + " How long array ");

    }

    private static void testArr(Test[] array)
    {
        for (int i=0; i<RUNS_INNER; i++) {
            for (Test test : array) {
                test.changeThing(i);
            }
        }
    }

    private static void testColl(ArrayList<Test> list)
    {
        for (int i=0; i<RUNS_INNER; i++) {
            for (Test eachTest : list) {
                eachTest.changeThing(i);
            }
        }
    }

    private static void testRefs(Test test1, Test test2)
    {
        for (int i=0; i<RUNS_INNER; i++) {
            test1.changeThing(i);
            test2.changeThing(i);
        }
    }
}

class Test {
    int thing;
    int thing2;
    public void changeThing(int addThis) {
        thing2 = addThis + thing;
    }
}

Outras dicas

A sua referência só é válido se o seu caso de uso real corresponde ao código de referência, isto é, muito poucas operações em cada elemento, de modo que o tempo de execução é largamente determinado pelo tempo de acesso, em vez das próprias operações. Se for esse o caso, então sim, você deve estar usando matrizes se o desempenho é crítico. Se, contudo, o seu caso de uso real, envolve computação muito mais real por elemento, então o tempo de acesso por elemento se tornará muito menos significativa.

Provavelmente não é válido. Se eu entender a maneira que compiladores JIT trabalho, compilando um método não irá afectar uma chamada para esse método que já está em execução. Como o método main só é chamado uma vez, ele vai acabar sendo interpretado, e uma vez que a maior parte do trabalho é feito no corpo desse método, os números que você começa não será particularmente indicativo de execução normal.

efeitos de compilação JIT pode ir alguma maneira a explicar por que o caso não coleções foi mais lento que o caso matrizes. Esse resultado é contra-intuitivo, e coloca uma dúvida sobre o outro resultado do benchmark que você relatou.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top