Windows上實現精確到毫秒的時鐘所遇到的問題與解決方案

來源:互聯網
上載者:User

問題1: 15ms 內多次調用 GetSystemTime / GetLocalTime(java 中對應的函數為 System.currentTimeMillis()), 返回相同的值

解決辦法, 使用GetSystemTime 作為baseline, 然後用windows 提供的高精度計時器 QueryPerformanceCounter(java 中相應的函數為 System.nanoTime()) 做計時,精確時鐘為baseline + 計時器時間。

問題2: QueryPerformanceCounter/QueryPerformanceFrequency 的問題

這個問題主要取決與windows QueryPerformanceCounter 的實現, 早期的實現是使用 the CPU-level timestamp-counter (TSC), 由於tick count在不同的核上的值差別比較大, 所以使用這個計時器計算出來的值完全不可預測, 早期比較簡單的一種解決辦法是只在某個線程使用計時器, 並且把這個線程綁到一個核上去(但是java中線程無法設定cpu affinity)

在windows xp sp2 和 windows server 2003 sp2 以及以後的系統上, QueryPerformanceCounter 可以選擇使用power management timer PMTimer, 通過在boot.ini中添加 /usepmtimer 選項來設定QueryPerformanceCounter 的實現方式, 因為PMTimer使用的是主板的計時器, 所以沒有多核同步的問題。

 

總結:1. 檢查QueryPerformanceFrequency的傳回值(參數),

   傳回值=3,579,545,則當前系統使用PMTimer  --> step 3

   傳回值=CPU頻率, 則系統使用TSC  --> step 2

 

2. 如果作業系統為windows xp sp2 / windows server 2003 sp2 or later, 在c:\boot.ini 添加參數/usepmtimer, 並重啟系統

 

3. 使用GetSystemTime + QueryPerformanceCounter 實現精確時鐘references: 

---------------------------------------------------------------------------------

 

http://msdn.microsoft.com/zh-cn/windows/hardware/gg463347

 

http://support.microsoft.com/kb/833721

http://support.microsoft.com/kb/895980

http://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks

 

Windows use of clocks and timers varies considerably from platform to platform and is plagued by problems - again this isn't necessarily Window's fault, just as it wasn't the VM's fault: the hardware support for clocks/timers is actually not very good - the references at the end lead you to more information on the timing hardware available. The following relates to the "NT" family (win 2k, XP, 2003) of Windows.

There are a number of different "clock" API's available in Windows. Those used by Hotspot are as follows:

  • System.currentTimeMillis() is implemented using the GetSystemTimeAsFileTime 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 the QueryPerformanceCounter/QueryPerformanceFrequency API (if available, else it returns currentTimeMillis\*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.)

    The default mechanism used by QPC is determined by the Hardware Abstraction layer(HAL), but some systems allow you to explicitly control it using options in boot.ini, such as /usepmtimer that explicitly requests use of the power management timer. This default changes not only across hardware but also across OS versions. For example Windows XP Service Pack 2 changed things to use the power management timer (PMTimer) rather than the processor timestamp-counter (TSC) due to problems with the TSC not being synchronized on different processors in SMP systems, and due the fact its frequency can vary (and hence its relationship to elapsed time) based on power-management settings. (The issues with the TSC, in particular for AMD systems, and how AMD aims to provide a stable TSC in future processors is discussed in Rich Brunner's article referenced below. You can also read how the Linux kernel folk have abandoned use of the TSC until a new stable version appears in CPUs.)

The timer related API's for doing timed-waits all use the waitForMultipleObjects API as previously mentioned. This API only accepts timeout values in milliseconds and its ability to recognize the passage of time is based on the timer interrupt programmed through the hardware.

Typically a Windows machine has a default 10ms timer interrupt period, but some systems have a 15ms period. This timer interrupt period may be modified by application programs using the timeBeginPeriod/timeEndPeriod API's. The period is still limited to milliseconds and there is no guarantee that a requested period will be supported. However, usually you can request a 1ms timer interrupt period (though its accuracy has been questioned in some reports). The hotspot VM in fact uses this 1ms period to allow for higher resolution Thread.sleep calls than would otherwise be possible. The sample Sleeper.java will cause this higher interrupt rate to be used, thus allowing experimentation with a 1ms versus 10ms period. It simply calls Thread.sleep(Integer.MAX_VALUE) which (because it is not a multiple of 10ms) causes the VM to switch to a 1ms period for the duration of the sleep - which in this case is "forever" and you'll have to ctrl-C the "java Sleeper" execution.

 public class Sleeper {   public static void main(String[] args) throws Throwable {     Thread.sleep(Integer.MAX_VALUE);   } } 

You can see what interrupt period is being used in Windows by running the perfmon tool. After you bring it up you'll need to add a new item to watch (click the + icon above the graph - even if it appears grayed/disabled). Select the interrupts/sec items and add it. Then right click on interrupts/sec under the graph and edit its properties. On the "data" tab, change the "scale" to 1 and on the graph tab, the vertical max to be 1000. Let the system settle for a few seconds and you should see the graph drawing a steady line. If you have a 10ms interrupt then it will be 100, for 1ms it will be 1000, for 15ms it will be 66.6, etc. Note: on a multiprocessor system show the interrupts/sec for each processor individually, not the total - one processor will be fielding the timer interrupts.

Note that any application can change the timer interrupt and that it affects the whole system. Windows only allows the period to be shortened, thus ensuring that the shortest requested period by all applications is the one that is used. If a process doesn't reset the period then Windows takes care of it when the process terminates. The reason why the VM doesn't just arbitrarily change the interrupt rate when it starts - it could do this - is that there is a potential performance impact to everything on the system due to the 10x increase in interrupts. However other applications do change it, typically multi-media viewers/players. Be aware that a browser running the JVM as a plug-in can also cause this change in interrupt rate if there is an applet running that uses the Thread.sleep method in a similar way to Sleeper.

Further note, that after Windows suspends or hibernates, the timer interrupt is restored to the default, even if an application using a higher interrupt rate was running at the time of suspension/hibernation.

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.