System.nanoTime() 是完全无用的吗?

2022-08-31 07:18:59

博客文章小心 Java 中的 System.nanoTime() 中所述,在 x86 系统上,Java 的 System.nanoTime() 使用特定于 CPU 的计数器返回时间值。现在考虑以下情况,我用它来测量呼叫的时间:

long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;

现在,在多核系统中,可能是在测量 time1 之后,线程被调度到另一个处理器,该处理器的计数器小于以前的 CPU。因此,我们可以在 time2 中得到一个小于 time1 的值。因此,我们将在时间花费中得到一个负值。

考虑到这种情况,是不是System.nanotime现在几乎没用了?

我知道改变系统时间不会影响纳米时间。这不是我上面描述的问题。问题在于,每个 CPU 在打开后将保留不同的计数器。与第一个 CPU 相比,第二个 CPU 上的此计数器可以更低。由于操作系统可以在获取 time1 后将线程调度到第二个 CPU,因此 timeSpend 的值可能不正确,甚至为负值。


答案 1

这个答案是在2011年写的,从当时运行的Sun JDK在当时的操作系统上实际做了什么的角度来看。那是很久以前的事了!列文托夫的答案提供了一个更新的观点。

那篇文章是错误的,是安全的。这篇文章上有一条评论,链接到David Holmes的一篇博客文章,David Holmes是Sun的实时和并发人员。它说:nanoTime

System.nanoTime() 是使用 QueryPerformanceCounter/QueryPerformanceFrequency API 实现的 [...]QPC使用的默认机制由硬件抽象层(HAL)[...]此默认值不仅跨硬件更改,而且跨操作系统版本更改。例如,Windows XP Service Pack 2 更改了使用电源管理计时器 (PMTimer) 而不是处理器时间戳计数器 (TSC) 的情况,因为 TSC 在 SMP 系统中的不同处理器上未同步,并且由于它的频率可以根据电源管理设置而变化(因此它与经过时间的关系)。

因此,在Windows上,直到WinXP SP2之前,这都是一个问题,但现在不是了。

我找不到讨论其他平台的第二部分(或更多),但那篇文章确实包括Linux遇到并以相同的方式解决了相同问题的评论,并链接到clock_gettime(CLOCK_REALTIME)的常见问题解答,其中说:

  1. clock_gettime(CLOCK_REALTIME) 在所有处理器/内核上是否一致?(拱门重要吗?例如ppc,arm,x86,amd64,sparc)。

它应该或它被认为是有缺陷的。

但是,在 x86/x86_64上,可能会看到未同步或可变频率的 TSC 导致时间不一致。2.4 内核真的没有针对此的保护,早期的 2.6 内核在这里的表现也不太好。从 2.6.18 及更高版本开始,检测此情况的逻辑更好,我们通常会回退到安全的时钟源。

ppc始终具有同步的时基,因此这应该不是问题。

因此,如果Holmes的链接可以被理解为暗示调用,那么从x86上的内核2.6.18开始,它就是安全的,并且始终在PowerPC上(因为IBM和摩托罗拉与英特尔不同,实际上知道如何设计微处理器)。nanoTimeclock_gettime(CLOCK_REALTIME)

可悲的是,没有提到SPARC或Solaris。当然,我们不知道IBM JVM是做什么的。但是现代Windows和Linux上的Sun JVM是正确的。

编辑:这个答案是基于它引用的来源。但我仍然担心它实际上可能是完全错误的。一些更新的信息将非常有价值。我刚刚发现了一个关于Linux时钟的四年新文章的链接,这可能是有用的。


答案 2

我做了一些搜索,发现如果一个人是迂腐的,那么是的,它可能被认为是无用的......在特定情况下...这取决于您的要求对时间的敏感程度...

从 Java Sun 站点查看以下引用

实时时钟和System.nanoTime()都基于相同的系统调用,因此基于相同的时钟。

使用 Java RTS,所有基于时间的 API(例如,计时器、定期线程、截止时间监控等)都基于高分辨率计时器。而且,与实时优先级一起,他们可以确保在正确的时间执行适当的代码,以实现实时约束。相比之下,普通的Java SE API只提供几种能够处理高分辨率时间的方法,不能保证在给定时间执行。在代码中的各个点之间使用 System.nanoTime() 执行经过的时间测量应始终准确。

Java对nanoTime()方法也有一个警告:

此方法只能用于测量经过的时间,与任何其他系统或挂钟时间概念无关。返回的值表示自某个固定但任意时间(可能是将来,因此值可能为负)以来的纳秒数。此方法提供纳秒级精度,但不一定提供纳秒级精度。不保证值更改的频率。由于数值溢出,跨度大于约 292.3 年(263 纳秒)的连续调用的差异将无法准确计算经过的时间。

似乎唯一可以得出的结论是,nanoTime() 不能作为一个准确的值来依赖。因此,如果您不需要测量仅相隔纳秒的时间,那么即使结果返回值为负,此方法也足够好。但是,如果您需要更高的精度,他们似乎建议您使用JAVA RTS。

所以要回答你的问题...没有纳米时间()不是无用的....它只是不是在每种情况下使用的最谨慎的方法。


推荐