为什么System.nanoTime()在迭代开始时不正确?

时间:2015-02-17 13:41:54

标签: java loops time

我注意到了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;
        }
    }
}

我得到以下结果:

Test result 1

为了消除Sys​​tem.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);
    }
}

这给出了以下结果:

Test result 2

初始延迟可能可以通过假设迭代的开始(在这种情况下为for循环)在循环开始之前花费一些额外的时间来初始化来解释。然而,由于第二圈据说也需要很长时间才能执行,我们可以相信它可能不会是for循环。

所以我的问题很简单,在使用System.nanoTime()和迭代时导致这种初始延迟的原因是什么?

注意:我也尝试过不同类型的迭代器,但问题仍然存在。

4 个答案:

答案 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;
}

我注意到你可能忽略的两个主要现象:

  1. 第一次调用println时,可能会进行一些初始化工作;
  2. 一旦代码被JIT编译,你的内部循环(超过j)将完全优化
  3. 在稳定状态下,每圈剩下约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是一种非常有效的技术......