java8 foreach 성능 분석

6265 단어 배우다
자바 포커스 성능에 대한 사고 연구
문장[java8]을 읽으면서java8의foreach정명을 위해 문장에서 언급한 예열 코드가 무엇인지 생각하게 된다.글에서 언급한 바와 같이 현재 많은 성능 테스트 분석 결과java8foreach의 성능이 좋지 않은데 왜oracle회사의 개발자들은 최적화를 고려하지 않습니까?설마 정말 람바다 문법을 도입하기 위해 성능을 희생한 건 아니겠지?그럼lambada의 사용 장면은 매우 제한될 것이다.
생성된 어셈블리 코드
테스트 코드:
import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List test = new ArrayList<>();

        test.add("1");

        test.forEach(System.out::println);

        for (String s : test) {
            System.out.println(s);
        }
    }
}

생성된 어셈블리 구문
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."":()V
       7: astore_1
       8: aload_1
       9: ldc           #4                  // String 1
      11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      16: pop
      17: aload_1
      18: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      21: dup
      22: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      25: pop
      26: invokedynamic #8,  0              // InvokeDynamic #0:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;
      31: invokeinterface #9,  2            // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
      36: aload_1
      37: invokeinterface #10,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      42: astore_2
      43: aload_2
      44: invokeinterface #11,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      49: ifeq          72
      52: aload_2
      53: invokeinterface #12,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      58: checkcast     #13                 // class java/lang/String
      61: astore_3
      62: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      65: aload_3
      66: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      69: goto          43
      72: return
}

주:idea가 어셈블리 문법을 어떻게 설정하는지 어디서 두 사람이 생성한 기계 언어가 똑같다고 답장하는 것을 보았는지 잊어버렸다. 그 결과 두 사람의 어셈블리 문법을 비교한 결과 차이가 있었다.나중에 공식 문서를 살펴보니 이 말을 답장하고 싶은 학생은 공식적인 설명을 표현하고자 하는 것 같습니다. for Each vs for-loop Froom a simple point of view, both loops provide the same functionality – loop through elements in a collection.The main difference between the two of them is that they are different iterators – the enhanced for-loop is an external iterator whereas the new forEach method is an internal one. 원본에서 볼 때forEach도 내부에서 forloop을 실현하는데 그 문제는 내부의 교체기와 외부 교체기의 차이가 있는가 하는 것이다.
External Iterator VS Internal Iterator
실현에 있어 양자는 특별한 차이가 없다. 각종 문장을 보면 내부 교체를 숭배한다. 주로 제어를 라이브러리에 맡긴다. 이렇게 하면 외부 교체가 실현할 수 없는 일부 기능을 실현할 수 있다. 예를 들어 정렬, 병행, 단락(short-circuiting)을 통해 값을 구하고 타성 값을 구하여 성능을 개선할 수 있다.그렇다면 이들의 성능 차이는 무엇 때문일까, 왜 생긴 것일까?
java8 foreach에서 Consumer 도입
Consumer 선언으로 인한 시간 오버헤드입니까?새로운 테스트 방법:
import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List test = new ArrayList<>();

        for (int i = 0; i < 100000; i++) {
            test.add(i);
        }

        long l = System.currentTimeMillis();
        test.forEach(i -> {
        });
        System.out.println(System.currentTimeMillis() - l);

        l = System.currentTimeMillis();
        for (Integer s : test) {
        }
        System.out.println(System.currentTimeMillis() - l);
    }
}


테스트 크기의 수정이 아무리 커도 loop과foreach는 10배의 양적 차이를 보인다. 만약에 이곳의 소모 시차가 Consumer를 성명해서 일어난 것이라고 가정하면 이때 코드를 수정한다.
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class Main {

    public static void main(String[] args) {
        List test = new ArrayList<>();

        for (int i = 0; i < 100000; i++) {
            test.add(i);
        }

        long l = System.currentTimeMillis();
        Consumer consumer = integer -> {
        };
        System.out.println(System.currentTimeMillis() - l);

        l = System.currentTimeMillis();
        test.forEach(consumer);
        System.out.println(System.currentTimeMillis() - l);

        l = System.currentTimeMillis();
        for (Integer s : test) {
        }
        System.out.println(System.currentTimeMillis() - l);
    }
}


이때 두 개의 반복 소모 시간이 같고 10배급의 소모 비용이 모두 Consumer를 성명하고 있음을 발견했다.우리는 사실foreach의 비용이 모두Consumer를 성명하고 있다는 결론을 얻었다. 교체기의 실현에 대해 내부 교체와 외부 교체는 성능에 차이가 없다.
사전 가열이 Consumer 선언의 시간 오버헤드를 최적화하는지 여부
아니면 방금 테스트 코드:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class Main {

    public static void main(String[] args) {
        List test = new ArrayList<>();

        for (int i = 0; i < 100000; i++) {
            test.add(i);
        }

        long l = System.currentTimeMillis();
        Consumer consumer = integer -> {
        };
        System.out.println(System.currentTimeMillis() - l);

        l = System.currentTimeMillis();
        Consumer consumer1 = integer -> {
        };
        System.out.println(System.currentTimeMillis() - l);

        l = System.currentTimeMillis();
        Consumer consumer2 = integer -> {
        };
        System.out.println(System.currentTimeMillis() - l);
    }
}


이때 두세 번째 시간 비용이 첫 번째 시간 비용보다 훨씬 적다는 것을 발견했다. 이때 JIT가 꾸민 꿍꿍이수작인지 추측해 보자.이로써 foreach 성능 차이에 따른 예가열 분석은 일단락되고, 항소심에서 제기된 JIT의 영향 여부는 이후 글에서 계속 분석된다.최근에는 마침내 이전의 생각, 예를 들면 jdk 원본을 보고 자신의 블로그를 쓰는 등이다.이것은 첫 번째 블로그이니 싫으면 탓하지 마라.

좋은 웹페이지 즐겨찾기