簡體   English   中英

System.currentTimeMillis 與 System.nanoTime

[英]System.currentTimeMillis vs System.nanoTime

精度對比精確

我想知道的是,在我的游戲中更新對象的位置時,我應該使用System.currentTimeMillis()還是System.nanoTime() 他們的運動變化與自上次通話以來經過的時間成正比,我希望盡可能精確。

我讀過不同操作系統之間存在一些嚴重的時間分辨率問題(即 Mac/Linux 的分辨率接近 1 毫秒,而 Windows 的分辨率為 50 毫秒??)。 我主要是在 Windows 上運行我的應用程序,50 毫秒的分辨率似乎很不准確。

還有比我列出的兩個更好的選擇嗎?

任何建議/意見?

如果您只是在尋找經過時間的極其精確的測量,請使用System.nanoTime() System.currentTimeMillis()將為您提供自紀元以來最准確的可能經過的時間,以毫秒為單位,但System.nanoTime()為您提供相對於某個任意點的納秒級精確時間。

從 Java 文檔:

 public static long nanoTime()

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

此方法只能用於測量經過的時間,與系統或掛鍾時間的任何其他概念無關。 返回的值表示自某個固定但任意的原始時間以來的納秒(可能在未來,因此值可能為負)。 此方法提供納秒精度,但不一定提供納秒精度。 不保證值的更改頻率。 由於數值溢出,跨越大約 292 年(2 63納秒)的連續調用之間的差異將無法准確計算經過的時間。

例如,要測量執行某些代碼所需的時間:

long startTime = System.nanoTime();    
// ... the code being measured ...    
long estimatedTime = System.nanoTime() - startTime;

另請參閱: JavaDoc System.nanoTime()JavaDoc System.currentTimeMillis()了解更多信息。

由於沒有其他人提到這一點......

在不同的 JVM 之間比較System.nanoTime()調用的結果是不安全的,每個 JVM 可能有一個獨立的“起源”時間。

System.currentTimeMillis()將在 JVM 之間返回(近似)相同的值,因為它與系統掛鍾時間相關。

如果要計算兩個事件之間經過的時間量,例如秒表,請使用nanoTime() 系統掛鍾的變化使currentTimeMillis()對於這個用例不正確。

Arkadiy更新:我在 Oracle Java 8 的 Windows 7 上觀察到System.currentTimeMillis()更正確行為。時間以 1 毫秒的精度返回。 OpenJDK 中的源代碼沒有改變,所以我不知道是什么導致了更好的行為。


Sun 的 David Holmes 幾年前發表了一篇博客文章,其中非常詳細地介紹了 Java 計時 API(特別是System.currentTimeMillis()System.nanoTime() )、何時需要使用哪個以及它們如何使用在內部工作。

Hotspot VM 內部:時鍾、定時器和調度事件 - 第一部分 - Windows

Java 在 Windows 上為具有定時等待參數的 API 使用的計時器的一個非常有趣的方面是計時器的分辨率可以根據可能進行的其他 API 調用而改變 - 系統范圍內(不僅僅是在特定進程中) . 他展示了一個示例,其中使用Thread.sleep()將導致此分辨率更改。

正如其他人所說, currentTimeMillis 是時鍾時間,它會因夏令時(不是:夏令時和時區與 currentTimeMillis 無關,其余為 true )、用戶更改時間設置、閏秒和互聯網時間同步而變化。 如果您的應用依賴於單調遞增的經過時間值,您可能更喜歡 nanoTime。

您可能認為玩家在玩游戲時不會擺弄時間設置,也許您是對的。 但不要低估互聯網時間同步或遠程桌面用戶造成的中斷。 nanoTime API 不受這種中斷的影響。

如果您想使用時鍾時間,但要避免因 Internet 時間同步而導致的中斷,您可以考慮使用 NTP 客戶端,例如 Meinberg,它“調整”時鍾頻率以將其歸零,而不僅僅是定期重置時鍾。

我從個人經歷說起。 在我開發的天氣應用程序中,我得到了隨機發生的風速峰值。 我花了一段時間才意識到我的時基被典型 PC 上的時鍾時間行為打亂了。 當我開始使用 nanoTime 時,我所有的問題都消失了。 對我的應用來說,一致性(單調性)比原始精度或絕對精度更重要。

較舊的 JVM 不支持System.nanoTime() 如果這是一個問題,請堅持使用currentTimeMillis

關於准確性,您幾乎是正確的。 在某些 Windows 機器上, currentTimeMillis()的分辨率約為 10 毫秒(不是 50 毫秒)。 我不知道為什么,但有些 Windows 機器和 Linux 機器一樣准確。

我過去使用過GAGETimer並取得了一定的成功。

是的,如果需要這樣的精度,請使用System.nanoTime() ,但請注意,您需要 Java 5+ JVM。

在我的 XP 系統上,我看到使用以下代碼報告的系統時間至少為 100 微秒 278納秒

private void test() {
    System.out.println("currentTimeMillis: "+System.currentTimeMillis());
    System.out.println("nanoTime         : "+System.nanoTime());
    System.out.println();

    testNano(false);                                                            // to sync with currentTimeMillis() timer tick
    for(int xa=0; xa<10; xa++) {
        testNano(true);
        }
    }

private void testNano(boolean shw) {
    long strMS=System.currentTimeMillis();
    long strNS=System.nanoTime();
    long curMS;
    while((curMS=System.currentTimeMillis()) == strMS) {
        if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)); }
        }
    if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)+", Milli: "+(curMS-strMS)); }
    }

對於游戲圖形和平滑的位置更新,請使用System.nanoTime()而不是System.currentTimeMillis() 我在游戲中從 currentTimeMillis() 切換到 nanoTime() 並在運動平滑度方面獲得了重大的視覺改進。

雖然一毫秒看起來應該已經很精確了,但在視覺上卻並非如此。 nanoTime()可以改進的因素包括:

  • 低於掛鍾分辨率的精確像素定位
  • 如果需要,可以在像素之間消除鋸齒
  • Windows 掛鍾不准確
  • 時鍾抖動(掛鍾實際向前滴答的時間不一致)

正如其他答案所暗示的那樣,如果重復調用 nanoTime 確實會產生性能成本 - 最好每幀只調用一次,並使用相同的值來計算整個幀。

System.currentTimeMillis()對於經過的時間是不安全的,因為該方法對系統的系統實時時鍾變化很敏感。 您應該使用System.nanoTime 請參考Java系統幫助:

關於納米時間方法:

.. 此方法提供納秒精度,但不一定提供納秒分辨率(即值更改的頻率)- 除了分辨率至少與 currentTimeMillis() 一樣好外,不做任何保證。

如果您使用System.currentTimeMillis()您的經過時間可能是負數(回到 <-- 未來)

我在nanotime 方面有很好的經驗。 它使用 JNI 庫將掛鍾時間提供為兩個長(自紀元以來的秒數和該秒內的納秒數)。 它可用於為 Windows 和 Linux 預編譯的 JNI 部分。

這里的一件事是 nanoTime 方法的不一致。它不會為相同的輸入提供非常一致的值。currentTimeMillis 在性能和一致性方面做得更好,而且雖然不如 nanoTime 精確,但誤差幅度較小,因此其值更准確。 因此,我建議您使用 currentTimeMillis

暫無
暫無

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

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