在低级别测量经过时间时,我可以选择使用以下任何一种:
System.currentTimeMillis();
System.nanoTime();
两种方法都实现了native
。在深入研究任何 C 代码之前,是否有人知道调用其中一个或另一个是否有任何实质性开销?我的意思是,如果我真的不关心额外的精度,那么预计哪一个会消耗更少的 CPU 时间?
注意:我使用的是标准的 Java 1.6 JDK,但这个问题可能对任何 JRE 都有效......
此页面上标记为正确的答案实际上是不正确的。由于 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()
本身是一种特殊的扭曲基准。答案和权威文章在这里。
我不相信你需要担心两者的开销。它是如此之小,以至于它本身几乎无法测量。这是两者的快速微基准:
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()
追求精确度和准确性,因为它与时钟无关。
你应该只用System.nanoTime()
它来测量运行某个东西需要多长时间。这不仅仅是纳秒精度的问题,System.currentTimeMillis()
而是“挂钟时间”,而System.nanoTime()
用于计时并且没有其他“真实世界时间”的怪癖。来自的Javadoc System.nanoTime()
:
此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关。
System.currentTimeMillis()
通常非常快(afaik 5-6 cpu 周期,但我不知道我在哪里读过这个),但它的分辨率在不同的平台上有所不同。
因此,如果您需要高精度,请选择nanoTime()
,如果您担心开销,请选择currentTimeMillis()
。
如果您有时间,请观看Cliff Click的演讲,他谈到了价格System.currentTimeMillis
以及其他事情。
这个问题的公认答案确实是不正确的。@brettw 提供的替代答案很好,但仍然没有详细说明。
有关此主题的完整处理和这些电话的实际成本,请参阅https://shipilev.net/blog/2014/nanotrusting-nanotime/
要回答提出的问题:
有谁知道调用一个或另一个是否有任何重大开销?
System#nanoTime
每次调用 的开销在 15 到 30 纳秒之间。nanoTime
,它的分辨率,每 30 纳秒只改变一次这意味着取决于您是否尝试每秒执行数百万个请求,调用nanoTime
意味着您实际上会丢失第二次调用的很大一部分nanoTime
。对于此类用例,请考虑测量来自客户端的请求,从而确保您不会陷入协调遗漏,测量队列深度也是一个很好的指标。
如果您不想在一秒钟内完成尽可能多的工作,那nanoTime
并不重要,但协调的遗漏仍然是一个因素。
最后,为了完整性,currentTimeMillis
不管它的成本是多少,都不能使用。这是因为不能保证在两个呼叫之间前进。尤其是在带有 NTP 的服务器上,currentTimeMillis
是不断移动的。更不用说计算机测量的大多数东西都不需要整整一毫秒。
在理论上,对于使用本机线程并位于现代抢占式操作系统上的 VM,currentTimeMillis 可以实现为每个时间片仅读取一次。据推测,nanoTime 实现不会牺牲精度。
唯一的问题currentTimeMillis()
是当你的虚拟机调整时间时,(这通常会自动发生)currentTimeMillis()
会随之而来,从而产生不准确的结果,特别是对于基准测试。