我注意到了System.nanoTime()的模式。每当我开始迭代时,nanoTime()在几圈内变得非常不正确,直到它最终稳定下来。
例如,如果我运行以下代码:
public class TimeTest{
public static void main(String[] args) {
long prev = System.nanoTime();
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 1000000; j++);
long time = System.nanoTime();
System.out.println(time - prev);
prev = time;
}
}
}
我得到以下结果:
为了消除System.out.println(String)弄乱结果的可能性,我还可以运行以下测试:
public class TimeTest{
public static void main(String[] args) {
long[] difs = new long[10];
long prev = System.nanoTime();
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 1000000; j++);
long time = System.nanoTime();
difs[i] = (time - prev);
prev = time;
}
for(long l : difs)
System.out.println(l);
}
}
这给出了以下结果:
初始延迟可能可以通过假设迭代的开始(在这种情况下为for循环)在循环开始之前花费一些额外的时间来初始化来解释。然而,由于第二圈据说也需要很长时间才能执行,我们可以相信它可能不会是for循环。
所以我的问题很简单,在使用System.nanoTime()和迭代时导致这种初始延迟的原因是什么?
注意:我也尝试过不同类型的迭代器,但问题仍然存在。
答案 0 :(得分:4)
我看起来像JIT warm up time of the JVM
当Java HotSpot编译器看到你的“热点”时会启动它 码。因此,您的代码运行速度更快是很常见的 时间!所以,你应该调整你的测试方法。
HotSpot编译器在后台编译,吞噬了CPU 周期。因此,当编译器忙时,您的程序是暂时的 慢点。但在编制了一些热点后,你的程序会突然出现 跑得快!
答案 1 :(得分:2)
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 1000000; j++);
long time = System.nanoTime();
System.out.println(time - prev);
prev = time;
}
我注意到你可能忽略的两个主要现象:
println
时,可能会进行一些初始化工作; j
)将完全优化 。 在稳定状态下,每圈剩下约30微秒,这大约是一个println
完成工作所需的时间。
答案 2 :(得分:1)
正如@BrianAgnew已经提到的那样,它是由JVM的及时热身引起的。
您可以使用-XX:CompileThresold=1
解决此问题,以强制java在第一次运行时编译所有内容。它会减慢程序的启动速度,但System.nanotime()
可能会正常工作。
什么是及时编译?
JIT编译是两种传统方法的结合 转换为机器代码 - 提前编译(AOT),以及 解释 - 并结合了一些优点和缺点 两者。[1]粗略地说,JIT编译结合了编译代码的速度 具有解释的灵活性,具有开销 解释器和编译的额外开销(不仅仅是 解释)。 JIT编译是动态编译的一种形式,并且 允许自适应优化,例如动态重新编译 - 因此 原则JIT编译可以比静态编译更快 汇编。解释和JIT编译尤其如此 适合动态编程语言,因为运行时系统可以 处理后期数据类型并强制执行安全保障。
答案 3 :(得分:0)
这是由于JIT,如果你为main
停用它,那么措施是一致的。
java -XX:CompileCommand=exclude,TimeTest,main TimeTest
给了我
CompilerOracle: exclude TimeTest.main
### Excluding compile: static TimeTest::main
8886537
9020980
8841953
8817411
8948350
8942021
8728320
8598453
8585024
8627902
请注意,这样做的影响是将计算速度降低至少100倍! JIT是一种非常有效的技术......