为什么 System.nanoTime() 在迭代开始时不正确?
Why is System.nanoTime() incorrect in the beginning of iterations?
我注意到 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() 与迭代一起使用时的初始延迟?
注意:我也尝试了不同类型的迭代器,但问题仍然存在。
我觉得像 JIT warm up time of the JVM
The Java HotSpot compiler kicks in when it sees a ‘hot spot’ in your
code. It is therefore quite common that your code will run faster over
time! So, you should adapt your testing methods.
The HotSpot compiler compiles in the background, eating away CPU
cycles. So when the compiler is busy, your program is temporarily
slower. But after compiling some hot spots, your program will suddenly
run faster!
正如@BrianAgnew 已经提到的,这是由 JVM 的及时预热引起的。
您可以使用 -XX:CompileThresold=1
强制 java 在第一次运行时编译所有内容来解决此问题。它会减慢程序的启动速度,但 System.nanotime()
可能会正常工作。
什么是即时编译?
JIT compilation is a combination of the two traditional approaches to
translation to machine code – ahead-of-time compilation (AOT), and
interpretation – and combines some advantages and drawbacks of
both.[1] Roughly, JIT compilation combines the speed of compiled code
with the flexibility of interpretation, with the overhead of an
interpreter and the additional overhead of compiling (not just
interpreting). JIT compilation is a form of dynamic compilation, and
allows adaptive optimization such as dynamic recompilation – thus in
principle JIT compilation can yield faster execution than static
compilation. Interpretation and JIT compilation are particularly
suited for dynamic programming languages, as the runtime system can
handle late-bound data types and enforce security guarantees.
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
)将在代码被 JIT 编译后 整体被优化掉 。
在稳定状态下,你每圈剩下大约 30 微秒,这大约是 println
完成工作所需的时间。
这是由于 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 是一种非常高效的技术...
我注意到 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() 与迭代一起使用时的初始延迟?
注意:我也尝试了不同类型的迭代器,但问题仍然存在。
我觉得像 JIT warm up time of the JVM
The Java HotSpot compiler kicks in when it sees a ‘hot spot’ in your code. It is therefore quite common that your code will run faster over time! So, you should adapt your testing methods.
The HotSpot compiler compiles in the background, eating away CPU cycles. So when the compiler is busy, your program is temporarily slower. But after compiling some hot spots, your program will suddenly run faster!
正如@BrianAgnew 已经提到的,这是由 JVM 的及时预热引起的。
您可以使用 -XX:CompileThresold=1
强制 java 在第一次运行时编译所有内容来解决此问题。它会减慢程序的启动速度,但 System.nanotime()
可能会正常工作。
什么是即时编译?
JIT compilation is a combination of the two traditional approaches to translation to machine code – ahead-of-time compilation (AOT), and interpretation – and combines some advantages and drawbacks of both.[1] Roughly, JIT compilation combines the speed of compiled code with the flexibility of interpretation, with the overhead of an interpreter and the additional overhead of compiling (not just interpreting). JIT compilation is a form of dynamic compilation, and allows adaptive optimization such as dynamic recompilation – thus in principle JIT compilation can yield faster execution than static compilation. Interpretation and JIT compilation are particularly suited for dynamic programming languages, as the runtime system can handle late-bound data types and enforce security guarantees.
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
)将在代码被 JIT 编译后 整体被优化掉 。
在稳定状态下,你每圈剩下大约 30 微秒,这大约是 println
完成工作所需的时间。
这是由于 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 是一种非常高效的技术...