正如博客文章中所述谨防系统Java 中的 .nanoTime(),在 x86 系统上,Java 的 System.nanoTime() 使用 CPU 特定计数器。 现在考虑以下我用来测量调用时间的情况:
long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;
现在在多核系统中,在测量 time1 后,线程可能会被调度到另一个处理器,该处理器的计数器小于前一个 CPU 的计数器。 因此,我们可以在 time2 中得到一个小于 time1 的值。 因此,我们会得到 timeSpent 的负值。
考虑到这种情况,System.nanotime 现在是不是几乎没有用处?
我知道改变系统时间不会影响纳米时间。 这不是我上面描述的问题。 问题是每个CPU自从开启以来都会保留一个不同的计数器。 与第一个 CPU 相比,第二个 CPU 上的该计数器可能较低。 由于线程在获取time1后可以被操作系统调度到第二个CPU,因此timeSpent的值可能不正确,甚至为负值。
As documented in the blog post Beware of System.nanoTime() in Java, on x86 systems, Java's System.nanoTime() returns the time value using a CPU specific counter. Now consider the following case I use to measure time of a call:
long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;
Now in a multi-core system, it could be that after measuring time1, the thread is scheduled to a different processor whose counter is less than that of the previous CPU. Thus we could get a value in time2 which is less than time1. Thus we would get a negative value in timeSpent.
Considering this case, isn't it that System.nanotime is pretty much useless for now?
I know that changing the system time doesn't affect nanotime. That is not the problem I describe above. The problem is that each CPU will keep a different counter since it was turned on. This counter can be lower on the second CPU compared to the first CPU. Since the thread can be scheduled by the OS to the second CPU after getting time1, the value of timeSpent may be incorrect and even negative.
发布评论
评论(14)
这个答案是在 2011 年从当时操作系统上运行的 Sun JDK 实际做了什么的角度写的。 那是很久以前的事! leventov 的回答提供了更最新的观点。
那篇文章是错误的,
nanoTime
是安全的。 该帖子中有一条评论链接到 David Holmes 的博客文章,这是一篇实时和Sun 的并发专家。 它说:因此,在 Windows 上,这个问题在 WinXP SP2 之前一直是一个问题,但现在已经不是了。
我找不到讨论其他平台的第二部分(或更多部分),但该文章确实包含了 Linux 遇到并以相同方式解决相同问题的评论,并提供了 clock_gettime(CLOCK_REALTIME) 的常见问题解答,其中显示:
因此,如果 Holmes 的链接可以理解为暗示
nanoTime
调用clock_gettime(CLOCK_REALTIME)
,那么从 x86 上的内核 2.6.18 开始,它是安全的,并且始终在 PowerPC 上(因为 IBM 和摩托罗拉与英特尔不同,实际上知道如何设计微处理器)。遗憾的是,没有提及 SPARC 或 Solaris。 当然,我们不知道 IBM JVM 是做什么的。 但现代 Windows 和 Linux 上的 Sun JVM 做到了这一点。
编辑:这个答案基于它引用的来源。 但我仍然担心它实际上可能是完全错误的。 一些更新的信息将非常有价值。 我刚刚发现一个 的链接关于 Linux 时钟的四年新文章可能会有用。
This answer was written in 2011 from the point of view of what the Sun JDK of the time running on operating systems of the time actually did. That was a long time ago! leventov's answer offers a more up-to-date perspective.
That post is wrong, and
nanoTime
is safe. There's a comment on the post which links to a blog post by David Holmes, a realtime and concurrency guy at Sun. It says:So, on Windows, this was a problem up until WinXP SP2, but it isn't now.
I can't find a part II (or more) that talks about other platforms, but that article does include a remark that Linux has encountered and solved the same problem in the same way, with a link to the FAQ for clock_gettime(CLOCK_REALTIME), which says:
So, if Holmes's link can be read as implying that
nanoTime
callsclock_gettime(CLOCK_REALTIME)
, then it's safe-ish as of kernel 2.6.18 on x86, and always on PowerPC (because IBM and Motorola, unlike Intel, actually know how to design microprocessors).There's no mention of SPARC or Solaris, sadly. And of course, we have no idea what IBM JVMs do. But Sun JVMs on modern Windows and Linux get this right.
EDIT: This answer is based on the sources it cites. But i still worry that it might actually be completely wrong. Some more up-to-date information would be really valuable. I just came across to a link to a four year newer article about Linux's clocks which could be useful.
自 Java 7 起,JDK 规范保证
System.nanoTime()
的安全。System.nanoTime()
的 Javadoc 清楚地表明,所有观察到的调用JVM(即跨所有线程)是单调的:JVM/JDK 实现负责消除调用底层操作系统实用程序时可能观察到的不一致问题(例如 Tom Anderson 的回答中提到的那些< /a>)。
这个问题的大多数其他旧答案(写于 2009-2012 年)都表达了 FUD,这可能与 Java 5 或 Java 6 相关,但不再与现代版本的 Java 相关。
然而,值得一提的是,尽管 JDK 保证了 nanoTime() 的安全性,但 OpenJDK 中存在多个错误,使其在某些平台或某些情况下不支持这种保证(例如 JDK-8040140,JDK-8184271)。 目前 OpenJDK 中还没有关于 nanoTime() 的公开(已知)错误,但是在 OpenJDK 新版本中发现新的此类错误或回归不应让任何人感到震惊。
考虑到这一点,使用
nanoTime()
进行定时阻塞、间隔等待、超时等的代码最好将负时间差(超时)视为零,而不是抛出异常。 em> 这种做法也是更可取的,因为它与java.util.concurrent.*
中所有类中所有定时等待方法的行为一致,例如Semaphore.tryAcquire()
、Lock.tryLock()
、BlockingQueue.poll()
等。尽管如此,
nanoTime()
仍应优先用于实现定时阻塞、间隔等待、超时等,而不是currentTimeMillis()
因为后者会受到“时间倒退”现象的影响(例如,由于服务器时间校正),即currentTimeMillis()
根本不适合测量时间间隔。 > 请参阅此答案了解更多信息。最好使用专门的基准测试框架和分析器,而不是直接使用nanoTime()来测量代码执行时间,例如JMH 和 async-profiler 挂钟分析模式。
Since Java 7,
System.nanoTime()
is guaranteed to be safe by JDK specification.System.nanoTime()
's Javadoc makes it clear that all observed invocations within a JVM (that is, across all threads) are monotonic:JVM/JDK implementation is responsible for ironing out the inconsistencies that could be observed when underlying OS utilities are called (e. g. those mentioned in Tom Anderson's answer).
The majority of other old answers to this question (written in 2009–2012) express FUD that was probably relevant for Java 5 or Java 6 but is no longer relevant for modern versions of Java.
It's worth mentioning, however, that despite JDK guarantees
nanoTime()
's safety, there have been several bugs in OpenJDK making it to not uphold this guarantee on certain platforms or under certain circumstances (e. g. JDK-8040140, JDK-8184271). There are no open (known) bugs in OpenJDK wrtnanoTime()
at the moment, but a discovery of a new such bug or a regression in a newer release of OpenJDK shouldn't shock anybody.With that in mind, code that uses
nanoTime()
for timed blocking, interval waiting, timeouts, etc. should preferably treat negative time differences (timeouts) as zeros rather than throw exceptions. This practice is also preferable because it is consistent with the behaviour of all timed wait methods in all classes injava.util.concurrent.*
, for exampleSemaphore.tryAcquire()
,Lock.tryLock()
,BlockingQueue.poll()
, etc.Nonetheless,
nanoTime()
should still be preferred for implementing timed blocking, interval waiting, timeouts, etc. tocurrentTimeMillis()
because the latter is a subject to the "time going backward" phenomenon (e. g. due to server time correction), i. e.currentTimeMillis()
is not suitable for measuring time intervals at all. See this answer for more information.Instead of using
nanoTime()
for code execution time measurements directly, specialized benchmarking frameworks and profilers should preferably be used, for example JMH and async-profiler in wall-clock profiling mode.我做了一些搜索,发现如果一个人是迂腐的,那么是的,它可能被认为是无用的......在特定情况下......这取决于你的要求对时间的敏感度......
查看 这句话来自 Java Sun 站点:
Java 还有一个警告nanoTime() 方法:
似乎可以得出的唯一结论是 nanoTime() 不能作为准确值。 因此,如果您不需要测量相距仅纳秒的时间,那么即使返回的结果值为负,此方法也足够好。 但是,如果您需要更高的精度,他们似乎建议您使用 JAVA RTS。
所以回答你的问题......没有 nanoTime() 并不是没有用......它只是不是在每种情况下使用的最谨慎的方法。
I did a bit of searching and found that if one is being pedantic then yes it might be considered useless...in particular situations...it depends on how time sensitive your requirements are...
Check out this quote from the Java Sun site:
Java also has a caveat for the nanoTime() method:
It would seem that the only conclusion that can be drawn is that nanoTime() cannot be relied upon as an accurate value. As such, if you do not need to measure times that are mere nano seconds apart then this method is good enough even if the resulting returned value is negative. However, if you're needing higher precision, they appear to recommend that you use JAVA RTS.
So to answer your question...no nanoTime() is not useless....its just not the most prudent method to use in every situation.
无需争论,只需使用来源即可。
在这里,SE 6 for Linux,做出你自己的结论:
No need to debate, just use the source.
Here, SE 6 for Linux, make your own conclusions:
Linux 会纠正 CPU 之间的差异,但 Windows 不会。 我建议您假设 System.nanoTime() 仅精确到 1 微秒左右。 获得更长计时的一个简单方法是调用 foo() 1000 次或更多次,并将时间除以 1000。
Linux corrects for discrepancies between CPUs, but Windows does not. I suggest you assume System.nanoTime() is only accurate to around 1 micro-second. A simple way to get a longer timing is to call foo() 1000 or more times and divide the time by 1000.
绝对不是没用。 计时爱好者正确地指出了多核问题,但在实际应用中,它通常比 currentTimeMillis() 好得多。
当计算帧刷新中的图形位置时,nanoTime() 会导致我的程序中的运动更加平滑。
而且我只在多核机器上进行测试。
Absolutely not useless. Timing aficionados correctly point out the multi-core problem, but in real-word applications it is often radically better than currentTimeMillis().
When calculating graphics positions in frame refreshes nanoTime() leads to MUCH smoother motion in my program.
And I only test on multi-core machines.
我发现使用 System.nanoTime() 报告的经过时间为负数。 需要明确的是,有问题的代码是:
变量“elapsedNanos”的值为负值。 (我确信中间调用也花费了不到 293 年的时间,这是存储在 long 中的 nano 的溢出点:)
这是在运行 AIX 的 IBM P690(多核)硬件上使用 IBM v1.5 JRE 64 位时发生的。 我只见过这个错误发生一次,所以它看起来非常罕见。 我不知道原因 - 是特定于硬件的问题,还是 JVM 缺陷 - 我不知道。 我也不知道 nanoTime() 一般的准确性的影响。
为了回答最初的问题,我不认为 nanoTime 是无用的 - 它提供亚毫秒计时,但存在实际(不仅仅是理论上)不准确的风险,您需要考虑到这一点。
I have seen a negative elapsed time reported from using System.nanoTime(). To be clear, the code in question is:
and variable 'elapsedNanos' had a negative value. (I'm positive that the intermediate call took less than 293 years as well, which is the overflow point for nanos stored in longs :)
This occurred using an IBM v1.5 JRE 64bit on IBM P690 (multi-core) hardware running AIX. I've only seen this error occur once, so it seems extremely rare. I do not know the cause - is it a hardware-specific issue, a JVM defect - I don't know. I also don't know the implications for the accuracy of nanoTime() in general.
To answer the original question, I don't think nanoTime is useless - it provides sub-millisecond timing, but there is an actual (not just theoretical) risk of it being inaccurate which you need to take into account.
这在运行 Windows XP 和 JRE 1.5.0_06 的 Core 2 Duo 上似乎不是问题。
在使用三个线程的测试中,我没有看到 System.nanoTime() 倒退。 处理器都很忙,线程偶尔会进入休眠状态以引发线程移动。
[编辑] 我猜测它只发生在物理上独立的处理器上,即同一芯片上的多个内核的计数器是同步的。
This doesn't seem to be a problem on a Core 2 Duo running Windows XP and JRE 1.5.0_06.
In a test with three threads I don't see System.nanoTime() going backwards. The processors are both busy, and threads go to sleep occasionally to provoke moving threads around.
[EDIT] I would guess that it only happens on physically separate processors, i.e. that the counters are synchronized for multiple cores on the same die.
不,不是...这仅取决于您的 CPU,请检查高精度事件计时器如何/为什么根据 CPU 的不同而对事物进行不同的处理。
基本上,阅读您的 Java 源代码并检查您的版本对该函数的作用,以及它是否适用于您将运行它的 CPU。
IBM 甚至建议您使用它来进行性能基准测试(2008 年帖子,但已更新)。
No, it's not... It just depends on your CPU, check High Precision Event Timer for how/why things are differently treated according to CPU.
Basically, read the source of your Java and check what your version does with the function, and if it works against the CPU you will be running it on.
IBM even suggests you use it for performance benchmarking (a 2008 post, but updated).
我链接到的内容本质上是相同的讨论,Peter Lawrey 提供了一个很好的答案。
为什么我使用 System.nanoTime( )?
许多人提到在 Java 中 System.nanoTime() 可能会返回负时间。 我为重复其他人已经说过的话表示歉意。
如果 System.nanoTime() 在执行时返回 coreID,那就太酷了。
I am linking to what essentially is the same discussion where Peter Lawrey is providing a good answer.
Why I get a negative elapsed time using System.nanoTime()?
Many people mentioned that in Java System.nanoTime() could return negative time. I for apologize for repeating what other people already said.
It'd be cool if System.nanoTime() returned coreID where it executed.
Java是跨平台的,而nanoTime是平台相关的。 如果您使用 Java - 时不要使用 nanoTime。 我发现这个函数在不同的 jvm 实现中存在真正的错误。
Java is crossplatform, and nanoTime is platform-dependent. If you use Java - when don't use nanoTime. I found real bugs across different jvm implementations with this function.
Java 5 文档还建议使用此方法来达到相同的目的。
Java 5 API 文档
The Java 5 documentation also recommends using this method for the same purpose.
Java 5 API Doc
此外,当您更改系统时钟时,
System.currentTimeMillies()
也会发生变化,而System.nanoTime()
则不会,因此后者测量持续时间更安全。Also,
System.currentTimeMillies()
changes when you change your systems clock, whileSystem.nanoTime()
doesn't, so the latter is safer to measure durations.nanoTime
对于计时来说极其不安全。 我在基本的素性测试算法上进行了尝试,对于相同的输入,它给出的答案实际上相隔一秒。 别用那种可笑的方法。 我需要比获取时间毫秒更准确和精确的东西,但又不像nanoTime
那么糟糕。nanoTime
is extremely insecure for timing. I tried it out on my basic primality testing algorithms and it gave answers which were literally one second apart for the same input. Don't use that ridiculous method. I need something that is more accurate and precise than get time millis, but not as bad asnanoTime
.