是System.nanoTime()完全无用?
正如在Java系统中的Beware of System.nanoTime()中所记载的,在x86系统上,Java的System.nanoTime()使用CPU特定的计数器返回时间值。 现在考虑一下我用来衡量一个通话时间的情况:
long time1= System.nanoTime(); foo(); long time2 = System.nanoTime(); long timeSpent = time2-time1;
现在在一个多核系统中,可能是在测量了time1之后,线程被调度到一个不同于前一个CPU的计数器的处理器。 因此我们可以得到一个小于 time1的时间值2。 因此,我们会在timeSpent中得到一个负值。
考虑到这种情况,是不是现在System.nanotime几乎没用?
我知道改变系统时间不会影响碳纳米pipe。 这不是我上面描述的问题。 问题是,每个CPU自打开以来都会保持不同的计数器。 与第一个CPU相比,第二个CPU的计数器可以更低。 由于在得到time1之后,线程可以由OS调度到第二个CPU,timeSpent的值可能不正确,甚至是负值。
那个post是错误的, nanoTime
是安全的。 有一篇关于这个post的评论,这个post是由 Sun的一位实时和并发的人David Holmes撰写的。 它说:
System.nanoTime()使用QueryPerformanceCounter / QueryPerformanceFrequency API实现QPC使用的默认机制由硬件抽象层(HAL)决定[…]。这个默认的变化不仅跨越硬件,而且跨OS版本。 例如,由于在SMP系统中不同步处理器上TSC未同步的问题,Windows XP Service Pack 2改变了使用电源pipe理定时器(PMTimer)而不是处理器时间戳计数器(TSC)的原因,并且由于其频率可以根据电源pipe理设置的不同而变化(因此与时间的关系)。
所以,在Windows上,直到WinXP SP2 才出现问题,但现在不是。
我找不到涉及其他平台的第二部分(或更多),但是那篇文章确实包含了Linux遇到的问题,并以相同的方式解决了相同的问题,并链接到clock_gettime(CLOCK_REALTIME) ,其中说:
- 所有处理器/内核的clock_gettime(CLOCK_REALTIME)是否一致? (是否拱形?例如ppc,arm,x86,amd64,sparc)。
它应该或它被认为是越野车。
但是,在x86 / x86_64上,可能会看到未定频率或可变频率的TSC会导致时间不一致。 2.4内核真的没有保护,早期的2.6内核在这里也不太好。 从2.6.18开始,检测这个的逻辑更好,我们通常会回到一个安全的时钟源。
ppc总是有一个同步的时基,所以这不应该是一个问题。
因此,如果福尔摩斯的链接可以被理解为暗示nanoTime
调用clock_gettime(CLOCK_REALTIME)
,那么在x86上内核2.6.18是安全的,并且总是在PowerPC上(因为与Intel不同,IBM和Motorola不知道如何devise微处理器)。
不幸的是,没有提到SPARC或Solaris。 当然,我们不知道IBM JVM是做什么的。 但是在现代Windows和Linux上的Sun JVM可以正确使用。
编辑:这个答案是基于它引用的来源。 但我仍然担心这可能是完全错误的。 一些更新的信息将是非常有价值的。 我刚刚遇到了一个有关Linux时钟四年的新文章的链接,这可能是有用的。
我做了一些search,发现如果一个人是迂腐的,那么是的,这可能被认为是无用的…在特定的情况下…这取决于你的需求是如何敏感的时间…
查看Java Sun站点的这个引用 :
实时时钟和System.nanoTime()都基于相同的系统调用,因此是相同的时钟。
使用Java RTS,所有基于时间的API(例如,定时器,定期线程,截止期限监控等)均基于高分辨率定时器。 并且,与实时优先级一起,它们可以确保在适当的时间执行适当的代码以实现实时约束。 相比之下,普通的Java SE API只提供了一些能够处理高分辨率时间的方法,在给定的时间不能保证执行。 在代码中的各个点之间使用System.nanoTime()来执行经过的时间测量应该始终是准确的。
Java 对nanoTime()方法也有一个警告 :
此方法只能用于测量已用时间,并不涉及系统或挂钟时间的任何其他概念。 返回的值表示自一些固定但随意的时间(可能在未来,所以值可能为负)以来的毫微秒。 该方法提供了纳秒精度,但不一定是纳秒精度。 没有保证值的变化频率。 连续调用中跨越大约292.3年(2 63纳秒)的差异由于数值溢出而不能精确地计算经过时间。
似乎可以得出的唯一结论是nanoTime()不能作为一个准确的值。 因此,如果您不需要测量仅仅相隔纳秒的时间,那么即使返回的结果为负值,此方法也足够好。 但是,如果您需要更高的精度,他们似乎build议您使用JAVA RTS。
所以要回答你的问题…没有nanoTime()是没有用的….它只是不是在所有情况下使用的最审慎的方法。
不需要辩论,只需使用源代码。 在这里,针对Linux的SE 6,作出自己的结论:
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; } }
免责声明:我是这个图书馆的开发者
你可能会更喜欢这个:
http://juliusdavies.ca/nanotime/
但它将DLL或Unix .so(共享对象)文件复制到当前用户的主目录中,以便它可以调用JNI。
一些背景信息在我的网站上:
http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html
Linux纠正了CPU之间的差异,但Windows不支持。 我build议你假设System.nanoTime()只能精确到1微秒左右。 获得更长时间的简单方法是调用foo()1000次或更多次,并将时间除以1000。
绝对不是无用的。 时机爱好者正确地指出了多核问题,但在实际应用中,它通常比currentTimeMillis()好得多。
在帧刷新中计算graphics位置nanoTime()会导致我的程序中运动更平滑。
而我只在多核机器上testing。
我已经看到从使用System.nanoTime()报告负时间 。 要清楚的是,有问题的代码是:
long startNanos = System.nanoTime(); Object returnValue = joinPoint.proceed(); long elapsedNanos = System.nanoTime() - startNanos;
和variables'elapsedNanos'有一个负值。 (我确信中间电话的使用时间还不到293年,这是纳米储存的长期溢出点:)
这发生在运行AIX的IBM P690(多核)硬件上使用IBM v1.5 JRE 64位。 我只看到这个错误发生一次,所以看起来非常罕见。 我不知道原因 – 是硬件特定的问题,还是JVM的缺陷 – 我不知道。 一般来说,我也不知道nanoTime()的准确性。
为了回答最初的问题,我不认为nanoTime是毫无用处的 – 它提供了毫秒级以下的时间,但是有一个实际的(而不仅仅是理论上的)不准确的风险,需要考虑。
我将链接到Peter Lawrey提供的一个很好的答案的基本上是相同的讨论。 为什么我使用System.nanoTime()获得消耗的时间?
许多人提到在Java System.nanoTime()可能会返回负面的时间。 我很抱歉重复其他人已经说过的话。
- nanoTime()不是时钟,而是CPU周期计数器。
- 返回值按频率除以时间。
- CPU频率可能会波动。
- 当你的线程安排在另一个CPU上时,有可能得到nanoTime(),这会导致负面的差异。 这是合乎逻辑的。 跨CPU的计数器不同步。
- 在很多情况下,你可能会得到相当令人误解的结果,但是你不能说出来,因为三angular洲不是负面的。 想想看。
- (未经证实)我认为如果指令被重新sorting,即使在同一个CPU上,也可能得到一个负面的结果。 为了防止这种情况,你必须调用序列化你的指令的内存屏障。
如果System.nanoTime()返回了执行的coreID,那将会很酷。
这在运行Windows XP和JRE 1.5.0_06的Core 2 Duo上似乎没有问题。
在三个线程的testing中,我没有看到System.nanoTime()后退。 处理器都很忙,线程偶尔进入hibernate状态,以引起线程移动。
[编辑]我猜想,它只发生在物理上分开的处理器,即计数器同步芯片上的多个核心。
Java是跨平台的,nanoTime是平台依赖的。 如果您使用Java – 不使用nanoTime。 我用这个函数在不同的jvm实现中发现了真正的错误。
不,这不取决于你的CPU,检查高精度事件定时器是如何/为什么根据CPU不同地处理。
基本上,阅读你的Java的来源,并检查你的版本做了什么function,如果它对CPU的作品,你将运行它。
IBM甚至build议您将其用于性能基准testing(2008年发布,但已更新)。
Java 5文档还build议使用此方法来达到同样的目的。
此方法只能用于测量已用时间,并不涉及系统或挂钟时间的任何其他概念。
Java 5 API文档
另外, System.currentTimeMillies()
会在您更改系统时钟时发生更改,而System.nanoTime()
则不会,所以后者更安全,以便测量持续时间。
nanoTime
对于时间来说是非常不安全的。 我尝试了我的基本素数testingalgorithm,它给了相同的input差不多一秒钟的答案。 不要用那种荒谬的方法。 我需要的东西比获得时间毫秒更准确,更精确,但不像nanoTime
那么糟糕。