Java 为什么要在这个相同的代码中减速?
我试图测量执行此循环的时间:Java 为什么要在这个相同的代码中减速?,java,Java,我试图测量执行此循环的时间: 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.
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);
}
}
区别在于在第二次测试之前是否执行此循环
for (int n = 0; n < 10_000_000; ++n)
test[n] = test[n];
for(int n=0;n<10\u 000\u 000;++n)
试验[n]=试验[n];
为什么??为什么重新分配会导致之后的循环花费两倍的时间?正确地分析是很困难的…我想把它作为一个评论,但我的声誉太低了,所以必须把它作为一个答案添加进去 我用你的代码创建了一个jar,并运行了好几次。我还将代码复制到C#,并在.NET运行时运行它 Java和C#都显示相同的精确时间,有无“重新分配”循环 如果将循环更改为
if ( "reassign".equals(args[0])) {
for (int n = 0; n < 5_000_000; ++n)
test[n] = test[n];
}
if(“重新分配”.equals(args[0])){
对于(int n=0;n<5_000_000;++n)
试验[n]=试验[n];
}
?Marko Topolniks和rossum的评论让我走上了正确的方向。
这是一个JIT编译器问题。
如果禁用JIT编译器,则会得到以下结果:
$ java -jar Test.jar -Djava.compiler=NONE noreassign
1st test : 19.23
2nd test : 19.33
$ java -jar Test.jar -Djava.compiler=NONE reassign
1st test : 19.23
2nd test : 19.32
一旦JIT编译器停用,奇怪的减速就会消失。至于为什么JIT编译器会导致这种行为。。。这超出了我的技能和知识范围。
但这并不像Marius Dornean的测试所示,在所有JVM中都会发生 “至于为什么JIT编译器会导致这种行为……这超出了我的技能和知识。” 三个基本事实:
main
的主体放入一个循环中,并重复运行它。然后丢弃前几次运行中的异常结果
(关闭JIT编译不是一个好的答案。通常,JIT编译后代码的性能特征将指示实际应用程序的执行方式…)
通过将编译器设置为“无”,可以禁用JIT编译,从而将其从等式中去掉
当人们试图手工编写微基准测试时,这种异常现象很常见。阅读以下问答:
$ java -jar Test.jar -Djava.compiler=NONE noreassign
1st test : 19.23
2nd test : 19.33
$ java -jar Test.jar -Djava.compiler=NONE reassign
1st test : 19.23
2nd test : 19.32