System.currentTimeMillis vs System.nanoTime

379

Akurasi Vs. Presisi

Apa yang ingin saya ketahui adalah apakah saya harus menggunakan System.currentTimeMillis () atau System.nanoTime () ketika memperbarui posisi objek saya di game saya? Perubahan gerakan mereka berbanding lurus dengan waktu yang telah berlalu sejak panggilan terakhir dan saya ingin setepat mungkin.

Saya telah membaca bahwa ada beberapa masalah resolusi waktu yang serius antara sistem operasi yang berbeda (yaitu bahwa Mac / Linux memiliki resolusi hampir 1 ms sementara Windows memiliki resolusi 50 ms ??). Saya terutama menjalankan aplikasi saya di windows dan resolusi 50ms tampaknya cukup tidak akurat.

Apakah ada opsi yang lebih baik daripada dua yang saya daftarkan?

Ada saran / komentar?

mmcdole
sumber
81
nanoTimesecara signifikan biasanya lebih akurat daripada currentTimeMillis tetapi ini adalah panggilan yang relatif mahal juga. currentTimeMillis()berjalan dalam beberapa (5-6) jam cpu, nanoTime tergantung pada arsitektur yang mendasarinya dan bisa 100 jam cpu.
bestsss
10
Anda semua menyadari bahwa Windows umumnya memiliki granularity timeslice 1000ms / 64, kan? Yaitu 15,625ms, atau 15625000nanoseconds!
7
Saya tidak berpikir seratus siklus clock tambahan akan berdampak pada permainan Anda, dan trade off mungkin akan sia-sia. Anda hanya perlu memanggil metode sekali per pembaruan game kemudian menyimpan nilai dalam mem, sehingga tidak akan menambah banyak overhead. Adapun rincian platform yang berbeda, saya tidak tahu.
Aglassman
9
Windows memiliki rincian waktu kelumpuhan DEFAULT 1000ms / 64. Anda dapat meningkatkan ini melalui API timeBeginPeriod asli. PC modern juga memiliki timer resolusi tinggi selain timer dasar. Penghitung waktu resolusi tinggi dapat diakses melalui panggilan QueryPerformanceCounter.
Robin Davies
2
@Gohan - Artikel ini menjelaskan secara rinci tentang inner System.currentTimeMillis(): pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.html
Attila Tanyi

Jawaban:

320

Jika Anda hanya mencari pengukuran waktu berlalu yang sangat tepat , gunakan System.nanoTime(). System.currentTimeMillis()akan memberi Anda waktu berlalu yang paling akurat dalam milidetik sejak zaman itu, tetapi System.nanoTime()memberi Anda waktu yang tepat nanodetik, relatif terhadap beberapa titik yang berubah-ubah.

Dari Dokumentasi Java:

public static long nanoTime()

Mengembalikan nilai saat ini dari timer sistem yang tersedia paling tepat, dalam nanodetik.

Metode ini hanya dapat digunakan untuk mengukur waktu yang berlalu dan tidak terkait dengan gagasan lain tentang waktu sistem atau jam dinding. Nilai yang dikembalikan mewakili nanodetik sejak beberapa waktu asal tetap tetapi sewenang-wenang (mungkin di masa depan, sehingga nilai mungkin negatif). Metode ini memberikan ketepatan nanosecond, tetapi tidak harus memiliki ketepatan nanosecond. Tidak ada jaminan yang dibuat tentang seberapa sering nilai berubah. Perbedaan panggilan berurutan yang menjangkau lebih dari sekitar 292 tahun (2 63 nanodetik) tidak akan secara akurat menghitung waktu yang berlalu karena luapan numerik.

Misalnya, untuk mengukur berapa lama beberapa kode diperlukan untuk dieksekusi:

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

Lihat juga: JavaDoc System.nanoTime () dan JavaDoc System.currentTimeMillis () untuk info lebih lanjut.

