System.currentTimeMillis vs System.nanoTime


379

Doğruluk Vs. Hassas

Bilmek istediğim, oyunumda nesnemin konumlarını güncellerken System.currentTimeMillis () veya System.nanoTime () kullanmalı mıyım ? Hareketteki değişimleri son çağrıdan bu yana geçen süre ile doğru orantılıdır ve ben mümkün olduğunca kesin olmak istiyorum.

Farklı işletim sistemleri arasında bazı ciddi zaman çözünürlüğü sorunları olduğunu okudum (yani Windows 50ms çözünürlüğe sahipken Mac / Linux neredeyse 1 ms çözünürlüğe sahip?). Öncelikle uygulamalarımı pencerelerde çalıştırıyorum ve 50 ms çözünürlük oldukça yanlış görünüyor.

Listelediğim ikisinden daha iyi seçenekler var mı?

Herhangi bir öneriniz / yorumunuz var mı?


81
nanoTimecurrentTimeMillis'den önemli ölçüde daha doğrudur ancak nispeten pahalı bir çağrıdır. currentTimeMillis()Birkaç (5-6) cpu saatinde çalışır, nanoTime temeldeki mimariye bağlıdır ve 100+ cpu saati olabilir.
bestsss

10
Hepiniz Windows'un genellikle 1000 ms / 64 zaman dilimi ayrıntısına sahip olduğunu anlıyorsunuz, değil mi? Hangi 15.625 ms veya 15625000 nanosaniye!

7
Yüzlerce ekstra saat döngüsünün oyununuzu etkileyeceğini sanmıyorum ve takas muhtemelen buna değer olacaktır. Yöntemi, oyun güncellemesi başına yalnızca bir kez çağırmanız ve değeri mem olarak kaydetmeniz gerekir, bu nedenle çok fazla ek yük eklemez. Farklı platformların ayrıntı düzeyine gelince, hiçbir fikrim yok.
aglassman

9
Windows'un DEFAULT zaman dilimi ayrıntı düzeyi 1000ms / 64'tür. Bunu, yerel timeBeginPeriod API'sı aracılığıyla artırabilirsiniz. Modern bilgisayarlarda temel zamanlayıcıya ek olarak yüksek çözünürlüklü zamanlayıcılar da bulunur. Yüksek çözünürlüklü zamanlayıcılara QueryPerformanceCounter çağrısı ile erişilebilir.
Robin Davies

2
- @Gohan Bu makale işleyişiyle ilgili ayrıntılı anlatır System.currentTimeMillis(): pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.html
Attila TANYI

Yanıtlar:


320

Sadece geçen sürenin son derece hassas ölçümlerini arıyorsanız , kullanın System.nanoTime(). System.currentTimeMillis()dönemden bu yana milisaniye cinsinden mümkün olan en doğru süreyi System.nanoTime()size verir , ancak rastgele bir noktaya göre size nanosaniye-hassas bir süre verir.

Java Belgelerinden:

public static long nanoTime()

Mevcut en hassas sistem zamanlayıcısının nanosaniye cinsinden geçerli değerini döndürür.

Bu yöntem yalnızca geçen süreyi ölçmek için kullanılabilir ve başka herhangi bir sistem veya duvar saati zamanı kavramıyla ilgili değildir. Döndürülen değer, sabit ancak keyfi bir başlangıç zamanından beri nanosaniyeyi temsil eder (belki de gelecekte, bu nedenle değerler negatif olabilir). Bu yöntem nanosaniye hassasiyet sağlar, ancak mutlaka nanosaniye doğruluğu sağlamaz. Değerlerin ne sıklıkta değiştiği konusunda hiçbir garanti verilmez. Yaklaşık 292 yıldan (2 63 nanosaniye) daha uzun süren ardışık çağrılardaki farklılıklar, sayısal taşma nedeniyle geçen süreyi doğru bir şekilde hesaplamayacaktır.

Örneğin, bazı kodların ne kadar sürede yürütüleceğini ölçmek için:

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

Ayrıca bkz: JavaDoc System.nanoTime () ve JavaDoc System.currentTimeMillis () .


