我正在尝试测量执行此循环的时间:
for (boolean t : test) {
if (!t)
++count;
}
并且得到不一致的结果。最终我设法通过以下代码获得了一致的结果:
public class Test {
public static void main(String[] args) {
int size = 100;
boolean[] test = new boolean[10_000_000];
java.util.Random r = new java.util.Random();
for (int n = 0; n < 10_000_000; ++n)
test[n] = !r.nextBoolean();
int expected = 0;
long acumulated = 0;
for (int repeat = -1; repeat < size; ++repeat) {
int count = 0;
long start = System.currentTimeMillis();
for (boolean t : test) {
if (!t)
++count;
}
long end = System.currentTimeMillis();
if (repeat != -1) // First run does not count, VM warming up
acumulated += end - start;
else // Use count to avoid compiler or JVM
expected = count; //optimization of inner loop
if ( count!=expected )
throw new Error("Tests don't run same ammount of times");
}
float average = (float) acumulated / size;
System.out.println("1st test : " + average);
int expectedBis = 0;
acumulated = 0;
if ( "reassign".equals(args[0])) {
for (int n = 0; n < 10_000_000; ++n)
test[n] = test[n];
}
for (int repeat = -1; repeat < size; ++repeat) {
int count = 0;
long start = System.currentTimeMillis();
for (boolean t : test) {
if (!t)
++count;
}
long end = System.currentTimeMillis();
if (repeat != -1) // First run does not count, VM warming up
acumulated += end - start;
else // Use count to avoid compiler or JVM
expectedBis = count; //optimization of inner loop
if ( count!=expected || count!=expectedBis)
throw new Error("Tests don't run same ammount of times");
}
average = (float) acumulated / size;
System.out.println("2nd test : " + average);
}
}
我得到的结果是:
$ java -jar Test.jar noreassign
1st test : 23.98
2nd test : 23.97
$ java -jar Test.jar reassign
1st test : 23.98
2nd test : 40.86
$ java -version
java version "1.7.0_79"
OpenJDK Runtime Environment (IcedTea 2.5.5) (Gentoo package icedtea-7.2.5.5)
OpenJDK 64-Bit Server VM (build 24.79-b02, mixed mode)
区别在于在第二次测试之前是否执行此循环。
for (int n = 0; n < 10_000_000; ++n)
test[n] = test[n];
为什么?为什么重新分配会导致这些循环花费两倍的时间?
正确进行分析很难...
最佳答案
"As for why the JIT compiler causes such behaviour... that is beyond my skill and knowledge."
三个基本事实:
JIT 编译后代码运行速度更快。
JIT 编译在一段代码运行一段时间后触发。 (“一点”的长度受 JVM 平台和命令行选项的影响。)
JIT 编译需要时间。
在您的情况下,当您在测试 1 和测试 2 之间插入大分配循环时,您很可能将触发 JIT 编译的时间点......从测试 2 期间移动到 2 测试之间。
在这种情况下解决这个问题的简单方法是将 main
的主体放入一个循环中并重复运行它。然后丢弃前几次运行中的异常结果。
(关闭 JIT 编译不是一个好的答案。通常,JIT 编译后代码的性能特征将指示真实应用程序的性能...)
通过将编译器设置为 NONE,您将禁用 JIT 编译,将其排除在外。
当人们尝试手动编写微基准时,这种异常情况很常见。阅读此问答:
关于java - 为什么这个相同的代码会变慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32319932/