【问题标题】:Why is System.nanoTime() incorrect in the beginning of iterations?为什么 System.nanoTime() 在迭代开始时不正确?
【发布时间】:2015-02-17 13:41:54
【问题描述】:

我注意到 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-loop。

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

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

【问题讨论】:

  • 不要责怪nanoTime()。时间是真实的。
  • 您使用什么 Java 版本的 JDK 或 JRE 进行测试?对于所有循环,我得到几乎相同的结果。 (JDK 1.7,Win 7 64 位)
  • JDK 8.0、JRE 1.7.0、OS X Mavericks 32 位

标签: java loops time


【解决方案1】:

在我看来就像JIT warm up time of the JVM

当 Java HotSpot 编译器在您的 代码。因此,您的代码运行得更快是很常见的 时间!所以,你应该调整你的测试方法。

HotSpot 编译器在后台编译,消耗 CPU 循环。所以当编译器忙的时候,你的程序暂时 慢点。但是编译了一些热点之后,你的程序会突然 跑得更快!

【讨论】:

  • 在运行单线程客户端代码的多核处理器上,JIT 编译器不会从主线程中窃取任何时间。
【解决方案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)将被优化掉整个

在稳定状态下,您每圈只剩下大约 30 微秒的时间,这大约是 println 完成工作所需的时间。

【讨论】:

    【解决方案3】:

    正如@BrianAgnew 已经提到的,它是由 JVM 的及时预热引起的。

    您可以使用-XX:CompileThresold=1 来强制java 在第一次运行时编译所有内容来解决这个问题。它会减慢程序的启动速度,但System.nanotime() 可能会正常工作。


    什么是即时编译?

    JIT 编译是两种传统方法的组合 翻译成机器代码——提前编译(AOT),以及 解释 - 并结合了一些优点和缺点 两者。[1]粗略地说,JIT 编译结合了编译代码的速度 具有解释的灵活性,具有 解释器和额外的编译开销(不仅仅是 口译)。 JIT编译是动态编译的一种形式, 允许自适应优化,例如动态重新编译——因此在 原则 JIT 编译可以产生比静态更快的执行 汇编。解释和 JIT 编译尤其重要 适用于动态编程语言,因为运行时系统可以 处理后期绑定的数据类型并执行安全保证。

    【讨论】:

      【解决方案4】:

      这是由于 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 是一种非常高效的技术...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-15
        • 2011-02-05
        • 1970-01-01
        • 2014-03-16
        • 1970-01-01
        相关资源
        最近更新 更多