20
Doğruluk ve kesinlik arasındaki farkı bildiğinizden emin misiniz? Bir nanosaniye hassasiyetinin doğru olmasının hiçbir yolu yoktur.
mmcdole

6
Üzgünüm, kesin demek istedim. Terimi gevşek bir şekilde kullanıyordum, ancak bunun kafa karıştırıcı (ve kelimenin yanlış kullanımı) olduğuna katılıyorum.
dancavallaro

4
@dancavallaro, bilgi için teşekkürler. Eğer sakıncası yoksa, cevabınızı dokümanlardaki bir alıntıyı içerecek şekilde düzenledim ve bağlantıları düzelttim
mmcdole

135
Bu cevap nanoTime () seçiminde teknik olarak doğrudur, ancak son derece önemli bir nokta üzerinde tamamen parlar. nanoTime (), dokümanın dediği gibi hassas bir zamanlayıcıdır. currentTimeMillis () ZAMANLAYICI DEĞİL, "duvar saati". nanoTime () her zaman pozitif geçen zaman üretecektir, currentTimeMillis değişmez (örneğin tarihi değiştirirseniz, bir saniye saniye vurursanız) Bu, bazı sistem türleri için son derece önemli bir ayrımdır.
charstar

11
Kullanıcı değiştirme zamanı ve NTP senkronizasyonu elbette, ancak currentTimeMillisDST nedeniyle neden değişsin? DST anahtarı çağın kaç saniye geçtiğini değiştirmez. Bir "duvar saati" olabilir, ancak UTC'ye dayalı bir saattir. Saat dilimi ve DST ayarlarınıza göre yerel saatiniz için ne anlama geldiğini belirlemelisiniz (veya bunu sizin için yapmak için diğer Java yardımcı programlarını kullanmalısınız).
Gölge Adam

100

Kimse bundan bahsetmediği için…

System.nanoTime()Farklı mesaj dizileri arasındaki aramaların sonuçlarını karşılaştırmak güvenli değildir . İpliklerin olayları öngörülebilir bir sırada gerçekleşse bile, nanosaniye fark pozitif veya negatif olabilir.

System.currentTimeMillis() dişler arasında güvenle kullanılabilir.


4
SP2'ye kadar olan ve şu şekilde belirtilen Windows'ta: stackoverflow.com/questions/510462/…
Peter Schmitz

3
Her gün yeni bir şeyler öğreniyorsun. Yine de, geçmişte güvensiz olduğu göz önüne alındığında (kesinlikle iş parçacıkları arasında saçma okumalar döndürür), bu tür kullanım muhtemelen spec dışında hala ve bu yüzden muhtemelen kaçınılmalıdır.
gubby

4
@jgubby: Çok ilginç ... farklı Konular arasındaki System.nanoTime () çağrılarının sonuçlarını karşılaştırmak güvenli olmayan herhangi bir destek referansı ? Aşağıdaki bağlantılar görülmeye değer: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519418 docs.oracle.com/javase/7/docs/api/java/lang/…
user454322

15
Burada belirtilen açıklamaya bakıldığında: docs.oracle.com/javase/7/docs/api/java/lang/… , nanoTime'dan döndürülen değerler arasındaki fark, aynı oldukları sürece karşılaştırma için geçerli gibi görünüyor JVM -The values returned by this method become meaningful only when the difference between two such values, obtained within the same instance of a Java virtual machine, is computed.
Tuxdude

7
JavaDocnanoTime() şöyle diyor: Aynı kaynak bir Java sanal makinesinin örneğinde bu yöntemin tüm çağrıları tarafından kullanılır; diğer sanal makine örneklerinin farklı bir başlangıç ​​noktası kullanması muhtemeldir. diğer bir deyişle, iş parçacıkları arasında aynı şeyi döndürür.
Simon Forsberg

58

Arkadiy Güncellemesi : System.currentTimeMillis()Oracle Java 8'de Windows 7'de daha doğru davranış gözlemledim . Zaman 1 milisaniyelik bir hassasiyetle geri döndü. OpenJDK kaynak kodu değişmedi, bu yüzden daha iyi davranış neden ne bilmiyorum.


