Java에서 instanceof 사용의 성능 영향 나는 응용 프로그램을 작업 중이며

나는 응용 프로그램을 작업 중이며 한 가지 디자인 방식에는 instanceof운영자를 극도로 많이 사용하는 것이 포함됩니다 . OO 디자인은 일반적으로을 사용하지 않으려 고 instanceof하지만 다른 이야기 이며이 질문은 성능과 전적으로 관련이 있습니다. 성능에 영향이 있는지 궁금합니다. 만큼 빠릅니다 ==?

예를 들어 10 개의 하위 클래스가있는 기본 클래스가 있습니다. 기본 클래스를 사용하는 단일 함수에서 클래스가 하위 클래스의 인스턴스인지 확인하고 루틴을 수행합니다.

내가 해결하려고 생각한 다른 방법 중 하나는 대신 “type id”정수 프리미티브를 사용하고 비트 마스크를 사용하여 서브 클래스의 범주를 표시 한 다음 서브 클래스 “type id”를 카테고리를 나타내는 상수 마스크.

되어 instanceof어떻게 든 빨리보다 수 JVM에 의해 최적화? Java를 고수하고 싶지만 앱의 성능이 중요합니다. 이 길을 가본 적이있는 사람이 조언을 해줄 수 있다면 좋을 것입니다. 너무 많이 nitpicking하거나 최적화하기 위해 잘못된 것에 집중하고 있습니까?



답변

최신 JVM / JIC 컴파일러는 instanceof, 예외 처리, 리플렉션 등을 포함하여 전통적으로 “느린”작업의 성능 저하를 제거했습니다.

도널드 크 누스 (Donald Knuth)는 “우리는 작은 효율성에 대해 잊어야합니다. 시간의 약 97 %를 말합니다. 조기 최적화는 모든 악의 근원입니다.” instanceof의 성능은 문제가되지 않으므로 문제가 확실 할 때까지 이국적인 해결 방법을 찾는 데 시간을 낭비하지 마십시오.


답변

접근하다

다양한 구현을 평가하기 위해 벤치 마크 프로그램 을 작성 했습니다 .

  1. instanceof 구현 (참조)
  2. 추상 클래스를 통해 객체 지향 @Override 테스트 메소드
  3. 자체 형식 구현 사용
  4. getClass() == _.class 이행

jmh 를 사용 하여 100 개의 워밍업 호출, 측정중인 1000 회 반복 및 10 개의 포크로 벤치 마크를 실행했습니다. 따라서 각 옵션은 10,000 회 측정되었으며 macOS 10.12.4 및 Java 1.8이 설치된 MacBook Pro에서 전체 벤치 마크를 실행하려면 12:18:57이 걸립니다. 벤치 마크는 각 옵션의 평균 시간을 측정합니다. 자세한 내용은 GitHub에서 구현을 참조하십시오. .

완전성 을 위해이 답변이전 버전과 내 벤치 마크가 있습니다. 있습니다.

결과

| 운영 | 작업 당 나노초의 런타임 | instanceof와 관련 |
| ------------ | ------------------------------------ -| ------------------------ |
| INSTANCEOF | 39,598 ± 0,022 ns / op | 100,00 % |
| GETCLASS | 39,687 ± 0,021 ns / op | 100,22 % |
| 종류 | 46,295 ± 0,026 ns / op | 116,91 % |
| OO | 48,078 ± 0,026 ns / op | 121,42 % |

tl; dr

Java 1.8에서는 매우 근접 instanceof하지만 가장 빠른 방법 getClass()입니다.


답변

instanceOf 성능이 문자가 하나 인 문자열 객체에 대한 간단한 s.equals () 호출과 비교되는 방법을보기 위해 간단한 테스트를 수행했습니다.

10.000.000 루프에서 instanceOf는 63-96ms를, 문자열 equals는 106-230ms를 주었다

Java jvm 6을 사용했습니다.

따라서 간단한 테스트에서 하나의 문자열 비교 대신 instanceOf를 수행하는 것이 더 빠릅니다.

문자열 대신 Integer의 .equals ()를 사용하면 == 사용했을 때만 동일한 결과를 얻었습니다.


답변

성능 영향을 결정하는 항목은 다음과 같습니다.

  1. instanceof 연산자가 true를 리턴 할 수있는 가능한 클래스 수
  2. 데이터 배포-첫 번째 또는 두 번째 시도에서 대부분의 인스턴스 작업이 해결됩니까? 실제 작업을 가장 많이 반환 할 수 있습니다.
  3. 배치 환경. Sun Solaris VM에서 실행하는 것은 Sun의 Windows JVM과 크게 다릅니다. Solaris는 기본적으로 ‘서버’모드에서 실행되는 반면 Windows는 클라이언트 모드에서 실행됩니다. Solaris의 JIT 최적화는 모든 메소드 액세스를 동일하게 수행 할 수있게합니다.

나는 네 가지 다른 파견 방법에 대한 마이크로 벤치 마크를 만들었습니다 . Solaris의 결과는 다음과 같으며 숫자가 작을수록 빠릅니다.

InstanceOf 3156
class== 2925
OO 3083
Id 3067 


답변

가장 마지막 질문에 답하기 : 프로파일 러가 말하지 않는 한, 인스턴스에서 우스꽝스러운 시간을 보낸다는 것입니다.

최적화 할 필요가없는 것을 최적화하는 것에 대해 궁금해하기 전에 : 알고리즘을 가장 읽기 쉬운 방식으로 작성하고 실행하십시오. jit-compiler가 자체 최적화 할 수있을 때까지 실행하십시오. 그런 다음이 코드 조각에 문제가있는 경우 프로파일 러를 사용하여 가장 많은 정보를 얻고 최적화 할 수있는 위치를 알려주십시오.

