如博客文章 Beware of System.nanoTime() in Java 中所述,在x86系统上,Java的System.nanoTime()使用CPU特定计数器返回时间值。现在考虑我用来测量呼叫时间的以下情况:
long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;
现在在多核系统中,可能是在测量time1之后,线程被调度到不同的处理器,其计数器小于先前CPU的计数器。因此,我们可以在time2中得到一个值 less 而不是time1。因此,我们将在timeSpent中得到负值。
考虑到这种情况,现在不是System.nanotime几乎没用了吗?
我知道改变系统时间不会影响纳米时间。这不是我上面描述的问题。问题是每个CPU都会在打开后保留不同的计数器。与第一个CPU相比,第二个CPU上的计数器可以更低。由于在获取time1后,操作系统可以将线程调度到第二个CPU,因此timeSpent的值可能不正确甚至是负数。
答案 0 :(得分:202)
这个答案是在2011年从当时在操作系统上运行的Sun JDK实际上所做的事情的角度编写的。那是很久以前的事! leventov's answer提供了更新的观点。
该帖子错误,nanoTime
是安全的。有一条评论链接到a blog post by David Holmes,这是Sun的实时和并发人员。它说:
使用QueryPerformanceCounter / QueryPerformanceFrequency API实现System.nanoTime()[...] QPC使用的默认机制由硬件抽象层(HAL)确定[...]此默认值不仅在硬件上更改,也适用于OS版本。例如,Windows XP Service Pack 2改变了使用电源管理计时器(PMTimer)而不是处理器时间戳计数器(TSC)的原因,因为TSC的问题未在SMP系统中的不同处理器上同步,并且由于其频率根据电源管理设置,可以改变(因此它与经过时间的关系)。
因此,在Windows上,这个 在WinXP SP2之前出现了问题,但现在还没有。
我找不到谈论其他平台的第二部分(或更多部分),但该文章确实包含了Linux遇到的注释,并以相同的方式解决了同样的问题,并带有{{3的链接,}说:
- 所有处理器/核心的clock_gettime(CLOCK_REALTIME)是否一致? (拱形物是否重要?例如ppc,arm,x86,amd64,sparc)。
醇>它 或者它被认为是错误的。
但是,在x86 / x86_64上,可以看到未同步或变量的频率TSC导致时间不一致。 2.4内核确实没有针对此的保护,早期的2.6内核也没有做得太好。从2.6.18开始,用于检测的逻辑更好,我们通常会回到安全的时钟源。
ppc总是有一个同步的时基,所以这应该不是问题。
因此,如果Holmes的链接可以被理解为暗示nanoTime
调用clock_gettime(CLOCK_REALTIME)
,那么从x86上的内核2.6.18开始就是安全的,并且总是在PowerPC上(因为IBM和Motorola,与英特尔不同,实际上知道如何设计微处理器。)
遗憾的是,没有提到SPARC或Solaris。当然,我们不知道IBM JVM的功能。但现代Windows和Linux上的Sun JVM正确地做到了这一点。
编辑:这个答案是基于它引用的来源。但我仍然担心它可能实际上是完全错误的。一些更新的信息将非常有价值。我刚刚看到了FAQ for clock_gettime(CLOCK_REALTIME)的链接,这可能很有用。
答案 1 :(得分:35)
我做了一些搜索,发现如果一个人是迂腐,那么是的,它可能被认为是无用的...在特殊情况......这取决于你的要求对时间的敏感程度......
从Java Sun站点查看this quote:
实时时钟和 System.nanoTime()都基于 相同的系统调用因此相同 时钟。
使用Java RTS,所有基于时间的API (例如,定时器,定期 线程,截止日期监控等 ())是基于 高分辨率计时器。而且,在一起 他们可以通过实时优先考虑 确保适当的代码 在适当的时间执行 实时约束。相反, 普通的Java SE API只提供了一些 能够处理的方法 高分辨率时代,没有 保证在给定的执行 时间。使用System.nanoTime()之间 要执行的代码中的各个点 经过时间测量应该 总是准确的。
Java还有一个caveat for the nanoTime()方法:
此方法只能用于 测量经过的时间而不是 与任何其他系统概念有关 或挂钟时间。返回的值 从某些方面起代表纳秒 固定但任意的时间(也许是在 未来,所以价值观可能如此 负)。这种方法提供 纳秒精度,但不是 必须达到纳秒精度。没有 保证是如何做的 经常改变价值观。差异 在连续的呼叫中跨越更大 大约292。3年(2 63 纳秒)不准确 计算由数字引起的经过时间 溢出。
似乎可以得出的唯一结论是nanoTime()不能作为准确的值来依赖。因此,如果您不需要测量仅相差纳秒的时间,那么即使得到的返回值为负,此方法也足够好。但是,如果您需要更高的精度,他们似乎建议您使用JAVA RTS。
所以回答你的问题......没有nanoTime()没有用......它不是在每种情况下都使用的最谨慎的方法。
答案 2 :(得分:18)
无需辩论,只需使用来源。 在这里,SE 6 for Linux,得出你自己的结论:
jlong os::javaTimeMillis() {
timeval time;
int status = gettimeofday(&time, NULL);
assert(status != -1, "linux error");
return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000);
}
jlong os::javaTimeNanos() {
if (Linux::supports_monotonic_clock()) {
struct timespec tp;
int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp);
assert(status == 0, "gettime error");
jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec);
return result;
} else {
timeval time;
int status = gettimeofday(&time, NULL);
assert(status != -1, "linux error");
jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec);
return 1000 * usecs;
}
}
答案 3 :(得分:10)
免责声明:我是此图书馆的开发者
您可能更喜欢这样:
http://juliusdavies.ca/nanotime/
但是它将DLL或Unix .so(共享对象)文件复制到当前用户的主目录中,以便它可以调用JNI。
我的网站上提供了一些背景信息:
http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html
答案 4 :(得分:8)
自Java 7起,{J {1}}被保证是安全的。 System.nanoTime()
's Javadoc明确指出,JVM中所有观察到的调用(即跨所有线程) )是单调的:
返回的值表示自某个固定但任意的原始时间以来的纳秒(也许在将来,因此值可能为负)。在Java虚拟机的实例中,此方法的所有调用都使用相同的源。其他虚拟机实例可能会使用其他来源。
JVM / JDK实现负责消除调用底层OS实用程序(例如,Tom Anderson's answer中提到的那些)时可能观察到的不一致。
此问题的其他大多数旧答案(写于2009–2012年)表示FUD可能与Java 5或Java 6有关,但与现代Java版本不再相关。
但是,值得一提的是,尽管JDK保证了System.nanoTime()
的安全性,但OpenJDK中还是存在一些错误,使得它在某些平台上或某些情况下(例如JDK-8040140 ,JDK-8184271)。目前nanoTime()
中没有OpenJDK的公开(已知)错误,但是发现此类新错误或OpenJDK的较新版本中的回归并不会让任何人感到震惊。
请记住,使用nanoTime()
进行定时阻塞,间隔等待,超时等的代码最好将负时间差(超时)视为零,而不是引发异常。这种做法也是可取的,因为它与nanoTime()
中所有类(例如Semaphore.tryAcquire()
,java.util.concurrent.*
,BlockingQueue.poll()
等)中所有定时等待方法的行为一致。 / p>
尽管如此,对于Lock.tryLock()
实施定时阻塞,间隔等待,超时等,仍应首选nanoTime()
,因为currentTimeMillis()
会受到“时间倒退”现象的影响(例如,由于服务器时间校正),i。 e。 currentTimeMillis()
根本不适合测量时间间隔。有关更多信息,请参见this answer。
最好不要使用专门的基准测试框架和分析器,例如,JMH中的async-profiler和wall-clock profiling mode,而不是直接使用nanoTime()
来测量代码执行时间。
答案 5 :(得分:6)
Linux纠正了CPU之间的差异,但Windows没有。我建议你假设System.nanoTime()只能精确到1微秒左右。获得更长时间的一种简单方法是将foo()调用1000次或更多次,并将时间除以1000.
答案 6 :(得分:5)
绝对没有用武之地。时间爱好者正确地指出了多核问题,但在实用词应用程序中,它通常比currentTimeMillis()好得多。
在帧刷新中计算图形位置时,nanoTime()可以在我的程序中实现更平滑的运动。
我只测试多核机器。
答案 7 :(得分:4)
我看到使用System.nanoTime()报告的已过去时间为负。需要说明的是,相关代码是:
long startNanos = System.nanoTime();
Object returnValue = joinPoint.proceed();
long elapsedNanos = System.nanoTime() - startNanos;
和变量'elapsedNanos'的值为负值。 (我很肯定中间调用也花了不到293年,这是存储在longs中的nanos的溢出点:)
这是在运行AIX的IBM P690(多核)硬件上使用IBM v1.5 JRE 64位时发生的。我只看到过这种错误,所以看起来非常罕见。我不知道原因 - 它是一个特定于硬件的问题,一个JVM缺陷 - 我不知道。我也不知道对nanoTime()的准确性的影响。
要回答原始问题,我认为nanoTime没有用处 - 它提供亚毫秒级时序,但实际(不仅仅是理论上)风险是不准确的,您需要考虑。
答案 8 :(得分:2)
不,它不是......它只取决于你的CPU,检查High Precision Event Timer如何/为什么根据CPU对事物进行不同的处理。
基本上,阅读Java的源代码并检查您的版本对该函数的作用,如果它对CPU起作用,您将运行它。
IBM even suggests您将其用于性能基准测试(2008年发布,但已更新)。
答案 9 :(得分:2)
在运行Windows XP和JRE 1.5.0_06的Core 2 Duo上,这似乎不是问题。
在使用三个线程的测试中,我没有看到System.nanoTime()向后移动。处理器都很忙,并且线程偶尔会睡觉以引起移动的线程。
[编辑]我猜它只发生在物理上独立的处理器上,即计数器在同一个芯片上同步多个核心。
答案 10 :(得分:2)
我正在联系Peter Laurerey提供一个好答案的基本相同的讨论。 Why I get a negative elapsed time using System.nanoTime()?
许多人提到在Java System.nanoTime()中可能会返回负时间。我为重复其他人已经说过的事而道歉。
如果System.nanoTime()返回执行它的coreID,那就太酷了。
答案 11 :(得分:1)
Java是跨平台的,而nanoTime与平台有关。如果使用Java - 何时不使用nanoTime。我用这个函数在不同的jvm实现中发现了真正的错误。
答案 12 :(得分:0)
答案 13 :(得分:0)
另外,System.currentTimeMillies()
会在您更改系统时钟时发生变化,而System.nanoTime()
则不会,因此后者更安全地衡量持续时间。
答案 14 :(得分:-3)
nanoTime
对时间安排非常不安全。我在我的基本素数测试算法上尝试了它,并给出了相同输入的相隔一秒的答案。不要使用那种荒谬的方法。我需要的东西比获得时间毫秒更精确和准确,但不如nanoTime
那么糟糕。