dancavallaro
sumber
20
Apakah Anda yakin tahu perbedaan antara akurasi dan presisi? Tidak ada cara yang akurat untuk presisi nanodetik.
mmcdole
6
Maaf, maksud saya tepat. Saya menggunakan istilah ini secara longgar, tapi saya setuju itu membingungkan (dan penggunaan kata yang tidak tepat).
dancavallaro
4
@dancavallaro, terima kasih atas informasinya. Jika Anda tidak keberatan, saya mengedit jawaban Anda untuk menyertakan kutipan dari dokumen dan memperbaiki tautan
mmcdole
135
Jawaban ini secara teknis benar dalam memilih nanoTime () tetapi benar-benar menerangkan pada poin yang sangat penting. nanoTime (), seperti kata doc, adalah pengatur waktu yang presisi. currentTimeMillis () BUKAN TIMER, itu adalah "jam dinding". nanoTime () akan selalu menghasilkan waktu berlalu yang positif, currentTimeMillis tidak akan (misalnya jika Anda mengubah tanggal, mencapai satu detik kabisat, dll.) Ini adalah perbedaan yang sangat penting untuk beberapa jenis sistem.
charstar
11
Pengguna mengubah waktu dan sinkronisasi NTP, tetapi mengapa akan currentTimeMillisberubah karena DST? Sakelar DST tidak mengubah jumlah detik melewati zaman. Ini mungkin merupakan "jam dinding" tetapi ini didasarkan pada UTC. Anda harus menentukan berdasarkan zona waktu Anda dan pengaturan DST apa yang diterjemahkan untuk waktu lokal Anda (atau menggunakan utilitas Java lainnya untuk melakukannya untuk Anda).
Shadow Man
100

Karena tidak ada orang lain yang menyebutkan ini ...

Tidaklah aman untuk membandingkan hasil System.nanoTime()panggilan di antara berbagai utas. Bahkan jika peristiwa thread terjadi dalam urutan yang dapat diprediksi, perbedaan dalam nanodetik bisa positif atau negatif.

System.currentTimeMillis() aman untuk digunakan di antara utas.

gemuk
sumber
4
Pada Windows yang hanya berlaku hingga SP2 menurut: stackoverflow.com/questions/510462/...
Peter Schmitz
3
Nah, Anda belajar sesuatu yang baru setiap hari. Saya menduga, meskipun, mengingat itu tidak aman di masa lalu (pasti mengembalikan pembacaan omong kosong di utas), penggunaan seperti itu mungkin masih di luar spesifikasi dan karenanya mungkin harus dihindari.
gemuk
4
@ jgubby: Sangat menarik ... ada referensi untuk mendukung yang tidak aman untuk membandingkan hasil panggilan System.nanoTime () di antara berbagai Thread ? Tautan berikut ini layak untuk dilihat: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519418 docs.oracle.com/javase/7/docs/api/java/lang/…
user454322
15
Melihat deskripsi yang disebutkan di sini: docs.oracle.com/javase/7/docs/api/java/lang/… , sepertinya perbedaan antara nilai yang dikembalikan dari nanoTime valid untuk perbandingan selama mereka berasal dari sama 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
JavaDoc untuknanoTime() mengatakan: Asal yang sama digunakan oleh semua doa metode ini dalam mesin virtual Java; contoh mesin virtual lainnya cenderung menggunakan asal yang berbeda. yang berarti bahwa itu tidak kembali sama di thread.
Simon Forsberg
58

Pembaruan oleh Arkadiy : Saya telah mengamati perilaku yang lebih benar System.currentTimeMillis()pada Windows 7 di Oracle Java 8. Waktu dikembalikan dengan presisi 1 milidetik. Kode sumber di OpenJDK belum berubah, jadi saya tidak tahu apa yang menyebabkan perilaku yang lebih baik.


David Holmes dari Sun memposting artikel blog beberapa tahun yang lalu yang memiliki tampilan yang sangat rinci pada Java timing APIs (khususnya System.currentTimeMillis()dan System.nanoTime()), ketika Anda ingin menggunakan yang mana, dan bagaimana mereka bekerja secara internal.

Di dalam Hotspot VM: Jam, Timer dan Acara Penjadwalan - Bagian I - Windows