Sun'dan David Holmes, birkaç yıl önce Java zamanlama API'larına (özellikle System.currentTimeMillis()ve System.nanoTime()), hangisini ve nasıl dahili olarak çalıştıklarını kullanmak istediğinize çok ayrıntılı bir bakış sunan bir blog makalesi yayınladı .

Etkin Nokta VM'sinin İçinde: Saatler, Zamanlayıcılar ve Zamanlama Etkinlikleri - Bölüm I - Windows

Zamanlanmış bekleme parametresi olan API'ler için Windows tarafından Java tarafından kullanılan zamanlayıcının çok ilginç bir yönü, zamanlayıcının çözünürlüğünün diğer API çağrılarının yapılmış olabileceğine bağlı olarak değişebilmesidir - sistem çapında (sadece belirli bir süreçte değil) . Kullanmanın Thread.sleep()bu çözüm değişikliğine neden olacağı bir örnek gösteriyor .


1
@Arkadiy: Güncellemedeki ifade için bir kaynağınız var mı?
Lii

@Lii - Ne yazık ki hayır. Bu sorudaki kodu çalıştırmak benden geliyor: stackoverflow.com/questions/34090914/… . Kod, Java 7 ile 15ms hassasiyet ve Java 8 ile 1 ms hassasiyet üretir

2
CurrentTimeMillis'i OpenJDK'da hotspot / src / os / windows / vm / os_windows.cpp'de os :: javaTimeMillis'e kadar izledim ( hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/os/… ) . Hala GetSystemTimeAsFileTime gibi görünüyor, bu yüzden değişikliğin nereden geldiğini bilmiyorum. Ya da geçerliyse. Kullanmadan önce test edin.

davranıştaki değişiklik GetSystemTimeAsFileTimeXP vs 7'de nasıl çalıştığının değiştirilmesinin sonucudur . Daha fazla ayrıntı için buraya bakın (tl; dr, tüm sistem daha kesin zamanlama yöntemleri getirdiğinden daha kesinleşti).

11

System.nanoTime()eski JVM'lerde desteklenmez. Bu bir endişe ise,currentTimeMillis

Doğruluk konusunda neredeyse haklısınız. SOME Windows makinelerinde currentTimeMillis()yaklaşık 10ms (50ms değil) çözünürlüğe sahiptir. Neden olduğundan emin değilim, ancak bazı Windows makineleri Linux makineleri kadar doğrudur.

Ben kullandım GAGETimer ılımlı bir başarı ile geçmişte.


3
"eski JVM'ler" hangileri gibi? Java 1.2 falan?
Simon Forsberg

1
System.nanoTime () 2004'te Java 1.5'te tanıtıldı. Java 1.4, 2013'te desteği genişletti, bu nedenle System.nanoTime () 'a artık güvenilebildiğini ve bu sorunun artık güncel olmadığını söylemek oldukça güvenlidir.
Dave L.

9

Diğerlerinin de söylediği gibi currentTimeMillis, yaz saati uygulaması, kullanıcıların saat ayarlarını değiştirmesi, artık saniye sayısı ve internet saati senkronizasyonu nedeniyle değişen saattir. Uygulamanız monoton olarak artan geçen zaman değerlerine bağlıysa bunun yerine nanoTime'ı tercih edebilirsiniz.

Oyuncuların oyun oynarken zaman ayarlarıyla uğraşmayacaklarını ve belki de haklı olacağını düşünebilirsiniz. Ancak internet saati senkronizasyonu veya uzak masaüstü kullanıcıları nedeniyle meydana gelen aksaklıkları hafife almayın. NanoTime API'si bu tür kesintilere karşı bağışıktır.

Saat zamanını kullanmak istiyor ancak internet zaman senkronizasyonu nedeniyle süreksizliklerden kaçınmak istiyorsanız, Meinberg gibi bir NTP istemcisini düşünebilirsiniz; bu, saati periyodik olarak sıfırlamak yerine saat hızını sıfıra "ayarlayan" olabilir.

Ben kişisel deneyimlerime dayanarak söylüyorum. Geliştirdiğim bir hava uygulamasında, rastgele gerçekleşen rüzgar hızı ani yükseliyordum. Benim zaman tabanımın tipik bir PC'deki saat zamanının davranışıyla bozulduğunu fark etmem biraz zaman aldı. NanoTime'ı kullanmaya başladığımda tüm sorunlarım kayboldu. Tutarlılık (monotonluk) benim uygulama için ham hassasiyet veya mutlak doğruluktan daha önemliydi.


