51

在低级别测量经过时间时,我可以选择使用以下任何一种:

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

两种方法都实现了native。在深入研究任何 C 代码之前,是否有人知道调用其中一个或另一个是否有任何实质性开销?我的意思是,如果我真的不关心额外的精度,那么预计哪一个会消耗更少的 CPU 时间?

注意:我使用的是标准的 Java 1.6 JDK,但这个问题可能对任何 JRE 都有效......

4

8 回答 8

45

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

这是System.currentTimeMillis()vs的 JMH 基准测试System.nanoTime()

@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();
   }
}

以下是结果(在 Intel Core 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()与 ~123ns 相比,每次调用 ~118ns 稍快。然而,同样清楚的是,一旦将平均误差考虑在内,两者之间的差异非常小。结果也可能因操作系统而异。但总的来说,它们在开销方面基本上是相等的。

2015 年 8 月 25 日更新:虽然这个答案更接近于最正确的答案,但使用 JMH 进行测量,它仍然不正确。测量类似事物System.nanoTime()本身是一种特殊的扭曲基准。答案和权威文章在这里

于 2014-03-10T15:31:33.857 回答
26

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

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()追求精确度和准确性,因为它与时钟无关。

于 2011-04-12T19:28:20.457 回答
11

你应该只用System.nanoTime()它来测量运行某个东西需要多长时间。这不仅仅是纳秒精度的问题,System.currentTimeMillis()而是“挂钟时间”,而System.nanoTime()用于计时并且没有其他“真实世界时间”的怪癖。来自的Javadoc System.nanoTime()

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

于 2011-04-12T19:29:23.017 回答
7

System.currentTimeMillis()通常非常快(afaik 5-6 cpu 周期,但我不知道我在哪里读过这个),但它的分辨率在不同的平台上有所不同。

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

于 2011-04-12T19:28:25.280 回答
6

如果您有时间,请观看Cliff Click的演讲,他谈到了价格System.currentTimeMillis以及其他事情。

于 2011-04-12T21:47:44.417 回答
4

这个问题的公认答案确实是不正确的。@brettw 提供的替代答案很好,但仍然没有详细说明。

有关此主题的完整处理和这些电话的实际成本,请参阅https://shipilev.net/blog/2014/nanotrusting-nanotime/

要回答提出的问题:

有谁知道调用一个或另一个是否有任何重大开销?

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

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

如果您不想在一秒钟内完成尽可能多的工作,那nanoTime并不重要,但协调的遗漏仍然是一个因素。

最后,为了完整性,currentTimeMillis不管它的成本是多少,都不能使用。这是因为不能保证在两个呼叫之间前进。尤其是在带有 NTP 的服务器上,currentTimeMillis是不断移动的。更不用说计算机测量的大多数东西都不需要整整一毫秒。

于 2016-11-20T15:37:15.283 回答
2

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

于 2011-04-12T19:40:21.163 回答
0

唯一的问题currentTimeMillis()是当你的虚拟机调整时间时,(这通常会自动发生)currentTimeMillis()会随之而来,从而产生不准确的结果,特别是对于基准测试。

于 2020-01-22T22:54:39.833 回答