2016-09-01 4 views
2

저는 오늘 자바 튜토리얼에서 아래의 코드를 읽었습니다.Java에서 루프를 최적화하기 위해

StackTraceElement elements[] = e.getStackTrace(); 
for (int i = 0, n = elements.length; i < n; i++) { 
    logger.log(Level.WARNING, elements[i].getMethodName()); 
} 

다음과 같은 코드를 초보자로 작성할 수 있습니다.

StackTraceElement elements[] = e.getStackTrace(); 

for (int i = 0; i < elements.length; i++) { 
    logger.log(Level.WARNING, elements[i].getMethodName()); 
} 

또는 다음과 같이하십시오.

int n = elements.length; 
for (int i = 0; i < n; i++) { 
    logger.log(Level.WARNING, elements[i].getMethodName()); 
} 

나는 그들 사이의 차이점을 묻고 싶습니다. Java에서 for 루프를 최적화하는 다른 훌륭한 방법이 있습니까?

+2

TBH'for (int i = 0, n = elements.length; i

+0

@EricZhang 이것을 보았던 튜토리얼에 대한 링크를 게시 할 수 있습니까? – Kayaman

+0

@Kayaman 자바 튜토리얼에서. url은 https://docs.oracle.com/javase/tutorial/essential/exceptions/chained.html –

답변

3

최적화의 황금률은 그렇지 않습니다.

두 번째 황금률은 중요하지 않습니다.

세 번째 황금률은 실제로 질문입니다. 측정 했습니까?

질문에 답하는 것은 팀에서 허용되는 표준이 무엇이든간에 : 이 아닌 다른 코드는 사용하지 않아야합니다. 즉, 훌륭한 팀에서 코드는 모두 비슷하고 지루합니다. 어떤 조치도 제쳐두고 거대한 번쩍이는 표시가 있습니다. "조심해. 규칙에서 예외 다!" - 당신이 정확히 말하고 싶지 않다면, 다른 사람들이 사용하는 것과 같이 가십시오.

지금, 코드 현명한 대답은 컴파일러에 있습니다

import java.util.logging.*; 

public class ForLoop { 
    private static Logger logger = Logger.getLogger("log"); 

    public static void a(Throwable e) { 
     StackTraceElement elements[] = e.getStackTrace(); 
     for (int i = 0, n = elements.length; i < n; i++) { 
      logger.log(Level.WARNING, elements[i].getMethodName()); 
     } 
    } 

    public static void b(Throwable e) { 
     StackTraceElement elements[] = e.getStackTrace(); 
     for (int i = 0; i < elements.length; i++) { 
      logger.log(Level.WARNING, elements[i].getMethodName()); 
     } 
    } 

    public static void c(Throwable e) { 
     StackTraceElement elements[] = e.getStackTrace(); 
     int n = elements.length; 
     for (int i = 0; i < n; i++) { 
      logger.log(Level.WARNING, elements[i].getMethodName()); 
     } 
    } 
} 

a()c()variable assignment 만 다른, 참조, 따라서 것이다 바와 같이

