【发布时间】:2015-07-21 20:17:13
【问题描述】:
我试图重现here 中描述的一些处理器缓存效果。我知道 Java 是一个托管环境,这些示例无法准确翻译,但我遇到了一个奇怪的案例,我试图提炼出一个简单的示例来说明效果:
public static void main(String[] args) {
final int runs = 10;
final int steps = 1024 * 1024 * 1024;
for (int run = 0; run < runs; run++) {
final int[] a = new int[1];
long start = System.nanoTime();
for (int i = 0; i < steps; i++) {
a[0]++;
}
long stop = System.nanoTime();
long time = TimeUnit.MILLISECONDS.convert(stop - start, TimeUnit.NANOSECONDS);
System.out.printf("Time for loop# %2d: %5d ms\n", run, time);
}
}
输出:
Time for loop# 0: 24 ms
Time for loop# 1: 106 ms
Time for loop# 2: 104 ms
Time for loop# 3: 103 ms
Time for loop# 4: 102 ms
Time for loop# 5: 103 ms
Time for loop# 6: 104 ms
Time for loop# 7: 102 ms
Time for loop# 8: 105 ms
Time for loop# 9: 102 ms
内部循环的第一次迭代大约是后续迭代的 4 倍。这与我通常期望的相反,因为通常性能会随着 JIT 的启动而提高。
当然,在任何严重的微基准测试中都会做几个热身循环,但我很好奇是什么导致了这种行为,特别是因为如果我们知道循环可以在 24 毫秒内执行,它不是非常满意,稳态时间超过100ms。
供参考我正在使用的JDK(在linux上):
openjdk version "1.8.0_40"
OpenJDK Runtime Environment (build 1.8.0_40-b20)
OpenJDK 64-Bit Server VM (build 25.40-b23, mixed mode)
更新:
以下是一些更新信息,基于一些 cmets 和一些实验:
1) 将 System.out I/O 移出循环(通过将时间存储在大小为“runs”的数组中)在时间上没有显着差异。
2) 上面显示的输出是当我在 Eclipse 中运行时。当我从命令行编译和运行(使用相同的 JDK/JVM)时,我得到的结果更温和,但仍然很重要(快 2 倍而不是 4 倍)。这看起来很有趣,因为通常在 Eclipse 中运行会减慢速度。
3) 将a 上移,移出循环,这样每次迭代都重用它没有效果。
4) 如果将int[] a 更改为long[] a,则第一次迭代运行得更快(大约 20%),而其他迭代仍保持相同(更慢)的速度。
更新 2:
我认为 apangin 的回答解释了这一点。我在 Sun 的 1.9 JVM 上试过这个,它来自:
openjdk version "1.8.0_40"
OpenJDK Runtime Environment (build 1.8.0_40-b20)
OpenJDK 64-Bit Server VM (build 25.40-b23, mixed mode)
Time for loop# 0: 48 ms
Time for loop# 1: 116 ms
Time for loop# 2: 112 ms
Time for loop# 3: 113 ms
Time for loop# 4: 112 ms
Time for loop# 5: 112 ms
Time for loop# 6: 111 ms
Time for loop# 7: 111 ms
Time for loop# 8: 113 ms
Time for loop# 9: 113 ms
到:
java version "1.9.0-ea"
Java(TM) SE Runtime Environment (build 1.9.0-ea-b73)
Java HotSpot(TM) 64-Bit Server VM (build 1.9.0-ea-b73, mixed mode)
Time for loop# 0: 48 ms
Time for loop# 1: 26 ms
Time for loop# 2: 22 ms
Time for loop# 3: 22 ms
Time for loop# 4: 22 ms
Time for loop# 5: 22 ms
Time for loop# 6: 22 ms
Time for loop# 7: 22 ms
Time for loop# 8: 22 ms
Time for loop# 9: 23 ms
这是相当的进步!
【问题讨论】:
-
不是 ms - bs,字节秒:P
-
我可以在我的 MacBook 上运行时报告稍微不那么显着 (2.2x) 但类似的结果:
java version "1.8.0_40" Java(TM) SE Runtime Environment (build 1.8.0_40-b27) Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode) -
哈,是的,我猜纳秒应该是 1000。真的应该是 time = TimeUnit.MILLISECONDS.convert(stop - start, TimeUnit.NANOSECONDS);
-
我可以不确认在 Windows 7 运行下的这种行为:
java version "1.7.0_45" Java(TM) SE Runtime Environment (build 1.7.0_45-b18) Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)。时间在大约 60 毫秒左右相当稳定。 -
我无法不在我的 MBP Retina 上确认此行为:
java version "1.8.0_25" Java(TM) SE Runtime Environment (build 1.8.0_25-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
标签: java performance jit