windows 为什么 System.nanoTime() 和 System.currentTimeMillis() 的漂移如此之快?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5839152/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why do System.nanoTime() and System.currentTimeMillis() drift apart so rapidly?
提问by Eddie
For diagnostic purposes, I want to be able to detect changes in the system time-of-day clock in a long-running server application. Since System.currentTimeMillis()
is based on wall clock time and System.nanoTime()
is based on a system timer that is independent(*) of wall clock time, I thought I could use changes in the difference between these values to detect system time changes.
出于诊断目的,我希望能够在长时间运行的服务器应用程序中检测系统时钟的变化。由于System.currentTimeMillis()
基于挂钟时间并且System.nanoTime()
基于独立于挂钟时间的系统计时器(*),我想我可以使用这些值之间差异的变化来检测系统时间的变化。
I wrote up a quick test app to see how stable the difference between these values is, and to my surprise the values diverge immediately for me at the level of several milliseconds per second. A few times I saw much faster divergences. This is on a Win7 64-bit desktop with Java 6. I haven't tried this test program below under Linux (or Solaris or MacOS) to see how it performs. For some runs of this app, the divergence is positive, for some runs it is negative. It appears to depend on what else the desktop is doing, but it's hard to say.
我编写了一个快速测试应用程序来查看这些值之间的差异有多稳定,令我惊讶的是,这些值以每秒几毫秒的水平对我来说立即发生了分歧。有几次我看到了更快的分歧。这是在带有 Java 6 的 Win7 64 位桌面上。我没有在 Linux(或 Solaris 或 MacOS)下尝试过下面的这个测试程序,看看它是如何执行的。对于此应用程序的某些运行,差异是正的,对于某些运行是负的。这似乎取决于桌面正在做什么,但很难说。
public class TimeTest {
private static final int ONE_MILLION = 1000000;
private static final int HALF_MILLION = 499999;
public static void main(String[] args) {
long start = System.nanoTime();
long base = System.currentTimeMillis() - (start / ONE_MILLION);
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Don't care if we're interrupted
}
long now = System.nanoTime();
long drift = System.currentTimeMillis() - (now / ONE_MILLION) - base;
long interval = (now - start + HALF_MILLION) / ONE_MILLION;
System.out.println("Clock drift " + drift + " ms after " + interval
+ " ms = " + (drift * 1000 / interval) + " ms/s");
}
}
}
Inaccuracies with the Thread.sleep()
time, as well as interruptions, should be entirely irrelevant to timer drift.
Thread.sleep()
时间不准确以及中断应该与计时器漂移完全无关。
Both of these Java "System" calls are intended for use as a measurement -- one to measure differences in wall clock time and the other to measure absolute intervals, so when the real-time-clock is not being changed, these values should change at very close to the same speed, right? Is this a bug or a weakness or a failure in Java? Is there something in the OS or hardware that prevents Java from being more accurate?
这两个 Java“系统”调用都旨在用作测量 - 一个用于测量挂钟时间的差异,另一个用于测量绝对间隔,因此当实时时钟未更改时,这些值应该更改以非常接近相同的速度,对吗?这是 Java 中的错误、弱点或失败吗?操作系统或硬件中是否存在阻止 Java 更准确的东西?
I fully expect some drift and jitter(**) between these independent measurements, but I expected well less than a minute per day of drift. 1 msec per second of drift, if monotonic, is almost 90 seconds! My worst-case observed drift was perhaps ten times that. Every time I run this program, I see drift on the very first measurement. So far, I have not run the program for more than about 30 minutes.
我完全预计这些独立测量之间会有一些漂移和抖动 (**),但我预计每天的漂移不到一分钟。每秒 1 毫秒的漂移,如果是单调的,几乎是 90 秒!我观察到的最坏情况下的漂移可能是它的十倍。每次我运行这个程序时,我都会在第一次测量时看到漂移。到目前为止,我还没有运行该程序超过 30 分钟。
I expect to see some small randomness in the values printed, due to jitter, but in almost all runs of the program I see steady increase of the difference, often as much as 3 msec per second of increase and a couple times much more than that.
由于抖动,我希望在打印的值中看到一些小的随机性,但在程序的几乎所有运行中,我看到差异稳步增加,通常每秒增加 3 毫秒,比这多几倍.
Does any version of Windows have a mechanism similar to Linux that adjusts the system clock speed to slowly bring the time-of-day clock into sync with the external clock source? Would such a thing influence both timers, or only the wall-clock timer?
是否有任何版本的 Windows 具有类似于 Linux 的机制,可以调整系统时钟速度以缓慢地使时钟与外部时钟源同步?这样的事情会影响两个计时器,还是只影响挂钟计时器?
(*) I understand that on some architectures, System.nanoTime()
will of necessity use the same mechanism as System.currentTimeMillis()
. I also believe it's fair to assume that any modern Windows server is not such a hardware architecture. Is this a bad assumption?
(*) 我知道在某些架构上,System.nanoTime()
必须使用与System.currentTimeMillis()
. 我也相信可以公平地假设任何现代 Windows 服务器都不是这样的硬件架构。这是一个糟糕的假设吗?
(**) Of course, System.currentTimeMillis()
will usually have a much larger jitter than System.nanoTime()
since its granularity is not 1 msec on most systems.
(**) 当然,System.currentTimeMillis()
抖动通常会比System.nanoTime()
大多数系统上的粒度不是 1 毫秒大得多。
采纳答案by QuantumMechanic
You might find this Sun/Oracle blog post about JVM timersto be of interest.
您可能会发现这篇有关 JVM 计时器的 Sun/Oracle 博客文章很有趣。
Here are a couple of the paragraphs from that article about JVM timers under Windows:
以下是该文章中关于 Windows 下 JVM 计时器的几段:
System.currentTimeMillis()
is implemented using theGetSystemTimeAsFileTime
method, which essentially just reads the low resolution time-of-day value that Windows maintains. Reading this global variable is naturally very quick - around 6 cycles according to reported information. This time-of-day value is updated at a constant rate regardless of how the timer interrupt has been programmed - depending on the platform this will either be 10ms or 15ms (this value seems tied to the default interrupt period).
System.nanoTime()
is implemented using theQueryPerformanceCounter
/QueryPerformanceFrequency
API (if available, else it returnscurrentTimeMillis*10^6
).QueryPerformanceCounter
(QPC) is implemented in different ways depending on the hardware it's running on. Typically it will use either the programmable-interval-timer (PIT), or the ACPI power management timer (PMT), or the CPU-level timestamp-counter (TSC). Accessing the PIT/PMT requires execution of slow I/O port instructions and as a result the execution time for QPC is in the order of microseconds. In contrast reading the TSC is on the order of 100 clock cycles (to read the TSC from the chip and convert it to a time value based on the operating frequency). You can tell if your system uses the ACPI PMT by checking if QueryPerformanceFrequency returns the signature value of 3,579,545 (ie 3.57MHz). If you see a value around 1.19Mhz then your system is using the old 8245 PIT chip. Otherwise you should see a value approximately that of your CPU frequency (modulo any speed throttling or power-management that might be in effect.)
System.currentTimeMillis()
是使用该GetSystemTimeAsFileTime
方法实现的,该方法本质上只是读取 Windows 维护的低分辨率时间值。读取这个全局变量自然是非常快的——根据报告的信息大约需要 6 个周期。无论定时器中断是如何编程的,这个时间值都会以恒定的速率更新——取决于平台,这将是 10 毫秒或 15 毫秒(这个值似乎与默认中断周期相关联)。
System.nanoTime()
使用QueryPerformanceCounter
/QueryPerformanceFrequency
API 实现(如果可用,则返回currentTimeMillis*10^6
)。QueryPerformanceCounter
(QPC) 以不同的方式实现,具体取决于它运行的硬件。通常,它将使用可编程间隔计时器 (PIT)、ACPI 电源管理计时器 (PMT) 或 CPU 级时间戳计数器 (TSC)。访问 PIT/PMT 需要执行慢速 I/O 端口指令,因此 QPC 的执行时间为微秒级。相比之下,读取 TSC 大约需要 100 个时钟周期(从芯片读取 TSC 并将其转换为基于工作频率的时间值)。您可以通过检查 QueryPerformanceFrequency 是否返回签名值 3,579,545(即 3.57MHz)来判断您的系统是否使用 ACPI PMT。如果您看到大约 1.19Mhz 的值,那么您的系统正在使用旧的 8245 PIT 芯片。
回答by Peter Schaeffer
I am not sure how much this will actually help. But this is an area of active change in the Windows/Intel/AMD/Java world. The need for accurate and precise time measurement has been apparent for several (at least 10) years. Both Intel and AMD have responded by changing how TSC works. Both companies now have something called Invariant-TSCand/or Constant-TSC.
我不确定这实际上有多大帮助。但这是 Windows/Intel/AMD/Java 世界中一个正在发生积极变化的领域。数年(至少 10 年)以来,对准确和精确时间测量的需求一直很明显。英特尔和 AMD 都通过改变 TSC 的工作方式做出回应。两家公司现在都有称为Invariant-TSC和/或Constant-TSC 的东西。
Check out rdtsc accuracy across CPU cores. Quoting from osgx (who refers to an Intel manual).
检查跨 CPU 内核的 rdtsc 准确性。引用 osgx (他指的是英特尔手册)。
"16.11.1 Invariant TSC
"16.11.1 不变的 TSC
The time stamp counter in newer processors may support an enhancement, referred to as invariant TSC. Processor's support for invariant TSC is indicated by PUID.80000007H:EDX[8].
较新处理器中的时间戳计数器可能支持增强功能,称为不变 TSC。处理器对不变 TSC 的支持由 PUID.80000007H:EDX[8] 指示。
The invariant TSC will run at a constant rate in all ACPI P-, C-. and T-states. This is the architectural behavior moving forward. On processors with invariant TSC support, the OS may use the TSC for wall clock timer services (instead of ACPI or HPET timers). TSC reads are much more efficient and do not incur the overhead associated with a ring transition or access to a platform resource."
不变 TSC 将在所有 ACPI P-、C- 中以恒定速率运行。和 T 状态。这是向前发展的架构行为。在具有不变 TSC 支持的处理器上,操作系统可以将 TSC 用于挂钟定时器服务(而不是 ACPI 或 HPET 定时器)。TSC 读取效率更高,并且不会产生与环转换或访问平台资源相关的开销。”
See also http://www.citihub.com/requesting-timestamp-in-applications/. Quoting from the author
另请参阅http://www.citihub.com/requesting-timestamp-in-applications/。引用作者的话
For AMD:
对于 AMD:
If CPUID 8000_0007.edx[8] = 1, then the TSC rate is ensured to be invariant across all P-States, C-States, and stop-grant transitions (such as STPCLK Throttling); therefore, the TSC is suitable for use as a source of time.
如果 CPUID 8000_0007.edx[8] = 1,则确保 TSC 速率在所有 P-State、C-State 和停止-授权转换(例如 STPCLK 节流)中保持不变;因此,TSC 适合用作时间源。
For Intel:
对于英特尔:
Processor's support for invariant TSC is indicated by CPUID.80000007H:EDX[8]. The invariant TSC will run at a constant rate in all ACPI P-, C-. and T-states. This is the architectural behaviour moving forward. On processors with invariant TSC support, the OS may use the TSC for wall clock timer services (instead of ACPI or HPET timers). TSC reads are much more efficient and do not incur the overhead associated with a ring transition or access to a platform resource."
处理器对不变 TSC 的支持由 CPUID.80000007H:EDX[8] 指示。不变 TSC 将在所有 ACPI P-、C- 中以恒定速率运行。和 T 状态。这是向前发展的架构行为。在具有不变 TSC 支持的处理器上,操作系统可以将 TSC 用于挂钟定时器服务(而不是 ACPI 或 HPET 定时器)。TSC 读取效率更高,并且不会产生与环转换或访问平台资源相关的开销。”
Now the really important point is that the latest JVMs appear to exploit the newly reliable TSC mechanisms. There isn't much online to show this. However, do take a look at http://code.google.com/p/disruptor/wiki/PerformanceResults.
现在真正重要的一点是,最新的 JVM 似乎利用了新的可靠 TSC 机制。网上没有太多显示这一点。但是,请查看http://code.google.com/p/disruptor/wiki/PerformanceResults。
"To measure latency we take the three stage pipeline and generate events at less than saturation. This is achieved by waiting 1 microsecond after injecting an event before injecting the next and repeating 50 million times. To time at this level of precision it is necessary to use time stamp counters from the CPU. We choose CPUs with an invariant TSC because older processors suffer from changing frequency due to power saving and sleep states. Intel Nehalem and later processors use an invariant TSC which can be accessed by the latest Oracle JVMs running on Ubuntu 11.04. No CPU binding has been employed for this test"
“为了测量延迟,我们采用三级管道并在低于饱和度的情况下生成事件。这是通过在注入事件后等待 1 微秒,然后再注入下一个并重复 5000 万次来实现的。要以这种精度水平计时,有必要使用来自 CPU 的时间戳计数器。我们选择具有不变 TSC 的 CPU,因为较旧的处理器因省电和睡眠状态而遭受频率变化的困扰。Intel Nehalem 和更高版本的处理器使用不变 TSC,运行在其上的最新 Oracle JVM 可以访问它Ubuntu 11.04。此测试未使用 CPU 绑定”
Note that the authors of the "Disruptor" have close ties to the folks working on the Azul and other JVMs.
请注意,“Disruptor”的作者与在 Azul 和其他 JVM 上工作的人有着密切的联系。
See also "Java Flight Records Behind the Scenes". This presentation mentions the new invariant TSC instructions.
另请参阅“幕后 Java 飞行记录”。本演示文稿提到了新的不变 TSC 指令。
回答by Florian Enner
Does any version of Windows have a mechanism similar to Linux that adjusts the system clock speed to slowly bring the time-of-day clock into sync with the external clock source? Would such a thing influence both timers, or only the wall-clock timer?
是否有任何版本的 Windows 具有类似于 Linux 的机制,可以调整系统时钟速度以缓慢地使时钟与外部时钟源同步?这样的事情会影响两个计时器,还是只影响挂钟计时器?
The Windows Timestamp Projectdoes what you are asking for. As far as I know it only affects the wall-clock timer.
该视窗时间戳项目做你所要求的。据我所知,它只影响挂钟计时器。
回答by Hot Licks
"Returns the current value of the most precise available system timer, in nanoseconds.
“以纳秒为单位返回最精确的可用系统计时器的当前值。
"This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees are made about how frequently values change. Differences in successive calls that span greater than approximately 292 years (2**63 nanoseconds) will not accurately compute elapsed time due to numerical overflow."
“此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关。返回的值表示自某个固定但任意的时间以来的纳秒(可能在未来,因此值可能为负) . 此方法提供纳秒精度,但不一定提供纳秒精度。不保证值的变化频率。跨越大约 292 年(2**63 纳秒)的连续调用中的差异将无法准确计算由于数值而导致的经过时间溢出。”
Note that it says "precise", not "accurate".
请注意,它说的是“精确”,而不是“准确”。
It's not a "bug in Java" or a "bug" in anything. It's a definition. The JVM developers look around to find the fastest clock/timer in the system and use it. If that's in lock-step with the system clock then good, but if it's not, that's just the way the cookie crumbles. It's entirely plausible, say, that a computer system will have an accurate system clock but then have a higher-rate timer internally that's tied to the CPU clock rate or some such. Since clock rate is often varied to minimize power consumption, the increment rate of this internal timer would vary.
它不是“Java 中的错误”或任何事物中的“错误”。这是一个定义。JVM 开发人员四处寻找系统中最快的时钟/计时器并使用它。如果这与系统时钟同步,那很好,但如果不是,这就是 cookie 崩溃的方式。比方说,计算机系统有一个准确的系统时钟,但内部有一个与 CPU 时钟频率或类似频率相关的更高速率的计时器,这是完全合理的。由于时钟频率经常变化以最小化功耗,因此该内部定时器的增量速率会变化。
回答by Arno
System.currentTimeMillis()
and System.nanoTime()
are not necessarily provided by
the same hardware. System.currentTimeMillis()
, backed by GetSystemTimeAsFileTime()
has 100ns resolution elements. Its source is the system timer. System.nanoTime()
is backed by the system's high performance counter. There is a whole variety of different hardware
providing this counter. Therefore its resolution varies, depending on the underlying hardware.
System.currentTimeMillis()
并且System.nanoTime()
不一定由相同的硬件提供。System.currentTimeMillis()
, 支持GetSystemTimeAsFileTime()
具有 100ns 分辨率元素。它的来源是系统定时器。System.nanoTime()
由系统的高性能计数器支持。有多种不同的硬件提供此计数器。因此,其分辨率因底层硬件而异。
In no case can it be assumed that these two sources are in phase. Measuring the two values
against each other will disclose a different running speed. If the update of System.currentTimeMillis()
is taken as the real progress in time, the output of System.nanoTime()
may be sometimes slower, sometimes faster, and also varying.
在任何情况下都不能假设这两个源是同相的。相互测量这两个值将揭示不同的运行速度。如果把of的更新System.currentTimeMillis()
当做真正的时间进度,输出的System.nanoTime()
可能有时慢,有时快,也有变化。
A careful calibration has to be done in order to phase lock these two time sources.
为了锁相这两个时间源,必须进行仔细的校准。
A more detailed description of the relation between these two time sources can be found at the Windows Timestamp Project.
可以在Windows Timestamp Project 中找到关于这两个时间源之间关系的更详细说明。