alf-pro:Learning alf$ javap -p -c ForLoop.class 
    Compiled from "ForLoop.java" 
    public class ForLoop { 
    private static java.util.logging.Logger logger; 

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

    public static void a(java.lang.Throwable); 
     Code: 
     0: aload_0  
     1: invokevirtual #2     // Method java/lang/Throwable.getStackTrace:()[Ljava/lang/StackTraceElement; 
     4: astore_1  
     5: iconst_0  
     6: istore_2  
     7: aload_1  
     8: arraylength 
     9: istore_3  
     10: iload_2  
     11: iload_3  
     12: if_icmpge  36 
     15: getstatic  #3     // Field logger:Ljava/util/logging/Logger; 
     18: getstatic  #4     // Field java/util/logging/Level.WARNING:Ljava/util/logging/Level; 
     21: aload_1  
     22: iload_2  
     23: aaload   
     24: invokevirtual #5     // Method java/lang/StackTraceElement.getMethodName:()Ljava/lang/String; 
     27: invokevirtual #6     // Method java/util/logging/Logger.log:(Ljava/util/logging/Level;Ljava/lang/String;)V 
     30: iinc   2, 1 
     33: goto   10 
     36: return   

    public static void b(java.lang.Throwable); 
     Code: 
     0: aload_0  
     1: invokevirtual #2     // Method java/lang/Throwable.getStackTrace:()[Ljava/lang/StackTraceElement; 
     4: astore_1  
     5: iconst_0  
     6: istore_2  
     7: iload_2  
     8: aload_1  
     9: arraylength 
     10: if_icmpge  34 
     13: getstatic  #3     // Field logger:Ljava/util/logging/Logger; 
     16: getstatic  #4     // Field java/util/logging/Level.WARNING:Ljava/util/logging/Level; 
     19: aload_1  
     20: iload_2  
     21: aaload   
     22: invokevirtual #5     // Method java/lang/StackTraceElement.getMethodName:()Ljava/lang/String; 
     25: invokevirtual #6     // Method java/util/logging/Logger.log:(Ljava/util/logging/Level;Ljava/lang/String;)V 
     28: iinc   2, 1 
     31: goto   7 
     34: return   

    public static void c(java.lang.Throwable); 
     Code: 
     0: aload_0  
     1: invokevirtual #2     // Method java/lang/Throwable.getStackTrace:()[Ljava/lang/StackTraceElement; 
     4: astore_1  
     5: aload_1  
     6: arraylength 
     7: istore_2  
     8: iconst_0  
     9: istore_3  
     10: iload_3  
     11: iload_2  
     12: if_icmpge  36 
     15: getstatic  #3     // Field logger:Ljava/util/logging/Logger; 
     18: getstatic  #4     // Field java/util/logging/Level.WARNING:Ljava/util/logging/Level; 
     21: aload_1  
     22: iload_3  
     23: aaload   
     24: invokevirtual #5     // Method java/lang/StackTraceElement.getMethodName:()Ljava/lang/String; 
     27: invokevirtual #6     // Method java/util/logging/Logger.log:(Ljava/util/logging/Level;Ljava/lang/String;)V 
     30: iinc   3, 1 
     33: goto   10 
     36: return   

    static {}; 
     Code: 
     0: ldc   #7     // String log 
     2: invokestatic #8     // Method java/util/logging/Logger.getLogger:(Ljava/lang/String;)Ljava/util/logging/Logger; 
     5: putstatic  #3     // Field logger:Ljava/util/logging/Logger; 
     8: return   
    } 

로 컴파일 JVM에서 JITted와 거의 동일하게 작동합니다. b()은 약간 다릅니다. 여기서는 arraylength이 루프 내에서 호출됩니다. @thurstycrow 메모는 반드시 성능 문제는 아니지만 그럴 수도 있습니다.

실제 성능과 관련하여 고려해야 할 가치가 있는지 확인해야합니다. 스택 트레이스 을 검사하는 코드는 stacktrace를 반복해서 가져올 것으로 예상되지 않기 때문에 거의 최적화 할 가치가 없습니다.또한 로깅은 배열 길이 검사보다 비용이 많이 든다고 주장합니다.

그러나이 특정 코드를 최적화해야하는 경우를 상상해 보면 던져진 모든 예외를 검사하고 끝내기보다는 너무 느리게하기를 원하는 사용자 지정 모니터링 도구를 작성한다고 가정 해보십시오. 천천히 (스택 트레이스를 수집하는 것은 비용이 많이 들지 않으며 탐색은 일반적으로 비용이 적게 듭니다.) JIT에서 발생할 수있는 모든 것을 고려하여 주위에 주변에이 코드를 포함시켜야하므로 야생에서 측정해야합니다.

하지만 그것은 지루한 대답입니다. 그래서 우리는 그것이별로 의미가 없다는 것을 알고 있더라도 벤치마킹을 해봅니다. 결국, 우리는 할 수 있습니다. 왜 그렇게하지 않습니까? 사용할 수있는 좋은 벤치마킹 도구는 JMH입니다. 자습서를 따라 가서 연주하거나이 해답을 얻을 수 있습니다.