19
"currentTimeMillis, yaz saati uygulaması nedeniyle değişen saattir" ... bu ifadenin yanlış olduğundan emin olabilirsiniz. System.currentTimeMillis()1 Ocak 1970, Gece Yarısı olan Unix / Posix Dönemi'nden bu yana geçen süreyi (milisaniye cinsinden) rapor eder. UTC hiçbir zaman gün ışığından yararlanma için ayarlanmadığından, yerel saat dilimleri gün ışığından yararlanma tasarrufu için yerel saat eklediğinde veya dengelendiğinde bu değer dengelenmez. Dahası, Java Zaman Ölçeği günleri 86.400 saniyeye sahip olmaya devam edecek şekilde artık saniyeleri düzeltir.
scottb

5

Evet, bu tür bir hassasiyet gerekiyorsa kullanın System.nanoTime(), ancak Java 5+ JVM'ye ihtiyacınız olduğunu unutmayın.

XP sistemlerimde, aşağıdaki kodu kullanarak sistem süresinin en az 100 mikrosaniye 278 nanosaniye olarak bildirildiğini görüyorum :

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)); }
    }

4

Oyun grafikleri ve sorunsuz konum güncellemeleri için, System.nanoTime()yerine kullanın System.currentTimeMillis(). Bir oyunda currentTimeMillis () 'den nanoTime ()' a geçtim ve hareket düzgünlüğünde önemli bir görsel gelişme elde ettim.

Bir milisaniye zaten kesin olması gerektiği gibi görünse de, görsel olarak değildir. Gelişebilecek faktörler nanoTime()şunları içerir:

  • duvar saati çözünürlüğünün altında doğru piksel konumlandırma
  • isterseniz pikseller arasında kenar yumuşatma yeteneği
  • Windows duvar saati yanlışlığı
  • saat titremesi (duvar saatinin gerçekten ileri gittiğinde tutarsızlık)

Diğer cevapların önerdiği gibi, nanoTime tekrar tekrar çağrıldığında bir performans maliyetine sahiptir - bunu çerçeve başına sadece bir kez aramak ve tüm çerçeveyi hesaplamak için aynı değeri kullanmak en iyisidir.


1

Nanotime konusunda iyi bir deneyim yaşadım . Bir JNI kütüphanesi kullanarak, duvar saati süresini iki uzun (o saniyeden bu yana geçen saniye ve nanosaniye saniye) sağlar. Hem Windows hem de Linux için önceden derlenmiş JNI parçası ile kullanılabilir.


1

System.currentTimeMillis()bu yöntem sistemin gerçek zamanlı saat değişikliklerine duyarlı olduğundan, geçen süre için güvenli değildir. Kullanmalısınız System.nanoTime. Lütfen Java Sistemi yardımına bakın:

NanoTime yöntemi hakkında:

.. Bu yöntem nanosaniye hassasiyet sağlar, ancak mutlaka nanosaniye çözünürlük (yani değer ne sıklıkta değişir) - çözünürlüğün en azından currentTimeMillis () kadar iyi olması dışında hiçbir garanti verilmez.

System.currentTimeMillis()Geçen sürenizi kullanırsanız negatif olabilir (Geri <- geleceğe)


-3

burada bir şey nanoTime yönteminin tutarsızlığıdır. aynı giriş için çok tutarlı değerler vermez. currentTimeMillis performans ve tutarlılık açısından çok daha iyi yapar ve ayrıca nanoTime kadar kesin olmasa da daha düşük bir hata payına sahiptir. ve dolayısıyla değerinde daha fazla doğruluk. bu nedenle currentTimeMillis kullanmanızı öneririm


6
Diğer cevaplarda ve yorumlarda belirtildiği gibi currentTimeMillis, sistem saati değişikliklerine tabidir ve bu nedenle JVM'deki önceki bir olaydan bu yana geçen süreyi hesaplamak için kötü bir seçimdir.
düello işaretleri
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.