gpt4 book ai didi

java - Java中的for循环优化

转载 作者:行者123 更新时间:2023-12-04 19:31:34 24 4
gpt4 key购买 nike

我今天在 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 循环?

最佳答案

优化的黄金法则是,不要。

第二条黄金法则是,还没有。

第三条黄金法则实际上是一个问题,你测量了吗?

回答您的问题时,请遵循您团队中公认的标准:您的代码不应该有任何不同,除非它做了一些真正不同的事情。也就是说,在一个好的团队中,代码都是相似且乏味的。任何一步都是一个巨大的闪光标志,上面写着:“小心,这是规则的异常(exception)!” - 除非您想确切地说,否则请使用其他人使用的任何东西。

现在,在代码方面,答案在编译器中:

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());
}
}
}

编译成

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
}

如您所见,a()c() 仅在variable assignment 处不同。 ,因此在 JVM 中的行为几乎相同,无论是否采用 JIT。 b() 略有不同:这里,arraylength 在循环中被调用。正如@thurstycrow 指出的那样,这不一定是性能问题,但也可能是。

因此,当涉及到实际性能时,您需要确定它是否值得考虑。检查堆栈跟踪的代码几乎永远不值得优化,因为您不希望一遍又一遍地获取堆栈跟踪。此外,我认为日志记录比数组长度检查要昂贵得多。

但是如果我们假设您遇到需要优化这段特定代码的情况,假设您正在编写一个自定义监控工具来检查所有抛出的异常,并且您希望它非常慢而不是非常慢(你看,收集堆栈跟踪是昂贵的;导航它通常非常便宜),你需要在野外测量它,因为 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;包含 for-each 和流的答案)。当我们运行它时,

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将这两个解决方案作为读者的练习。

关于java - Java中的for循环优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39264844/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com