簡體   English   中英

為什么System.nanoTime()和System.currentTimeMillis()如此迅速地分開?

[英]Why do System.nanoTime() and System.currentTimeMillis() drift apart so rapidly?

出於診斷目的,我希望能夠在長時間運行的服務器應用程序中檢測系統時鍾的變化。 由於System.currentTimeMillis()基於掛鍾時間而System.nanoTime()是基於掛鍾時間獨立(*)的系統計時器,我以為我可以使用這些值之間的差異來檢測系統時間變化。

我寫了一個快速測試應用程序,看看這些值之間的差異是多么穩定,令我驚訝的是,這些值在我每秒幾毫秒的水平上立刻發散。 有幾次我看到了更快的分歧。 這是在帶有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");
    }
  }
}

Thread.sleep()時間的不准確以及中斷應該與定時器漂移完全無關。

這兩個Java“系統”調用都用作測量 - 一個用於測量掛鍾時間的差異,另一個用於測量絕對間隔,因此當實時時鍾未被更改時,這些值應該更改在非常接近相同的速度,對吧? 這是Java中的錯誤或弱點還是失敗? 操作系統或硬件中是否存在阻止Java更准確的內容?

我完全期望這些獨立測量之間存在一些漂移和抖動(**),但我預計每天漂移不到一分鍾。 每秒1毫秒的漂移,如果是單調的,幾乎是90秒! 我觀察到的最壞情況可能是十倍。 每次我運行這個程序時,我都會看到第一次測量的偏差。 到目前為止,我還沒有運行該程序超過30分鍾。

由於抖動,我希望在打印的值中看到一些小的隨機性,但在程序的幾乎所有運行中,我看到差異的穩定增加,通常高達每秒3毫秒的增加,並且遠遠超過每秒3毫秒。 。

任何版本的Windows都有類似於Linux的機制,可以調整系統時鍾速度,以便慢慢使時鍾與外部時鍾源同步嗎? 這樣的事情會影響定時器,還是只影響掛鍾定時器?

(*)據我所知,在某些體系結構中, System.nanoTime()必然會使用與System.currentTimeMillis()相同的機制。 我也相信,假設任何現代Windows服務器都不是這樣的硬件架構是公平的。 這是一個不好的假設嗎?

(**)當然, System.currentTimeMillis()通常會有比System.nanoTime()更大的抖動,因為在大多數系統上它的粒度不是1毫秒。

您可能會發現有關JVM計時器的Sun / Oracle博客文章很有用。

以下是該文章中有關Windows下JVM計時器的幾個段落:

System.currentTimeMillis()是使用GetSystemTimeAsFileTime方法實現的,該方法基本上只讀取Windows維護的低分辨率時間值。 根據報告的信息,讀取這個全局變量自然非常快 - 大約6個周期。 無論定時器中斷如何編程,這個時間值都以恆定速率更新 - 取決於平台,這將是10ms或15ms(此值似乎與默認中斷周期相關)。

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芯片。 否則,您應該看到一個大約相當於CPU頻率的值(模數可能有效的任何速度限制或電源管理。)

我不確定這實際上有多大幫助。 但這是Windows / Intel / AMD / Java世界中一個積極變化的領域。 在幾個(至少10年)內,對准確和精確的時間測量的需求已經很明顯。 英特爾和AMD都通過改變TSC的工作方式做出了回應。 兩家公司現在都有一種叫做Invariant-TSC和/或Constant-TSC的東西

檢查CPU內核的rdtsc精度 引用osgx(誰是指英特爾手冊)。

“16.11.1不變的TSC

較新處理器中的時間戳計數器可以支持增強,稱為不變TSC。 處理器對不變TSC的支持由PUID.80000007H:EDX [8]指示。

不變的TSC將在所有ACPI P-,C-中以恆定速率運行。 和T狀態。 這是向前發展的建築行為。 在具有不變TSC支持的處理器上,OS可以將TSC用於掛鍾計時器服務(而不是ACPI或HPET計時器)。 TSC讀取效率更高,並且不會產生與環轉換或訪問平台資源相關的開銷。“