Satu aspek yang sangat menarik dari penghitung waktu yang digunakan oleh Java pada Windows untuk API yang memiliki parameter waktu tunggu adalah bahwa resolusi penghitung waktu dapat berubah tergantung pada apa panggilan API lain yang mungkin telah dibuat - sistem lebar (tidak hanya dalam proses tertentu) . Dia menunjukkan contoh di mana penggunaan Thread.sleep()akan menyebabkan resolusi ini berubah.

Michael Burr
sumber
1
@Arkadiy: Apakah Anda memiliki sumber untuk pernyataan dalam pembaruan?
Lii
@ Lii - Sayangnya, tidak. Itu datang dari saya menjalankan kode dalam pertanyaan ini: stackoverflow.com/questions/34090914/… . Kode menghasilkan ketelitian 15ms dengan Java 7 dan 1 ms presisi dengan Java 8
2
Saya melacak currentTimeMillis ke os :: javaTimeMillis di hotspot / src / os / windows / vm / os_windows.cpp di OpenJDK ( hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/os/… ) . Sepertinya masih GetSystemTimeAsFileTime, jadi saya tidak tahu dari mana perubahan itu berasal. Atau apakah itu valid. Tes sebelum menggunakan.
perubahan perilaku adalah hasil dari mengubah cara GetSystemTimeAsFileTimekerja di XP vs 7. Lihat di sini untuk detail lebih lanjut (t; dr itu menjadi lebih tepat karena seluruh sistem memperkenalkan beberapa metode waktu yang lebih tepat).
11

System.nanoTime()tidak didukung di JVM yang lebih lama. Jika itu masalah, tetaplah dengancurrentTimeMillis

Mengenai akurasi, Anda hampir benar. Pada BEBERAPA mesin Windows, currentTimeMillis()memiliki resolusi sekitar 10 ms (bukan 50 ms). Saya tidak yakin mengapa, tetapi beberapa mesin Windows sama akuratnya dengan mesin Linux.

Saya telah menggunakan GAGETimer di masa lalu dengan kesuksesan sedang.

Paul Morel
sumber
3
"JVM lama" seperti yang mana? Java 1.2 atau sesuatu?
Simon Forsberg
1
System.nanoTime () diperkenalkan di Java 1.5 pada 2004. Java 1.4 memperluas dukungan pada 2013, jadi cukup aman untuk mengatakan System.nanoTime () sekarang dapat diandalkan dan jawaban ini sekarang kedaluwarsa.
Dave L.
9

Seperti yang dikatakan orang lain, currentTimeMillis adalah waktu jam, yang berubah karena waktu musim panas, pengguna mengubah pengaturan waktu, detik kabisat, dan sinkronisasi waktu internet. Jika aplikasi Anda bergantung pada peningkatan nilai waktu yang berlalu secara monoton, Anda mungkin lebih memilih nanoTime.

Anda mungkin berpikir bahwa para pemain tidak akan mengutak-atik pengaturan waktu selama bermain game, dan mungkin Anda akan benar. Tetapi jangan meremehkan gangguan karena sinkronisasi waktu internet, atau mungkin pengguna desktop jarak jauh. API nanoTime kebal terhadap gangguan semacam ini.

Jika Anda ingin menggunakan waktu jam, tetapi menghindari diskontinuitas karena sinkronisasi waktu internet, Anda dapat mempertimbangkan klien NTP seperti Meinberg, yang "menyetel" laju jam ke nol, bukan hanya mengatur ulang jam secara berkala.

Saya berbicara dari pengalaman pribadi. Dalam aplikasi cuaca yang saya kembangkan, saya mendapatkan lonjakan kecepatan angin secara acak. Butuh beberapa saat bagi saya untuk menyadari bahwa basis waktu saya sedang terganggu oleh perilaku waktu jam pada PC biasa. Semua masalah saya hilang ketika saya mulai menggunakan nanoTime. Konsistensi (monotonisitas) lebih penting bagi aplikasi saya daripada presisi mentah atau akurasi absolut.

