소규모 어레이 벤치마킹과 비교Java의 목록:내 벤치마킹 코드가 잘못된 걸까요?

StackOverflow https://stackoverflow.com/questions/2227947

문제

부인 성명: 나는 살펴보았다 이 질문 그리고 이 질문그러나 그들은 작은 세부 사항과 일반적인 최적화에 의해 탈선되었습니다.현재 앱에서 얻을 수있는 모든 성능이 필요합니다.이 앱은 실시간으로 처리 스피킹 MIDI 데이터를 수신합니다.역시 스케일업이 필요해 뿐만 아니라 가능한 한.

나는 비교하고있다 array 작은 목록에 대한 많은 읽기 성능 ArrayList 또한 변수를 손에 쥐는 것만으로도 충분합니다.배열이 더 나은 것으로 나타났습니다. ArrayList 2.5배로 심지어 객체 참조를 갖는 것보다 낫습니다.

내가 알고 싶은 것은:

  1. 내 벤치마크는 괜찮나요? 테스트 순서와 실행 횟수를 변경하지 않고 전환했습니다..또한 나노초 대신 밀리초를 사용해도 소용이 없었습니다.
  2. 이 차이를 최소화하려면 Java 옵션을 지정해야 합니까?
  3. 이 차이가 실제라면, 이 경우에는 내가 선호하면 안 될까? Test[] 에게 ArrayList<Test> 이 상황에서 이를 변환하는 데 필요한 코드를 삽입하시겠습니까? 분명히 나는 ​​글을 쓰는 것보다 읽는 것을 더 많이 하고 있습니다.

JVM은 OSX의 Java 1.6.0_17이며 확실히 핫스팟 모드에서 실행되고 있습니다.

  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;
    }
}
도움이 되었습니까?

해결책

Microbenchmark는 Java와 같은 플랫폼에서 제대로 작동하기가 매우 어렵습니다.벤치마킹할 코드를 별도의 메소드로 추출하고 워밍업으로 수천 번 실행한 후 측정해야 합니다.나는 그것을 수행했고(아래 코드) 결과적으로 참조를 통한 직접 액세스는 배열을 통한 것보다 3배 빠르지만 컬렉션은 여전히 ​​2배만큼 느립니다.

이 숫자는 JVM 옵션을 기반으로 합니다. -server -XX:+DoEscapeAnalysis.없이 -server, 컬렉션을 사용하는 것은 과감하게 느립니다(그러나 이상하게도 직접 ​​액세스와 배열 액세스는 상당히 빨라서 뭔가 이상한 일이 일어나고 있음을 나타냅니다). -XX:+DoEscapeAnalysis 컬렉션 속도가 30% 더 향상되지만 실제 프로덕션 코드에서도 제대로 작동할지 여부는 매우 의심스럽습니다.

전반적으로 내 결론은 다음과 같습니다.마이크로벤치마크는 잊어버리십시오. 너무 쉽게 오해를 불러일으킬 수 있습니다.전체 애플리케이션을 다시 작성하지 않고도 프로덕션 코드에 최대한 가깝게 측정하세요.

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

다른 팁

벤치마크는 실제 사용 사례가 벤치마크 코드와 일치하는 경우에만 유효합니다.각 요소에 대한 작업이 거의 없으므로 실행 시간은 작업 자체보다는 액세스 시간에 따라 크게 결정됩니다.그렇다면 그렇습니다. 성능이 중요하다면 어레이를 사용해야 합니다.그러나 실제 사용 사례에 요소당 실제 계산이 더 많이 포함된다면 요소당 액세스 시간은 훨씬 덜 중요해집니다.

아마도 유효하지 않을 것입니다.JIT 컴파일러의 작동 방식을 이해한다면 메서드를 컴파일해도 이미 실행 중인 해당 메서드에 대한 호출에는 영향을 주지 않습니다.이후 main 메소드는 한 번만 호출되면 결국 해석되고, 대부분의 작업이 해당 메소드의 본문에서 수행되므로, 얻는 숫자는 특별히 정상적인 실행을 나타내지는 않습니다.

JIT 컴파일 효과는 컬렉션이 없는 경우가 배열의 경우보다 느린 이유를 설명하는 데 도움이 될 수 있습니다.그 결과는 직관에 반하며 귀하가 보고한 다른 벤치마크 결과에 의문을 제기합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top