另見http://www.citihub.com/requesting-timestamp-in-applications/ 引自作者

對於AMD:

如果CPUID 8000_0007.edx [8] = 1,則確保TSC速率在所有P狀態,C狀態和停止授權轉換(例如STPCLK限制)之間保持不變; 因此,TSC適合用作時間源。

對於英特爾:

處理器對不變TSC的支持由CPUID.80000007H:EDX [8]指示。 不變的TSC將在所有ACPI P-,C-中以恆定速率運行。 和T狀態。 這是向前發展的建築行為。 在具有不變TSC支持的處理器上,OS可以將TSC用於掛鍾計時器服務(而不是ACPI或HPET計時器)。 TSC讀取效率更高,並且不會產生與環轉換或訪問平台資源相關的開銷。“

現在非常重要的一點是,最新的JVM似乎利用了新的可靠TSC機制。 沒有多少在線顯示這一點。 不過,請查看http://code.google.com/p/disruptor/wiki/PerformanceResults

“為了測量延遲,我們采用三級流水線並生成低於飽和度的事件。這是通過在注入下一個事件並重復注入5000萬次之前等待1微秒來實現的。要達到這個精度水平,有必要使用來自CPU的時間戳計數器。我們選擇具有不變TSC的CPU,因為較舊的處理器由於省電和睡眠狀態而受到頻率變化的影響.Intel Nehalem和后來的處理器使用不變的TSC,可以通過運行的最新Oracle JVM訪問Ubuntu 11.04。此測試沒有使用CPU綁定“

請注意,“Disruptor”的作者與處理Azul和其他JVM的人有密切聯系。

另請參閱“幕后的Java飛行記錄”。 本演示文稿提到了新的不變TSC指令。

System.currentTimeMillis()System.nanoTime()不一定由同一硬件提供。 GetSystemTimeAsFileTime()支持的System.currentTimeMillis()具有100ns分辨率元素。 它的來源是系統計時器。 System.nanoTime()由系統的高性能計數器支持。 提供此計數器有各種不同的硬件。 因此,其分辨率因底層硬件而異。

在任何情況下都不能假設這兩個源是同相的。 相互測量兩個值將公開不同的運行速度。 如果將System.currentTimeMillis()的更新視為真正的時間進度,則System.nanoTime()的輸出有時可能更慢,有時更快,也會變化。

必須進行仔細校准才能鎖定這兩個時間源。

可以在Windows時間戳項目中找到有關這兩個時間源之間關系的更詳細說明。

任何版本的Windows都有類似於Linux的機制,可以調整系統時鍾速度,以便慢慢使時鍾與外部時鍾源同步嗎? 這樣的事情會影響定時器,還是只影響掛鍾定時器?

Windows時間戳項目可以滿足您的要求。 據我所知,它只會影響掛鍾計時器。

“返回最精確的可用系統計時器的當前值,以納秒為單位。

“此方法只能用於測量經過的時間,與系統或掛鍾時間的任何其他概念無關。返回的值表示納秒,因為某些固定但是任意時間(可能在將來,因此值可能為負)這種方法提供納秒精度,但不一定是納秒精度。不保證值的變化頻率。連續調用的差異超過大約292年(2 ** 63納秒)將無法准確計算由於數值而導致的經過時間溢出。”

請注意,它表示“精確”,而不是“准確”。

它不是“Java中的bug”或任何“bug”。 這是一個定義。 JVM開發人員四處尋找系統中最快的時鍾/定時器並使用它。 如果那與系統時鍾鎖定步驟那么好,但如果不是,那就是cookie崩潰的方式。 比如說,計算機系統將具有准確的系統時鍾,但內部具有更高速率的定時器,這與CPU時鍾速率或某些此類速率相關,這完全合情合理。 由於時鍾頻率通常是變化的以使功耗最小化,因此該內部定時器的增量速率會變化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM