我试图测量执行此循环的时间:
Get-Content
结果不一致。最终,我设法通过以下代码获得了一致的结果:
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)
为什么呢?为什么这样的重新分配导致它之后的那些循环花费两倍的时间? 正确分析是很难的......
答案 0 :(得分:3)
我会将此添加为评论,但我的声誉太低,因此必须将其添加为答案。
我已经用您的确切代码创建了一个jar,并运行了几次。我还将代码复制到C#并在.NET运行时运行它。
Java和C#都显示相同的确切时间,无论是否重新分配&#39;环。
如果将循环更改为
,您将获得什么时间 if ( "reassign".equals(args[0])) {
for (int n = 0; n < 5_000_000; ++n)
test[n] = test[n];
}
答案 1 :(得分:3)
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中都会发生这种情况。
答案 2 :(得分:3)
“至于JIT编译器导致这种行为的原因......这超出了我的技能和知识。”
三个基本事实:
JIT编译后代码运行得更快。
在一大块代码运行一段时间后触发JIT编译。 (“有点”多长时间会影响JVM平台和命令行选项。)
JIT编译需要时间。
在您的情况下,当您在测试1和测试2之间插入大分配循环时,您很可能会移动触发JIT编译的时间点...从测试2到2个测试之间。
在这种情况下解决这个问题的简单方法是将main
的主体放入循环并重复运行。然后在前几次运行中丢弃异常结果。
(关闭JIT编译不是一个好的答案。通常,JIT编译之后的代码的性能特征将指示真实应用程序的执行方式......)< / p>
通过将编译器设置为NONE,您将禁用JIT编译,将其从等式中删除。
当人们试图手工编写微基准时,这种异常很常见。阅读此Q&amp; A: