这是一个部分答案,未来的调查是必要的。但是,就目前而言,答案是 - 这是 JIT 优化的效果。另外,请注意,微基准测试并不是性能测试的最佳选择,尤其是对于像 Java 这样的动态编译语言(例如,请参阅this guru paper)。
我正在使用 Windows 10 家庭版,java -version 打印:
java版本“1.8.0_121”
Java(TM) SE 运行时环境(内部版本 1.8.0_121-b13)
Java HotSpot(TM) 64 位服务器 VM(内部版本 25.121-b13,混合模式)
我已对您的代码进行了如下重组,并添加了 x 作为外部计数器,以确保不会优化循环:
void test1() {
int x = 0;
long b = System.currentTimeMillis();
for (int i = 0; i < 100_001; i++) {
int t = 0;
while (i != t++) {
x++;
}
}
long b1 = System.currentTimeMillis();
System.out.println("T(test1) = " + (b1 - b));
System.out.println("x(test1) = " + x);
}
void test2() {
int x=0;
long b = System.currentTimeMillis();
for (int i = 0; i < 100001; i++) {
int t = 0;
while (t++ != i) {
x++;
}
}
long b1 = System.currentTimeMillis();
System.out.println("T(test2) = " + (b1 - b));
System.out.println("x(test2) = " + x);
}
每个函数被调用两次:
t.test1();
t.test1();
t.test2();
t.test2();
好的,让我们看看标准java Test 调用的结果(没有提供其他解释器参数):
T(test1) = 3745
x(test1) = 705082704
T(test1) = 0
x(test1) = 705082704
T(test2) = 5
x(test2) = 705082704
T(test2) = 0
x(test2) = 705082704
如您所见,在第二次调用之后,两种情况下的运行时间都等于 0。即使我们将 int x = 0 初始化更改为 int x = new Random().nextInt() 以确保不会缓存第二次调用结果或其他什么,也会发生同样的情况。通常,在进行测量之前应该“预热” Java 解释器,即在同一次运行中测量代码性能两次并丢弃第一个结果,以确保优化到位。但这是您在解决在线评委练习时所没有的奢侈。
现在来说另一部分。 Oracle 的 JDK 有一个 -Xint 解释器开关,它完全关闭 JIT。让我们使用它,看看会发生什么。我也使用了-XX:+PrintCompilation 标志来确保没有发生任何编译(即解释器被调用为java -XX:+PrintCompilation -Xint Test;如果没有打印额外的诊断信息,则意味着代码没有被编译):
T(test1) = 56610
x(test1) = 705082704
T(test1) = 55635
x(test1) = 705082704
T(test2) = 60247
x(test2) = 705082704
T(test2) = 58249
x(test2) = 705082704
两个观察结果:现在任务需要很长时间,并且所有调用的结果都相似。需要做更多的调查来发现为什么这两个代码被 JIT 优化的不同。
编辑:JIT 第 2 部分的乐趣
所以,我继续尝试不同的编译选项。一般来说,JIT 使用两种类型的编译器。 C1(客户端)编译器旨在提供更快的 JVM 启动时间,但不如 C2(服务器)编译器快。我使用的 64 位 Java 8 JVM 似乎使服务器成为唯一易于使用的选项(请参阅 this FAQ;但是仍然可以使用 -XX:TieredStopAtLevel= 标志选择不同的编译级别;为简洁起见,我不会粘贴我得到的结果使用它,但他们支持这样的论点,即服务器编译器版本使test2 的第一次调用更快)。
我的机器上也恰好有 32 位 JRE。它不支持服务器编译器并提供以下版本信息:
java版本“1.8.0_121”
Java(TM) SE 运行时环境(内部版本 1.8.0_121-b13)
Java HotSpot(TM) 客户端虚拟机(build 25.121-b13,混合模式)
这个JVM的结果如下:
T(test1) = 3947
x(test1) = 705082704
T(test1) = 3985
x(test1) = 705082704
T(test2) = 4031
x(test2) = 705082704
T(test2) = 4172
x(test2) = 705082704