벤치 마크가 될 것

package org.sample; 

import java.util.logging.*; 
import java.util.*; 
import org.openjdk.jmh.annotations.Benchmark; 
import org.openjdk.jmh.annotations.State; 
import org.openjdk.jmh.annotations.Scope; 

@State(Scope.Thread) 
public class ForLoop { 
    private static Logger logger = Logger.getLogger("log"); 
    static { 
     logger.setLevel(Level.OFF); 
    } 

    private StackTraceElement elements[] = new Exception().getStackTrace(); 

    @Benchmark 
    public void a0() { 
     for (int i = 0, n = elements.length; i < n; i++) { 
      logger.log(Level.WARNING, elements[i].getMethodName()); 
     } 
    } 

    @Benchmark 
    public void a() { 
     for (int i = 0, n = elements.length; i < n; i++) { 
      logger.log(Level.WARNING, elements[i].getMethodName()); 
     } 
    } 

    @Benchmark 
    public void b() { 
     for (int i = 0; i < elements.length; i++) { 
      logger.log(Level.WARNING, elements[i].getMethodName()); 
     } 
    } 

    @Benchmark 
    public void c() { 
     int n = elements.length; 
     for (int i = 0; i < n; i++) { 
      logger.log(Level.WARNING, elements[i].getMethodName()); 
     } 
    } 

    @Benchmark 
    public void d() { 
     for (StackTraceElement e: elements) { 
      logger.log(Level.WARNING, e.getMethodName()); 
     } 
    } 

    @Benchmark 
    public void e() { 
     Arrays.stream(elements) 
      .forEach(item -> logger.log(Level.WARNING, item.getMethodName())); 
    } 
} 

(나중에 a0에 대한 자세한;에 대한-각각의 스트림을 포함과 답변). 우리가 그것을 실행하면,

alf-pro:test alf$ java -jar target/benchmarks.jar -wi 5 -i 5 -t 4 -f 1 
# JMH 1.13 (released 41 days ago) 
# VM version: JDK 1.8.0, VM 25.0-b70 
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/bin/java 
# VM options: <none> 
# Warmup: 5 iterations, 1 s each 
# Measurement: 5 iterations, 1 s each 
# Timeout: 10 min per iteration 
# Threads: 4 threads, will synchronize iterations 
# Benchmark mode: Throughput, ops/time 
# Benchmark: org.sample.ForLoop.a 

# Run progress: 0.00% complete, ETA 00:01:00 
# Fork: 1 of 1 
# Warmup Iteration 1: 139092694.098 ops/s 
# Warmup Iteration 2: 130833602.261 ops/s 
# Warmup Iteration 3: 121355640.666 ops/s 
# Warmup Iteration 4: 114037414.954 ops/s 
# Warmup Iteration 5: 110355992.809 ops/s 
Iteration 1: 111331613.966 ops/s 
Iteration 2: 111401698.736 ops/s 
Iteration 3: 108193438.523 ops/s 
Iteration 4: 105277721.260 ops/s 
Iteration 5: 103549199.780 ops/s 


Result "a": 
    107950734.453 ±(99.9%) 13602765.938 ops/s [Average] 
    (min, avg, max) = (103549199.780, 107950734.453, 111401698.736), stdev = 3532595.117 
    CI (99.9%): [94347968.515, 121553500.391] (assumes normal distribution) 


# JMH 1.13 (released 41 days ago) 
# VM version: JDK 1.8.0, VM 25.0-b70 
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/bin/java 
# VM options: <none> 
# Warmup: 5 iterations, 1 s each 
# Measurement: 5 iterations, 1 s each 
# Timeout: 10 min per iteration 
# Threads: 4 threads, will synchronize iterations 
# Benchmark mode: Throughput, ops/time 
# Benchmark: org.sample.ForLoop.a0 

# Run progress: 16.67% complete, ETA 00:00:51 
# Fork: 1 of 1 
# Warmup Iteration 1: 96143259.419 ops/s 
# Warmup Iteration 2: 107561397.775 ops/s 
# Warmup Iteration 3: 97488364.065 ops/s 
# Warmup Iteration 4: 95880266.969 ops/s 
# Warmup Iteration 5: 96943938.140 ops/s 
Iteration 1: 95208831.922 ops/s 
Iteration 2: 94012219.834 ops/s 
Iteration 3: 95369199.325 ops/s 
Iteration 4: 96090174.793 ops/s 
Iteration 5: 90375159.036 ops/s 


Result "a0": 
    94211116.982 ±(99.9%) 8743077.939 ops/s [Average] 
    (min, avg, max) = (90375159.036, 94211116.982, 96090174.793), stdev = 2270549.576 
    CI (99.9%): [85468039.043, 102954194.921] (assumes normal distribution) 


# JMH 1.13 (released 41 days ago) 
# VM version: JDK 1.8.0, VM 25.0-b70 
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/bin/java 
# VM options: <none> 
# Warmup: 5 iterations, 1 s each 
# Measurement: 5 iterations, 1 s each 
# Timeout: 10 min per iteration 
# Threads: 4 threads, will synchronize iterations 
# Benchmark mode: Throughput, ops/time 
# Benchmark: org.sample.ForLoop.b 

# Run progress: 33.33% complete, ETA 00:00:41 
# Fork: 1 of 1 
# Warmup Iteration 1: 58778298.331 ops/s 
# Warmup Iteration 2: 66786552.916 ops/s 
# Warmup Iteration 3: 67834850.418 ops/s 
# Warmup Iteration 4: 69421299.220 ops/s 
# Warmup Iteration 5: 72392517.533 ops/s 
Iteration 1: 70791625.464 ops/s 
Iteration 2: 70393808.080 ops/s 
Iteration 3: 68931230.026 ops/s 
Iteration 4: 67113234.799 ops/s 
Iteration 5: 71628641.383 ops/s 


Result "b": 
    69771707.950 ±(99.9%) 6847578.759 ops/s [Average] 
    (min, avg, max) = (67113234.799, 69771707.950, 71628641.383), stdev = 1778294.458 
    CI (99.9%): [62924129.192, 76619286.709] (assumes normal distribution) 


# JMH 1.13 (released 41 days ago) 
# VM version: JDK 1.8.0, VM 25.0-b70 
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/bin/java 
# VM options: <none> 
# Warmup: 5 iterations, 1 s each 
# Measurement: 5 iterations, 1 s each 
# Timeout: 10 min per iteration 
# Threads: 4 threads, will synchronize iterations 
# Benchmark mode: Throughput, ops/time 
# Benchmark: org.sample.ForLoop.c 

# Run progress: 50.00% complete, ETA 00:00:31 
# Fork: 1 of 1 
# Warmup Iteration 1: 87120233.617 ops/s 
# Warmup Iteration 2: 88506588.802 ops/s 
# Warmup Iteration 3: 82506886.728 ops/s 
# Warmup Iteration 4: 75379852.092 ops/s 
# Warmup Iteration 5: 83735974.951 ops/s 
Iteration 1: 80107472.633 ops/s 
Iteration 2: 85088925.886 ops/s 
Iteration 3: 81051339.754 ops/s 
Iteration 4: 85997882.597 ops/s 
Iteration 5: 87092494.956 ops/s 


Result "c": 
    83867623.165 ±(99.9%) 11946241.585 ops/s [Average] 
    (min, avg, max) = (80107472.633, 83867623.165, 87092494.956), stdev = 3102401.003 
    CI (99.9%): [71921381.580, 95813864.750] (assumes normal distribution) 


# JMH 1.13 (released 41 days ago) 
# VM version: JDK 1.8.0, VM 25.0-b70 
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/bin/java 
# VM options: <none> 
# Warmup: 5 iterations, 1 s each 
# Measurement: 5 iterations, 1 s each 
# Timeout: 10 min per iteration 
# Threads: 4 threads, will synchronize iterations 
# Benchmark mode: Throughput, ops/time 
# Benchmark: org.sample.ForLoop.d 

