IT story

등가 정적 및 비 정적 방법의 속도 차이가 큼

hot-time 2020. 9. 14. 21:39
반응형

등가 정적 및 비 정적 방법의 속도 차이가 큼


이 코드에서는 main메서드 에서 Object를 생성 한 다음 해당 objects method : ff.twentyDivCount(i)(runs in 16010 ms)를 호출하면 다음 주석을 사용하여 호출하는 것보다 훨씬 빠르게 twentyDivCount(i)실행됩니다 . (runs in 59516 ms). 물론 객체를 생성하지 않고 실행하면 메서드를 정적으로 만들어서 메인에서 호출 할 수 있습니다.

public class ProblemFive {

    // Counts the number of numbers that the entry is evenly divisible by, as max is 20
    int twentyDivCount(int a) {    // Change to static int.... when using it directly
        int count = 0;
        for (int i = 1; i<21; i++) {

            if (a % i == 0) {
                count++;
            }
        }
        return count;
    }

    public static void main(String[] args) {
        long startT = System.currentTimeMillis();;
        int start = 500000000;
        int result = start;

        ProblemFive ff = new ProblemFive();

        for (int i = start; i > 0; i--) {

            int temp = ff.twentyDivCount(i); // Faster way
                       // twentyDivCount(i) - slower

            if (temp == 20) {
                result = i;
                System.out.println(result);
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();;
        System.out.println((end - startT) + " ms");
    }
}

편집 : 지금까지 다른 시스템이 다른 결과를 생성하는 것처럼 보이지만 JRE 1.8. *를 사용하면 원래 결과가 일관되게 재현되는 것처럼 보입니다.


JRE 1.8.0_45를 사용하면 비슷한 결과를 얻습니다.

조사:

  1. -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInliningVM 옵션으로 자바를 실행 하면 두 메서드가 모두 컴파일되고 인라인됨을 보여줍니다.
  2. 메서드 자체에 대해 생성 된 어셈블리를 보면 큰 차이가 없습니다.
  3. 그러나 일단 인라인되면 생성 된 어셈블리 main는 매우 다릅니다. 인스턴스 메서드는 특히 루프 언 롤링 측면에서보다 적극적으로 최적화됩니다.

그런 다음 테스트를 다시 실행했지만 위의 의심을 확인하기 위해 다른 루프 풀기 설정을 사용했습니다. 다음과 같이 코드를 실행했습니다.

  • -XX:LoopUnrollLimit=0 두 방법 모두 느리게 실행됩니다 (기본 옵션이있는 정적 방법과 유사).
  • -XX:LoopUnrollLimit=100 두 방법 모두 빠르게 실행됩니다 (기본 옵션이있는 인스턴스 방법과 유사).

결론적으로 기본 설정을 사용하면 핫스팟 1.8.0_45 의 JIT 메서드가 정적 일 때 루프를 풀 수없는 것 같습니다 (그런 방식으로 작동하는 이유는 모르겠지만). 다른 JVM은 다른 결과를 생성 할 수 있습니다.


assylias의 답변에 근거한 입증되지 않은 추측입니다.

JVM은 70과 같은 루프 언 롤링에 대한 임계 값을 사용합니다. 어떤 이유로 든 정적 호출은 약간 더 크고 언 롤링되지 않습니다.

결과 업데이트

  • LoopUnrollLimit(52) 아래에서, 두 버전은 느리다.
  • 52에서 71 사이에는 정적 버전 만 느립니다.
  • 71 이상에서는 두 버전 모두 빠릅니다.

내 생각에 정적 호출이 내부 표현에서 약간 더 크고 OP가 이상한 경우에 부딪 혔기 때문에 이것은 이상합니다. 그러나 그 차이는 약 20 인 것 같습니다.

 

-XX:LoopUnrollLimit=51
5400 ms NON_STATIC
5310 ms STATIC
-XX:LoopUnrollLimit=52
1456 ms NON_STATIC
5305 ms STATIC
-XX:LoopUnrollLimit=71
1459 ms NON_STATIC
5309 ms STATIC
-XX:LoopUnrollLimit=72
1457 ms NON_STATIC
1488 ms STATIC

실험하려는 사람들에게는 내 버전 이 유용 할 수 있습니다.


When this is executed in debug mode, the numbers are the same for the instance and static cases. That further means that the JIT hesitates to compile the code to native code in the static case the same way as it does in the instance method case.

왜 그렇게합니까? 말하기 어렵습니다. 이것이 더 큰 응용 프로그램이라면 아마도 올바른 일을 할 것입니다 ...


I just tweaked the test slightly and I got the following results:

산출:

Dynamic Test:
465585120
232792560
232792560
51350 ms
Static Test:
465585120
232792560
232792560
52062 ms

노트

개별적으로 테스트하는 동안 동적의 경우 ~ 52 초, 정적의 경우 ~ 200 초를 얻었습니다.

이것은 프로그램입니다 :

public class ProblemFive {

    // Counts the number of numbers that the entry is evenly divisible by, as max is 20
    int twentyDivCount(int a) {  // Change to static int.... when using it directly
        int count = 0;
        for (int i = 1; i<21; i++) {

            if (a % i == 0) {
                count++;
            }
        }
        return count;
    }

    static int twentyDivCount2(int a) {
         int count = 0;
         for (int i = 1; i<21; i++) {

             if (a % i == 0) {
                 count++;
             }
         }
         return count;
    }

    public static void main(String[] args) {
        System.out.println("Dynamic Test: " );
        dynamicTest();
        System.out.println("Static Test: " );
        staticTest();
    }

    private static void staticTest() {
        long startT = System.currentTimeMillis();;
        int start = 500000000;
        int result = start;

        for (int i = start; i > 0; i--) {

            int temp = twentyDivCount2(i);

            if (temp == 20) {
                result = i;
                System.out.println(result);
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();;
        System.out.println((end - startT) + " ms");
    }

    private static void dynamicTest() {
        long startT = System.currentTimeMillis();;
        int start = 500000000;
        int result = start;

        ProblemFive ff = new ProblemFive();

        for (int i = start; i > 0; i--) {

            int temp = ff.twentyDivCount(i); // Faster way

            if (temp == 20) {
                result = i;
                System.out.println(result);
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();;
        System.out.println((end - startT) + " ms");
    }
}

또한 테스트 순서를 다음과 같이 변경했습니다.

public static void main(String[] args) {
    System.out.println("Static Test: " );
    staticTest();
    System.out.println("Dynamic Test: " );
    dynamicTest();
}

그리고 나는 이것을 얻었다 :

Static Test:
465585120
232792560
232792560
188945 ms
Dynamic Test:
465585120
232792560
232792560
50106 ms

보시다시피 정적보다 동적이 호출되면 정적 속도가 크게 감소합니다.

이 벤치 마크를 기반으로 :

나는 모든 것이 JVM 최적화에 달려 있다고 가정 합니다. 따라서 정적 및 동적 방법을 사용하기 위해 경험 법칙을 따르는 것이 좋습니다.

엄지 손가락 규칙 :

Java: when to use static methods


Please try:

public class ProblemFive {
    public static ProblemFive PROBLEM_FIVE = new ProblemFive();

    public static void main(String[] args) {
        long startT = System.currentTimeMillis();
        int start = 500000000;
        int result = start;


        for (int i = start; i > 0; i--) {
            int temp = PROBLEM_FIVE.twentyDivCount(i); // faster way
            // twentyDivCount(i) - slower

            if (temp == 20) {
                result = i;
                System.out.println(result);
                System.out.println((System.currentTimeMillis() - startT) + " ms");
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();
        System.out.println((end - startT) + " ms");
    }

    int twentyDivCount(int a) {  // change to static int.... when using it directly
        int count = 0;
        for (int i = 1; i < 21; i++) {

            if (a % i == 0) {
                count++;
            }
        }
        return count;
    }
}

참고URL : https://stackoverflow.com/questions/30454904/large-difference-in-speed-of-equivalent-static-and-non-static-methods

반응형