【问题标题】:Why is there such a big difference on time between first nanoTime() call and the successive calls?为什么第一次 nanoTime() 调用和后续调用之间的时间差异如此之大?
【发布时间】:2015-10-29 18:02:23
【问题描述】:

所以我的问题更笼统。我有以下简单的代码:

for(int i=0;i<10;i++){
    long starttime=System.nanoTime();
    System.out.println("test");
    long runtime=System.nanoTime()-starttime;
    System.out.println(i + ":" +"runtime="+runtime);
}

我收到以下输出:

test
0:runtime=153956
test
1:runtime=15396
test
2:runtime=22860
test
3:runtime=11197
test
4:runtime=11197
test
5:runtime=12129
test
6:runtime=11663
test
7:runtime=11664
test
8:runtime=53185
test
9:runtime=12130

第一个和第二个运行时出现差异的原因是什么?提前感谢=)

【问题讨论】:

  • stackoverflow.com/questions/860231/… 参考这个问题,它可能会有所帮助。
  • 优化/预测可能吗? + 首次静态“测试”初始化
  • JVM 使用 JIT 编译器将 jvm 字节码编译成真正的机器码。编译时间包含在第一个区间内。

标签: java system nanotime


【解决方案1】:

JVM 和标准库中的很多东西都是延迟初始化的,以改善 JVM 启动时间。所以第一次执行该行时

System.out.println("test");

一个重量级的初始化过程发生。完成它的时间包含在您的第一次测量中。后续调用会沿着状态已初始化的快速路径进行。

您可以在 Java 中的大量 API 调用上观察到相同的效果。

当然,还有很多因素会影响完成任何给定方法调用所需的时间,尤其是当它在其路径中包含系统调用时。但是,first 调用延迟的异常值是特殊的,因为它具有确定性的原因,因此可以可靠地重现。

【讨论】:

    【解决方案2】:

    很多事情都会影响你的计算。

    您机器上的其他进程呢?您是否考虑过 JVM 预热?也许是垃圾收集?所有这些因素以及更多因素都会导致这种行为。

    如果你想获得“更好”的结果,你应该运行更多次,然后取平均值。

    这就是为什么您应该知道如何在 Java 中进行基准测试,请参阅 How do I write a correct micro-benchmark in Java?

    【讨论】:

    • @MarkoTopolnik - 我不仅应该在第一次通话中考虑这个问题,而且应该在第 9 次通话中考虑这个问题。而且,我相信这是唯一正确描述所有可能发生的事情的答案。我不会只考虑第一种情况。你应该对所有的观察给出答案。
    • If you want to get "better" results you should run it for much more times, and take the average.---你的意思是,多次运行同一个程序? OP 实际上是这样做的,并注意到每次 first 调用都需要更长的时间。这个问题与微基准测试唯一有关的是它询问了一个特定的陷阱及其原因。
    • OP 接受了它。所以这似乎是一个答案。这些问题不需要回答,它们是对 OP 的提示,我认为他理解它们。
    • 是的,非常感谢,在我看来有几个正确答案
    【解决方案3】:

    JVM 花了一些时间来初始化所有需要的对象、访问系统时间、系统输出流等……你有两种方法发生在两者之间:

    System.nanoTime() 
    System.out.println() 
    

    每个都可能执行了很多初始化代码)。

    每次连续调用都快得多,因为这一切都已经设置好了。因此,当您对应用程序的性能进行基准测试时,通常会放弃预热和冷却阶段(例如,第一和最后 15 分钟)。

    【讨论】:

    • 只有天知道为什么第 9 次调用有点慢。这不是问题所在。为什么第一次调用很慢是因为 Java 懒惰地初始化东西,获取操作系统句柄等......
    • 或者运行JIT编译器。 ;-)
    • @PeterPaulKiefer 由于我有相反的信息,您能否提供任何可靠的来源,说明 JIT 编译器在代码的第一次迭代中使用默认的 JVM 设置启动?
    • @PeterPaulKiefer 您链接到关于 JIT 编译器一般用途的问答。我的问题要具体得多:提供参考以支持 JIT 编译发生在 第一次 迭代代码中的断言。否则,众所周知的事实是,必须通过多次执行代码来“预热”代码以触发 JIT 编译(具体而言,10,000 次迭代是阈值的默认值)。我真的很惊讶这个事实还没有传达给你。即使使用分层编译,C1 也不会在第一次运行时使用。
    • @PeterPaulKiefer the JVM can decide very early---在这里你承认你不知道它实际上发生在这种特殊情况下。考虑到 System.out.println() 调用是一个非常特殊的情况,并且有来自 JVM 的非常具体的支持(从 out 开始是一个最终的空值字段——一个神奇的、语言外延迟初始化的明显标志),真的有你的断言没有分量。您声称额外延迟是关于 JIT 编译而排除其他所有内容的说法尤其毫无根据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-18
    • 1970-01-01
    • 2019-09-13
    • 2018-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多