Java中的时间测量开销

时间:2011-04-12 19:19:40

标签: java timestamp overhead

当测量低水平的经过时间时,我可以选择使用以下任何一种:

System.currentTimeMillis();
System.nanoTime();

这两种方法都已实施native。在深入研究任何C代码之前,有没有人知道是否有任何大量的开销要求调用其中一个?我的意思是,如果我真的不关心额外的精度,那么预期哪个CPU耗时更少?

N.B:我使用的是标准的Java 1.6 JDK,但问题可能对任何JRE都有​​效......

8 个答案:

答案 0 :(得分:43)

此页面上标记正确的答案实际上不正确。由于JVM死代码消除(DCE),堆栈替换(OSR),循环展开等,这不是编写基准测试的有效方法。只有像Oracle的JMH微基准测试框架这样的框架才能正确地测量这样的东西。如果您对此类微基准的有效性有任何疑问,请阅读this post

以下是System.currentTimeMillis()System.nanoTime()的JMH基准:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class NanoBench {
   @Benchmark
   public long currentTimeMillis() {
      return System.currentTimeMillis();
   }

   @Benchmark
   public long nanoTime() {
    return System.nanoTime();
   }
}

以下是结果(在英特尔酷睿i5上):

Benchmark                            Mode  Samples      Mean   Mean err    Units
c.z.h.b.NanoBench.currentTimeMillis  avgt       16   122.976      1.748    ns/op
c.z.h.b.NanoBench.nanoTime           avgt       16   117.948      3.075    ns/op

这表明System.nanoTime()在每次调用约118ns时略快于~122ns。然而,同样清楚的是,一旦考虑到平均误差,两者之间几乎没有差别。结果也可能因操作系统而异。但总的说法应该是它们在开销方面基本相同。

更新2015/08/25:虽然这个答案更接近正确,但使用JMH进行衡量,仍然不正确。测量像System.nanoTime()这样的东西本身就是一种特殊的扭曲基准测试。答案和最终文章是here

答案 1 :(得分:25)

我不相信你需要担心两者的开销。它是如此微小,它本身几乎无法衡量。以下是两者的快速微观基准:

for (int j = 0; j < 5; j++) {
    long time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.currentTimeMillis();
    }
    System.out.println((System.nanoTime() - time) + "ns per million");

    time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.nanoTime();
    }
    System.out.println((System.nanoTime() - time) + "ns per million");

    System.out.println();
}

最后的结果:

14297079ns per million
29206842ns per million

看来System.currentTimeMillis()的速度是System.nanoTime()的两倍。然而,无论如何,29ns将比你测量的任何其他东西短得多。因为它与时钟无关,所以我需要System.nanoTime()来获得精确度和准确度。

答案 2 :(得分:11)

您应该只使用System.nanoTime()来衡量某些内容的运行时间。这不仅仅是纳秒级精度的问题,System.currentTimeMillis()是“挂钟时间”,而System.nanoTime()用于计时,而不具有另一方的“现实世界时间”怪癖。来自System.nanoTime()的Javadoc:

  

此方法只能用于测量经过时间,与系统或挂钟时间的任何其他概念无关。

答案 3 :(得分:6)

System.currentTimeMillis()通常非常快(afaik 5-6 cpu cycle但我不知道我在哪里读过这个),但它的分辨率因平台而异。

因此,如果您需要高精度,请转到nanoTime(),如果您担心开销,请转到currentTimeMillis()

答案 4 :(得分:6)

如果您有时间,请注意this talk by Cliff Click,他会谈及System.currentTimeMillis的价格以及其他事项。

答案 5 :(得分:4)

这个问题的接受答案确实是错误的。 @brettw提供的另一个答案是好的,但仍然清楚细节。

有关此主题的完整处理以及这些来电的实际费用,请参阅https://shipilev.net/blog/2014/nanotrusting-nanotime/

回答问题:

  

是否有人知道是否有任何实质性的开销呼叫一个或另一个?

  • 每次通话调用System#nanoTime的开销在15到30纳秒之间。
  • nanoTime报告的值,其分辨率,每30纳秒只更改一次

这意味着,如果您每秒尝试执行数百万次请求,则调用nanoTime意味着您实际上正在丢失第二次调用nanoTime的大部分内容。对于此类用例,请考虑测量来自客户端的请求,从而确保您不会陷入coordinated omission,测量队列深度也是一个很好的指标。

如果你没有尽可能多地将工作塞进一秒钟,那么nanoTime并不重要,但协调的遗漏仍然是一个因素。

最后,为了完整性,currentTimeMillis无论成本如何都不能使用。这是因为无法保证在两次通话之间前进。特别是在具有NTP的服务器上,currentTimeMillis不断移动。更不用说计算机测量的大多数东西都不需要整整一毫秒。

答案 6 :(得分:2)

在理论层面,对于使用本机线程并位于现代抢占式操作系统上的VM,currentTimeMillis可以实现为每个时间段只读一次。据推测,nanoTime实现不会牺牲精度。

答案 7 :(得分:0)

currentTimeMillis()的唯一问题是,当您的VM调整时间时(这通常是自动发生的)currentTimeMillis()会随之而来,因此会产生未准确的结果,尤其是用于基准测试。