2013-07-07 2 views
3

Java 5는 for-each 루프를 제공해주었습니다.이 루프는 가능할 때마다 사용해야합니다.배열을 반복하는 관용구가 가장 효율적입니까?

블록의 배열 색인을 사용해야하는 경우 가장 효율적인 관용구는 무엇입니까?

// Option (1) 
for (int i = array.length - 1; i >= 0; i--) { 
    // Code. 
} 

// Option (2) 
for (int i = 0; i < array.length; i++) { 
    // Code. 
} 

// Option (3) 
for (int i = 0, n = array.length; i < n; i++) { 
    // Code. 
} 

는 (물론,이. 대부분의 프로그램하지만 유머 나에게 큰 성능 차이를하지 않습니다)

  1. 거꾸로 반복하고 끔찍한입니다 :-). 어쩌면 비우호적 인 캐시일까요? 또는 현대 가공업자가 메모리에서 뒤로 걷는 것을 감지 할 수 있습니까?

  2. 이 짧고 JIT 컴파일러가 array이 변경되지 않으므로 length이 본질적으로 (3)으로 바뀔 수 있다는 것을 알 수 있습니다. 하지만 그렇게 할 수 있습니까? 그것은 상한의 일부 Collection.size()을인지

  3. 이 가장 빠른 옵션으로 Effective Java에서 항목 45 여호수아 블로흐에 의해 제안 (JVM이 오라클의 핫스팟/자바 7이라고 가정). 그러나 배열에도 적용됩니까? 바이트 코드 (아래 참조)에서 사이클당 (사전 최적화 된) arraylength 명령어를 저장한다는 것을 알 수 있습니다. 달빅 가상 머신에서 루프에 대한

This question, 목록 (1) - (3)과 같은 빠른 느린합니다. 그러나 2008 년 정보는 Dalvik이 훨씬 더 성숙한 것이므로 지금도 그렇게 생각하지 않습니다. 위의 예에서 생성 된 바이트 코드를 보면

, 분명한 차이점이 있습니다

Compiled from "ForLoops.java" 
public class ForLoops extends java.lang.Object{ 
static int[] array; 

public ForLoops(); 
    Code: 
    0: aload_0 
    1: invokespecial #10; //Method java/lang/Object."<init>":()V 
    4: return 

public static void forLoop1(); 
    Code: 
    0: getstatic #17; //Field array:[I 
    3: arraylength 
    4: iconst_1 
    5: isub 
    6: istore_0 
    7: goto 13 
    10: iinc 0, -1 
    13: iload_0 
    14: ifge 10 
    17: return 

public static void forLoop2(); 
    Code: 
    0: iconst_0 
    1: istore_0 
    2: goto 8 
    5: iinc 0, 1 
    8: iload_0 
    9: getstatic #17; //Field array:[I 
    12: arraylength 
    13: if_icmplt 5 
    16: return 

public static void forLoop3(); 
    Code: 
    0: iconst_0 
    1: istore_0 
    2: getstatic #17; //Field array:[I 
    5: arraylength 
    6: istore_1 
    7: goto 13 
    10: iinc 0, 1 
    13: iload_0 
    14: iload_1 
    15: if_icmplt 10 
    18: return 

} 
+0

이론상 'array.length'는 한 번만 평가하면되기 때문에 일반적으로 1과 3이 2보다 효율적입니다. 그러나 Java에서'길이'를 평가하는 것은 매우 간단합니다. 특히 JITCed 인 경우 특히 그렇습니다. 따라서 이론적 인 차이점은 모두 실제로 사라지지만 말입니다. 대부분의 경우 루프 본문의 내용과 JITC 최적화 프로그램이 루프 본문과 루프 제어를 병합하는 방식에 따라 다릅니다. –

+0

@Nicholas 좋은 일이 바이트 코드를 꺼내! 그러나 바이트 코드가 궁극적으로 JIT로 인해 실행되는 것이 아니라는 점을 명심해야합니다. 우리가 JITed 지침을 얻을 수 있다면, 그것은 최종 답이 될 것이지만 이것은 (a) 플랫폼에 따라 다르며 (b) JITing의 "수준"이 다르므로 비현실적입니다. 또한 런타임 중에 생성 된 코드에 투명성을 부여하는 JIT에 대해서는 알지 못합니다. 최소한 JVM 메모리 공간을 직접 살펴 봐야합니다. – sigpwned

+0

"가장 효율적인"선택은 일반적으로 나머지 코드에 가장 적합한 선택이라는 점을 지적해야합니다. 예를 들어, 거꾸로 반복하는 것은 때로는 어색하고 때로는 매우 유용합니다. 마찬가지로 배열 객체가 루프 내부에서 대체되면 각 반복에서'array.length'를 확인하는 것이 실제로 필요할 수 있습니다. –

답변

1

쉽게 자신을 위해이를 테스트 할 수; 그렇게한다면 HotSpot 제작자가 직접 these tips about performance testing을 봐야 할 것입니다. This answer도 유용 할 수 있습니다. 이러한 구현을 테스트하기로 결정했다면, 우리가 찾은 것을 알려주십시오!

그러나 전체적으로 볼 때 이러한 것들에 대해 너무 걱정해서는 안됩니다. 대신에 읽을 수있는 코드를 작성하고 일을 끝내는 데 중점을 둡니다. 대부분의 경우, "트릭"없이 코드가 Fast Enough로 실행됩니다. 현대 하드웨어는 매우 빠르며 JIT도 꽤 좋습니다.

코드가 너무 느리게 실행되는 경우 첫 번째 프로필을 실행 한 다음 최적화하십시오. 다른 것은 조기입니다. 그리고 우리보다 더 똑똑한 사람에게서 기억하십시오 :

"시기상조 최적화는 모든 악의 뿌리입니다." - 도널드 크 누스

는 편집 : 당신의 측면에서 덜이에 대한 호기심 것 때문에 "내가 어떻게 내 코드를 작성해야합니까?" 그리고 생각 실험의 관점에서 볼 때 을 모두 같거나 더 빠른 속도로 실행해야합니다.

이러한 루프 중 어느 것도 분기 예측자를 조정할 가능성이 없습니다 (어쨌든 합리적 크기의 배열의 경우). 기본 JIT가 반복되는 배열 길이 참조를 (2) 스타일에서 (3) 스타일로 변환 할 것으로 기대합니다.모든 것이 평등하다면, (1)의 캐시 성능은 (2) 또는 (3)의 성능보다 떨어지지 않습니다. 주어진 배열에 대해 동일한 캐시 라인이로드되고 히트 (또는 히트되지 않음) 될 것입니다.

물론 내 기대는 부적합합니다. 알아야 할 유일한 방법은 테스트하는 것입니다! 테스트 할 때 writing good microbenchmarks is hard을 기억하십시오.

+1

+1 ... 내 의견을 편집했습니다 :) –

+1

테스트 할 수는 있지만 JVM 기능에 대한 generel 통찰력이 있습니다. 또한 성능이 중요하다고 가정 해 봅시다. – Nicholas

+0

@ Nicholas C 언어로 코드를 작성하고 싶지만 Java를 사용하라는 메시지가 나타났습니다. –

관련 문제