고도로 최적화 된 컴파일러의 경우 병목 현상에 대한 추측이 완전히 틀릴 수 있습니다.

그리고이 답변의 진정한 정신으로 (나는 진심으로 믿습니다) : 일단 jit 컴파일러가 그것을 최적화 할 수있는 기회를 얻은 후에 instanceof와 ==가 어떻게 관련되는지는 절대 모릅니다.

잊어 버렸습니다 : 첫 번째 달리기를 측정하지 마십시오.


답변

나는 같은 질문을 가지고 있지만 내 것과 비슷한 유스 케이스에 대한 ‘성능 메트릭’을 찾지 못했기 때문에 샘플 코드를 더 많이 작성했습니다. 내 하드웨어와 Java 6 & 7에서 instanceof와 스위치의 10mln 반복의 차이점은

for 10 child classes - instanceof: 1200ms vs switch: 470ms
for 5 child classes  - instanceof:  375ms vs switch: 204ms

따라서 instanceof는 특히 많은 if-else-if 문에서 실제로 느리지 만 실제 응용 프로그램에서는 차이가 무시할 수 있습니다.

import java.util.Date;

public class InstanceOfVsEnum {

    public static int c1, c2, c3, c4, c5, c6, c7, c8, c9, cA;

    public static class Handler {
        public enum Type { Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, TypeA }
        protected Handler(Type type) { this.type = type; }
        public final Type type;

        public static void addHandlerInstanceOf(Handler h) {
            if( h instanceof H1) { c1++; }
            else if( h instanceof H2) { c2++; }
            else if( h instanceof H3) { c3++; }
            else if( h instanceof H4) { c4++; }
            else if( h instanceof H5) { c5++; }
            else if( h instanceof H6) { c6++; }
            else if( h instanceof H7) { c7++; }
            else if( h instanceof H8) { c8++; }
            else if( h instanceof H9) { c9++; }
            else if( h instanceof HA) { cA++; }
        }

        public static void addHandlerSwitch(Handler h) {
            switch( h.type ) {
                case Type1: c1++; break;
                case Type2: c2++; break;
                case Type3: c3++; break;
                case Type4: c4++; break;
                case Type5: c5++; break;
                case Type6: c6++; break;
                case Type7: c7++; break;
                case Type8: c8++; break;
                case Type9: c9++; break;
                case TypeA: cA++; break;
            }
        }
    }

    public static class H1 extends Handler { public H1() { super(Type.Type1); } }
    public static class H2 extends Handler { public H2() { super(Type.Type2); } }
    public static class H3 extends Handler { public H3() { super(Type.Type3); } }
    public static class H4 extends Handler { public H4() { super(Type.Type4); } }
    public static class H5 extends Handler { public H5() { super(Type.Type5); } }
    public static class H6 extends Handler { public H6() { super(Type.Type6); } }
    public static class H7 extends Handler { public H7() { super(Type.Type7); } }
    public static class H8 extends Handler { public H8() { super(Type.Type8); } }
    public static class H9 extends Handler { public H9() { super(Type.Type9); } }
    public static class HA extends Handler { public HA() { super(Type.TypeA); } }

    final static int cCycles = 10000000;

    public static void main(String[] args) {
        H1 h1 = new H1();
        H2 h2 = new H2();
        H3 h3 = new H3();
        H4 h4 = new H4();
        H5 h5 = new H5();
        H6 h6 = new H6();
        H7 h7 = new H7();
        H8 h8 = new H8();
        H9 h9 = new H9();
        HA hA = new HA();

        Date dtStart = new Date();
        for( int i = 0; i < cCycles; i++ ) {
            Handler.addHandlerInstanceOf(h1);
            Handler.addHandlerInstanceOf(h2);
            Handler.addHandlerInstanceOf(h3);
            Handler.addHandlerInstanceOf(h4);
            Handler.addHandlerInstanceOf(h5);
            Handler.addHandlerInstanceOf(h6);
            Handler.addHandlerInstanceOf(h7);
            Handler.addHandlerInstanceOf(h8);
            Handler.addHandlerInstanceOf(h9);
            Handler.addHandlerInstanceOf(hA);
        }
        System.out.println("Instance of - " + (new Date().getTime() - dtStart.getTime()));

        dtStart = new Date();
        for( int i = 0; i < cCycles; i++ ) {
            Handler.addHandlerSwitch(h1);
            Handler.addHandlerSwitch(h2);
            Handler.addHandlerSwitch(h3);
            Handler.addHandlerSwitch(h4);
            Handler.addHandlerSwitch(h5);
            Handler.addHandlerSwitch(h6);
            Handler.addHandlerSwitch(h7);
            Handler.addHandlerSwitch(h8);
            Handler.addHandlerSwitch(h9);
            Handler.addHandlerSwitch(hA);
        }
        System.out.println("Switch of - " + (new Date().getTime() - dtStart.getTime()));
    }
}


답변

instanceof CPU 명령 몇 개만 사용하면 정말 빠릅니다.

X클래스에로드 된 서브 클래스 가없는 경우 (JVM은 알고 있음) 다음과 instanceof같이 최적화 할 수 있습니다.

     x instanceof X
==>  x.getClass()==X.class
==>  x.classID == constant_X_ID

주요 비용은 단지 읽기입니다!

X서브 클래스가로드 된 경우 몇 가지 추가 읽기가 필요합니다. 그것들은 같은 위치에있을 가능성이 높기 때문에 추가 비용도 매우 낮습니다.

모두 좋은 소식입니다!