Frage

  

Hinweis: Ich habe durch ausgesehen   Frage und href="https://stackoverflow.com/questions/716597">   aber sie beide durch kleine entgleist wurde   Details und allgemeine   Optimierung-is-unnötige Sorgen.   Ich brauche wirklich die ganze Leistung I   kann in meiner aktuellen App bekommen, das ist   Empfangen von verarbeitungs spewing MIDI-Daten   in Echtzeit. Auch braucht es, Scale-up    so gut wie möglich.

Ich bin im Vergleich array Leistung auf einer hohen Anzahl von für kleine Listen ArrayList liest und auch nur die Variablen in der Hand haben. Ich finde, dass ein Array ArrayList um einen Faktor von 2,5 und schlägt sogar nur Beats mit den Objektreferenzen.

Was möchte ich wissen möchte, ist:

  1. Ist mein Benchmark okay? Ich habe die Reihenfolge der Tests und die Anzahl der Durchläufe ohne Änderung geschaltet . Ich habe auch Millisekunden statt ns ohne Erfolg verwendet.
  2. sollte ich alle Java-Optionen werden die Angabe dieser Differenz minimieren?
  3. Wenn dieser Unterschied wirklich ist, in diesem Fall soll ich nicht lieber Test[] zu ArrayList<Test> in dieser Situation und im Code setzte notwendig, sie zu konvertieren? Ich bin natürlich sehr viel mehr als das Schreiben zu lesen .

JVM ist Java 1.6.0_17 auf OSX, und es wird auf jeden Fall in Hotspot-Modus ausgeführt wird.

  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;
    }
}
War es hilfreich?

Lösung

Microbenchmarks sind sehr, sehr hart rechts auf einer Plattform wie Java zu erhalten. Sie haben auf jeden Fall den Code zu extrahieren, in separate Methoden gebenchmarkt werden, um sie ein paar tausend Mal so Warm-up laufen und dann messen. Ich habe getan, dass (Code unten) und das Ergebnis ist, dass die direkte Zugriff durch Referenzen ist dann dreimal so schnell wie durch ein Array, aber die Sammlung ist noch langsamer um den Faktor 2.

Diese Zahlen basieren auf der JVM-Optionen -server -XX:+DoEscapeAnalysis. Ohne -server, die Sammlung mit ist drastisch langsamer (aber seltsamerweise direkt und Array-Zugriff ist ziemlich viel schneller, was darauf hinwies, dass es etwas seltsam los ist). -XX:+DoEscapeAnalysis liefert weitere 30% Speedup für die Sammlung, aber es ist sehr viel questionabled, ob es für Ihre tatsächlichen Produktionscode als auch funktionieren wird.

Insgesamt meine Schlussfolgerung wäre: vergessen Microbenchmarks, können sie zu leicht irreführend sein. Messen Sie so nah an der Produktion Code, wie Sie können, ohne Ihre gesamte Anwendung neu zu schreiben.

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;
    }
}

Andere Tipps

ist Ihre Benchmark nur gültig, wenn Ihr tatsächlicher Anwendungsfall des Benchmark-Code übereinstimmt, das heißt sehr wenige Operationen für jedes Element, so dass die Ausführungszeit weitgehend durch Zugriffszeit bestimmt wird, anstatt die Operationen selbst. Wenn das der Fall ist, dann ja, dann sollten Sie mit Arrays sein, wenn die Leistung entscheidend ist. Wenn jedoch Ihr realer Anwendungsfall viel mehr tatsächliche Berechnung pro Element beinhaltet, dann wird die Zugriffszeit pro Element wird viel weniger signifikant.

Es ist wahrscheinlich nicht gültig. Wenn ich die Art und Weise verstehen, dass JIT Compiler Arbeit, ein Verfahren Kompilieren nicht um einen Anruf zu dieser Methode beeinflussen, die bereits ausgeführt wird. Da die main Methode nur einmal aufgerufen wird, wird es interpretiert, am Ende wird, und der größte Teil der Arbeit, da im Körper dieses Verfahren durchgeführt wird, werden die Zahlen, die Sie bekommen nicht besonders bezeichnend für die normale Ausführung.

JIT-Kompilierung Effekte können in gewisser Weise zu erklären, warum die keine Sammlungen Fall langsamer war, dass die Arrays Fall. Das Ergebnis ist kontraintuitiv, und es stellt einen Zweifel an dem anderen Benchmark-Ergebnis, dass Sie berichtet.

scroll top