# Run progress: 66.67% complete, ETA 00:00:20 
# Fork: 1 of 1 
# Warmup Iteration 1: 90916967.864 ops/s 
# Warmup Iteration 2: 91629586.405 ops/s 
# Warmup Iteration 3: 107866944.554 ops/s 
# Warmup Iteration 4: 102023453.435 ops/s 
# Warmup Iteration 5: 111336773.130 ops/s 
Iteration 1: 107770637.269 ops/s 
Iteration 2: 107103283.175 ops/s 
Iteration 3: 107545862.850 ops/s 
Iteration 4: 113804266.277 ops/s 
Iteration 5: 114628060.965 ops/s 


Result "d": 
    110170422.107 ±(99.9%) 14295432.924 ops/s [Average] 
    (min, avg, max) = (107103283.175, 110170422.107, 114628060.965), stdev = 3712478.533 
    CI (99.9%): [95874989.183, 124465855.031] (assumes normal distribution) 


# JMH 1.13 (released 41 days ago) 
# VM version: JDK 1.8.0, VM 25.0-b70 
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/bin/java 
# VM options: <none> 
# Warmup: 5 iterations, 1 s each 
# Measurement: 5 iterations, 1 s each 
# Timeout: 10 min per iteration 
# Threads: 4 threads, will synchronize iterations 
# Benchmark mode: Throughput, ops/time 
# Benchmark: org.sample.ForLoop.e 

# Run progress: 83.33% complete, ETA 00:00:10 
# Fork: 1 of 1 
# Warmup Iteration 1: 33385239.816 ops/s 
# Warmup Iteration 2: 38698292.771 ops/s 
# Warmup Iteration 3: 38876327.513 ops/s 
# Warmup Iteration 4: 37957299.742 ops/s 
# Warmup Iteration 5: 39190117.656 ops/s 
Iteration 1: 38286235.065 ops/s 
Iteration 2: 39449652.416 ops/s 
Iteration 3: 38541883.894 ops/s 
Iteration 4: 40384962.473 ops/s 
Iteration 5: 36975457.540 ops/s 


Result "e": 
    38727638.278 ±(99.9%) 4934050.936 ops/s [Average] 
    (min, avg, max) = (36975457.540, 38727638.278, 40384962.473), stdev = 1281357.359 
    CI (99.9%): [33793587.342, 43661689.213] (assumes normal distribution) 


# Run complete. Total time: 00:01:02 

Benchmark Mode Cnt   Score   Error Units 
ForLoop.a thrpt 5 107950734.453 ± 13602765.938 ops/s 
ForLoop.a0 thrpt 5 94211116.982 ± 8743077.939 ops/s 
ForLoop.b thrpt 5 69771707.950 ± 6847578.759 ops/s 
ForLoop.c thrpt 5 83867623.165 ± 11946241.585 ops/s 
ForLoop.d thrpt 5 110170422.107 ± 14295432.924 ops/s 
ForLoop.e thrpt 5 38727638.278 ± 4934050.936 ops/s 

우리는 세 가지 방법은 말 그대로 매우 다른 측정에서 같은 방법으로 결과가 때 몇 번 실행하는 것이 너무 빨리 것을 볼 수 있습니다. 그래서이 예제에서는 여기서 멈출 것입니다. 실제 사례에 대해서는 문제를 일으키는 실제 코드로 자유롭게 선택하십시오.

PS. for-each 및 스트리밍 솔루션이 추가되었습니다. 보시다시피 for each는 거의 동일합니다 (더 읽기 쉽습니다). 스트림이 눈에 띄게 느립니다. 나는이 두 가지 해결책을 위해 독자를위한 연습으로 javap examination을 남겨두고 있습니다.

+0

질문에 대한 설명에 감사드립니다. 고맙습니다. –

3

아니요, 정확히 같습니다. 변수에 element.length을 저장하고 대신 그 변수를 사용하십시오. 그들은 같은 시간에 같은 일을합니다. 마지막 예는 가장 이해하기 쉬운 imho이기 때문에 가장 좋습니다.

+2

추가 정보 : Java에서는 배열의 길이를'n '에 할당하여 성능을 향상시키지 않습니다. Javascript를 코딩하는 경우에는'element.length'가 매번 다시 계산되므로,'n'에 길이를 할당하는 접근법이 권장됩니다. 그러나 Java의 경우에는 필요하지 않습니다. –