KarlU
sumber
19
"currentTimeMillis adalah waktu jam, yang berubah karena waktu musim panas" ... cukup yakin bahwa pernyataan ini salah. System.currentTimeMillis()melaporkan waktu yang telah berlalu (dalam milidetik) sejak Zaman Unix / Posix yaitu Tengah Malam, 1 Januari 1970 UTC. Karena UTC tidak pernah disesuaikan untuk penghematan siang hari, nilai ini tidak akan diimbangi ketika zona waktu lokal menambah atau tunduk pada waktu lokal untuk penghematan siang hari. Selain itu, Skala Waktu Jawa memuluskan detik kabisat sehingga hari-hari terus tampak memiliki 86.400 detik.
scottb
5

Ya, jika ketepatan seperti itu diperlukan penggunaan System.nanoTime(), tetapi perlu diketahui bahwa Anda kemudian memerlukan Java 5+ JVM.

Pada sistem XP saya, saya melihat waktu sistem yang dilaporkan setidaknya 100 mikrodetik 278 nanodetik menggunakan kode berikut:

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)); }
    }
Lawrence Dol
sumber
4

Untuk grafik game & pembaruan posisi halus, gunakan System.nanoTime()alih-alih System.currentTimeMillis(). Saya beralih dari currentTimeMillis () ke nanoTime () dalam sebuah game dan mendapat peningkatan visual utama dalam kelancaran gerak.

Sementara satu milidetik mungkin tampak seolah-olah seharusnya sudah tepat, secara visual tidak. Faktor-faktor yang nanoTime()dapat ditingkatkan termasuk:

  • penentuan posisi piksel yang akurat di bawah resolusi jam dinding
  • kemampuan anti-alias antar piksel, jika Anda mau
  • Ketidaktepatan jam dinding Windows
  • clock jitter (ketidakkonsistenan saat jam dinding benar-benar bergerak maju)

Seperti jawaban lain yang disarankan, nanoTime memang memiliki biaya kinerja jika dipanggil berulang kali - akan lebih baik untuk memanggilnya hanya sekali per frame, dan menggunakan nilai yang sama untuk menghitung seluruh frame.

Thomas W
sumber
1

Saya sudah memiliki pengalaman yang baik dengan nanotime . Ini menyediakan waktu jam dinding sebagai dua long (detik sejak zaman dan nanodetik dalam detik itu), menggunakan perpustakaan JNI. Ini tersedia dengan bagian JNI yang dikompilasi untuk Windows dan Linux.

Jon Bright
sumber
1

System.currentTimeMillis()tidak aman untuk waktu yang berlalu karena metode ini peka terhadap perubahan sistem waktu nyata sistem. Anda harus menggunakan System.nanoTime. Silakan lihat bantuan Sistem Java:

Tentang metode nanoTime:

.. Metode ini memberikan ketepatan nanosecond, tetapi tidak harus resolusi nanosecond (yaitu, seberapa sering nilainya berubah) - tidak ada jaminan yang dibuat kecuali bahwa resolusinya setidaknya sebagus currentTimeMillis () ..

Jika Anda menggunakan System.currentTimeMillis()waktu yang Anda lewati bisa negatif (Kembali <- ke masa depan)

Ricardo Gasca
sumber
-3

satu hal di sini adalah inkonsistensi metode nanoTime. itu tidak memberikan nilai yang sangat konsisten untuk input yang sama .currentTimeMillis melakukan jauh lebih baik dalam hal kinerja dan konsistensi, dan juga, meskipun tidak setepat nanoTime, memiliki margin kesalahan yang lebih rendah , dan karena itu lebih akurat nilainya. Karena itu saya menyarankan agar Anda menggunakan currentTimeMillis

sarvesh
sumber
6
Seperti dicatat dalam jawaban dan komentar lain, currentTimeMillis dapat berubah jam sistem dan karenanya pilihan yang buruk untuk menghitung waktu yang telah berlalu sejak beberapa peristiwa sebelumnya di JVM.
spidol duelin