1

나는 이런 식으로 할 것 : 단순히 코드에 다음과 같이 당신은 또한 쓸 수

for(StackTraceElement element : elements) { 
    logger.log(Level.WARNING, element.getMethodName()); 
} 
1

.

StackTraceElement elements[] = e.getStackTrace(); 

for(StackTraceElement element : elements) { 
    logger.log(Level.WARNING, element.getMethodName()); 
} 
+1

내부적으로 'n'으로 무언가를 최적화해야합니다. –

1

첫 번째 버전과의 차이점은 매번 elements의 코드를 역 참조해야한다는 것입니다. 이론적으로 변수에 값을 저장하는 것이 더 효율적이어야하지만이 작업은 너무 싸기 때문에 실제로는 차이점을 거의 느끼지 못할 것입니다.

두 번째 버전과 다른 점은 주로 for 루프의 범위를 벗어나는 변수 n을 소개한다는 것입니다. 즉, n을 아래의 변수 이름으로 다시 사용하려는 경우 동일한 유형 (이 경우 int) 인 경우에만 수행 할 수 있습니다. 이 루프에서

0

: 당신이 요소의 길이 variabile에 액세스하는 각각의 반복에 대한

for (int i = 0; i < elements.length; i++) { 
    logger.log(Level.WARNING, elements[i].getMethodName()); 
} 

. 다른 루프에서

는 :

int n = elements.length; 
for (int i = 0; i < n; i++) { 
    logger.log(Level.WARNING, elements[i].getMethodName()); 
} 

당신은 n 변수의 길이를 저장할 수 있지만 각 반복에 대해 당신은 n 변수에 액세스 할 수 있습니다.

동일합니다.

당신은 쓸 수 있습니다 :

StackTraceElement elements[] = e.getStackTrace(); 
for(StackTraceElement element : elements) { 
    logger.log(Level.WARNING, element.getMethodName()); 
} 

보다 읽기 쉬운 해결책은 예를 들어 ArrayList를 사용할 :

arrayName.stream().forEach(item -> System.out.println(item.toString())); 

이 솔루션은 자바 (8)을 필요로한다.

for (int i = 0; i < elements.length; i++) :

+0

마지막 솔루션이 작동하지 않는 것 같습니다. 혹시'어레이. 스트림'이라는 뜻 이었습니까? – alf

+0

마지막 솔루션은 arrayList 객체를위한 것입니다. – pioardi

1

사실, 두 가지 옵션 사이에 약간의 차이가 여기에 당신은 elements.length에게 n 시간을 다시 계산합니다. 이제 대부분의 경우 컬렉션의 크기는 캐시되지만 모든 메서드 호출에서 크기가 다시 계산되어 시간 복잡성이 증가하는 경우가 있습니다.

final int n = elements.length; 
for (int i = 0; i < n; i++) 

여기에서 길이를 한 번 계산하면됩니다. 또한 누군가에게 변경을 원하지 않기 때문에 최종 결정을 내리는 것이 중요합니다.

는하지만, 가장 좋은 방법은이 같은 for each 루프를 사용하는 것입니다

for (StackTraceElement element : e.getStackTrace())을 -이, 카운터의 사용을 제거 더 나은 따라서 더 읽기보고 약간의 성능 향상이있을 수 있습니다.

1

차이점은 없습니다.

당신은 단지 여분의 변수를 생성하고 사용하는 1

경우 #.

경우 # 2

마다 루프 elements.length 방법이 계산 실행한다.

사례 # 3

이 경우와 같은 좋은 1. 여분 배열 경계 검사를 수행해야 때문에, 변수에 배열의 길이를 저장하지 않도록 더 나은 사실

+0

'elements.length'는 최종 값이므로 두 번 이상 "계산"되지 않습니다. – Kayaman

2

반복마다. this video (28:00부터 시작)을 살펴보십시오. JIT 컴파일러가 코드를 최적화하기 위해 어떤 노력을했는지 자세히 설명합니다.

관련 문제