System.currentTimeMillis กับ System.nanoTime


379

Vs ความแม่นยำ ความแม่นยำ

สิ่งที่ฉันอยากรู้คือฉันควรใช้System.currentTimeMillis ()หรือSystem.nanoTime ()เมื่ออัปเดตตำแหน่งของวัตถุในเกมหรือไม่ การเปลี่ยนแปลงในการเคลื่อนไหวของพวกเขาเป็นสัดส่วนโดยตรงกับเวลาที่ผ่านไปนับตั้งแต่การโทรครั้งสุดท้ายและฉันต้องการให้แม่นยำที่สุดเท่าที่จะทำได้

ฉันได้อ่านแล้วว่ามีปัญหาการแก้ไขเวลาที่ร้ายแรงระหว่างระบบปฏิบัติการที่แตกต่างกัน (เช่น Mac / Linux มีความละเอียดเกือบ 1 ms ในขณะที่ Windows มีความละเอียด 50ms หรือไม่) ฉันทำงานแอพของฉันบน Windows เป็นหลักและความละเอียด 50ms ดูไม่ถูกต้อง

มีตัวเลือกที่ดีกว่าสองอย่างที่ฉันระบุไว้หรือไม่

ข้อเสนอแนะ / ความคิดเห็นใด ๆ


81
nanoTimeโดยปกติแล้วจะมีความแม่นยำมากกว่าในปัจจุบัน TimeMillis แต่ก็มีค่าใช้จ่ายที่ค่อนข้างแพงเช่นกัน currentTimeMillis()รันในนาฬิกา cpu เพียงไม่กี่ (5-6) ตัว nanoTime ขึ้นอยู่กับสถาปัตยกรรมพื้นฐานและสามารถเป็นนาฬิกา cpu ได้ 100+
bestsss

10
คุณรู้หรือไม่ว่า Windows โดยทั่วไปมีเวลาเป็นเม็ดเล็ก ๆ ที่ 1000ms / 64 ใช่ไหม? ช่วงเวลาใดที่ 15.625 มิลลิวินาทีหรือ 15625000 ล้านวินาที!

7
ฉันไม่คิดว่าจะเพิ่มจำนวนรอบนาฬิกาเป็นร้อยเท่าจะส่งผลต่อเกมของคุณและการแลกเปลี่ยนอาจจะคุ้มค่า คุณควรจะเรียกวิธีการหนึ่งครั้งต่อการอัปเดตเกมจากนั้นบันทึกค่าในหน่วยความจำดังนั้นจึงไม่เพิ่มค่าใช้จ่ายจำนวนมาก สำหรับความละเอียดของแพลตฟอร์มต่าง ๆ ฉันไม่รู้เลย
aglassman

9
Windows มีค่าเริ่มต้นเป็นค่าเริ่มต้นที่น้อยครั้งละ 1000ms / 64 คุณสามารถเพิ่มสิ่งนี้ผ่านเนทิฟเวลา BeginPeriod API พีซีสมัยใหม่ยังมีตัวจับเวลาความละเอียดสูงนอกเหนือจากตัวจับเวลาพื้นฐาน ตัวจับเวลาความละเอียดสูงสามารถเข้าถึงได้ผ่านการเรียก QueryPerformanceCounter
Robin Davies

2
@Gohan - บทความนี้มีรายละเอียดเกี่ยวกับผลงานภายในของSystem.currentTimeMillis(): pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.html
Attila Tanyi

คำตอบ:


320

หากคุณกำลังมองหาการวัดที่แม่นยำมากของเวลาที่ผ่านไปSystem.nanoTime()ใช้ System.currentTimeMillis()จะให้เวลาที่ผ่านไปได้แม่นยำที่สุดในหน่วยมิลลิวินาทีนับตั้งแต่ยุค แต่System.nanoTime()ให้เวลาที่แม่นยำในระดับนาโนวินาทีเมื่อเทียบกับบางจุด

จากเอกสาร Java:

public static long nanoTime()

ส่งคืนค่าปัจจุบันของตัวจับเวลาระบบที่แม่นยำที่สุดในหน่วยนาโนวินาที

วิธีนี้สามารถใช้เพื่อวัดเวลาที่ผ่านไปและไม่เกี่ยวข้องกับความคิดอื่น ๆ ของระบบหรือเวลานาฬิกาแขวน ค่าที่ส่งคืนแสดงถึง nanoseconds ตั้งแต่บางเวลาคงที่ แต่กำเนิดโดยพลการ(อาจเป็นในอนาคตดังนั้นค่าอาจเป็นค่าลบ) วิธีนี้ให้ความแม่นยำระดับนาโนวินาที แต่ไม่จำเป็นต้องมีความแม่นยำระดับนาโนวินาที ไม่มีการรับประกันเกี่ยวกับค่าที่เปลี่ยนแปลงบ่อยครั้ง ความแตกต่างในการโทรติดต่อกันที่มีระยะเวลามากกว่า 292 ปี (2 63 นาโนวินาที) จะไม่คำนวณเวลาที่ผ่านไปอย่างแม่นยำเนื่องจากตัวเลขล้น

ตัวอย่างเช่นในการวัดระยะเวลาที่ใช้ในการรันโค้ด:

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

ดูเพิ่มเติมที่: JavaDoc System.nanoTime ()และJavaDoc System.currentTimeMillis ()สำหรับข้อมูลเพิ่มเติม


20
คุณแน่ใจหรือไม่ว่าทราบความแตกต่างระหว่างความแม่นยำและความแม่นยำ ไม่มีวิธีใดที่จะแม่นยำกับความแม่นยำระดับนาโนวินาที
mmcdole

6
ขออภัยฉันหมายถึงชัดเจน ฉันใช้คำนี้อย่างหลวม ๆ แต่ฉันเห็นด้วยว่ามันสับสน (และการใช้คำที่ไม่เหมาะสม)
dancavallaro

4
@davavallaro ขอบคุณสำหรับข้อมูล หากคุณไม่รังเกียจฉันจะแก้ไขคำตอบของคุณเพื่อรวมการเสนอราคาจากเอกสารและแก้ไขลิงก์
mmcdole

135
คำตอบนี้ถูกต้องทางเทคนิคในการเลือก nanoTime () แต่คัดสรรอย่างสมบูรณ์ในจุดที่สำคัญมาก nanoTime () ตามที่ doc บอกว่าเป็นตัวจับเวลาที่แม่นยำ currentTimeMillis () ไม่ใช่ TIMER มันคือ "นาฬิกาแขวน" nanoTime () จะสร้างเวลาที่ผ่านไปในเชิงบวกเสมอ currentTimeMillis จะไม่ (เช่นถ้าคุณเปลี่ยนวันกดวินาทีกระโดด ฯลฯ ) นี่เป็นความแตกต่างที่สำคัญอย่างยิ่งสำหรับระบบบางประเภท
charstar

11
เวลาที่ผู้ใช้เปลี่ยนและการซิงค์ NTP แน่นอน แต่ทำไมจะcurrentTimeMillisเปลี่ยนเนื่องจาก DST สวิตช์ DST ไม่เปลี่ยนจำนวนวินาทีผ่านยุค มันอาจจะเป็น "นาฬิกาแขวน" แต่มันก็เป็นนาฬิกาที่อ้างอิงจาก UTC คุณต้องพิจารณาตามการตั้งค่าเขตเวลาและเวลาที่คุณต้องการแปลตามเวลาท้องถิ่นของคุณ (หรือใช้ยูทิลิตี Java อื่น ๆ
Shadow Man

100

เนื่องจากไม่มีใครพูดถึงเรื่องนี้ ...

ไม่ปลอดภัยที่จะเปรียบเทียบผลลัพธ์ของการSystem.nanoTime()โทรระหว่างเธรดที่ต่างกัน แม้ว่าเหตุการณ์ของเธรดจะเกิดขึ้นตามลำดับที่คาดการณ์ได้ความแตกต่างของนาโนวินาทีสามารถเป็นบวกหรือลบได้

System.currentTimeMillis() ปลอดภัยสำหรับการใช้ระหว่างเธรด


4
บน Windows ที่เก็บไว้จนถึง SP2 เท่านั้นตาม: stackoverflow.com/questions/510462/…
Peter Schmitz

3
คุณเรียนรู้สิ่งใหม่ทุกวัน ฉันสงสัยว่าแม้ว่ามันจะไม่ปลอดภัยในอดีต (กลับแน่นอนอ่านเรื่องไร้สาระในกระทู้) การใช้งานดังกล่าวอาจยังคงอยู่นอกสเปคและควรหลีกเลี่ยง
gubby

4
@jgubby: น่าสนใจมาก ... การอ้างอิงถึงการสนับสนุนที่ไม่ปลอดภัยในการเปรียบเทียบผลลัพธ์ของการเรียก System.nanoTime () ระหว่างเธรดที่แตกต่างกันหรือไม่ การเชื่อมโยงต่อไปนี้จะคุ้มค่าที่จะดูที่: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519418 docs.oracle.com/javase/7/docs/api/java/lang/...
user454322

15
ดูคำอธิบายที่กล่าวถึงที่นี่: docs.oracle.com/javase/7/docs/api/java/lang/…ดูเหมือนว่าความแตกต่างระหว่างค่าที่ส่งคืนจาก nanoTime นั้นจะถูกต้องสำหรับการเปรียบเทียบตราบใดที่ยังคงเหมือนเดิม 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 fornanoTime() says: ต้นกำเนิดเดียวกันนั้นถูกใช้โดยการเรียกใช้เมธอดนี้ทั้งหมดในอินสแตนซ์ของเครื่องเสมือน Java; อินสแตนซ์ของเครื่องเสมือนอื่น ๆ มีแนวโน้มที่จะใช้ต้นกำเนิดที่แตกต่างกัน ซึ่งหมายความว่ามันจะกลับมาเหมือนกันทั่วทั้งกระทู้
Simon Forsberg

58

อัปเดตโดยArkadiy : ฉันสังเกตเห็นการทำงานที่ถูกต้องของSystem.currentTimeMillis()บน Windows 7 ใน Oracle Java 8 เวลาถูกส่งคืนด้วยความแม่นยำ 1 มิลลิวินาที ซอร์สโค้ดใน OpenJDK ไม่เปลี่ยนแปลงดังนั้นฉันไม่รู้ว่าอะไรทำให้เกิดพฤติกรรมที่ดีขึ้น


David Holmes of Sun โพสต์บทความบล็อกเมื่อสองสามปีที่แล้วซึ่งมีรายละเอียดอย่างละเอียดเกี่ยวกับ Java timing APIs (โดยเฉพาะSystem.currentTimeMillis()และSystem.nanoTime()) เมื่อคุณต้องการใช้ซึ่งและวิธีการทำงานภายใน

ภายใน Hotspot VM: นาฬิกาตัวจับเวลาและเหตุการณ์การกำหนดเวลา - ส่วนที่ 1 - Windows

สิ่งที่น่าสนใจอย่างหนึ่งของตัวจับเวลาที่ใช้โดย Java บน Windows สำหรับ API ที่มีพารามิเตอร์การรอเวลาคือความละเอียดของตัวจับเวลาสามารถเปลี่ยนแปลงได้ขึ้นอยู่กับการเรียก API อื่น ๆ ที่เกิดขึ้น - ระบบกว้าง (ไม่ใช่แค่ในกระบวนการเฉพาะ) . เขาแสดงตัวอย่างที่การใช้Thread.sleep()จะทำให้การเปลี่ยนแปลงความละเอียดนี้


1
@Arkadiy: คุณมีแหล่งที่มาสำหรับคำสั่งในการปรับปรุงหรือไม่?
Lii

@Lii - น่าเสียดายที่ไม่มี มันมาจากฉันใช้รหัสในคำถามนี้: stackoverflow.com/questions/34090914/... รหัสสร้างความแม่นยำ 15ms พร้อมความแม่นยำ Java 7 และ 1 ms พร้อม Java 8

2
ฉันติดตาม currentTimeMillis ไปที่ os :: javaTimeMillis ใน hotspot / src / os / windows / vm / os_windows.cpp ใน OpenJDK ( hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/os/… ) . ดูเหมือนว่ายังคงเป็น GetSystemTimeAsFileTime ดังนั้นฉันไม่ทราบว่าการเปลี่ยนแปลงมาจากไหน หรือถ้ามันถูกต้อง ทดสอบก่อนใช้

การเปลี่ยนแปลงพฤติกรรมเป็นผลมาจากการเปลี่ยนแปลงวิธีการ GetSystemTimeAsFileTimeทำงานใน XP กับ 7 ดูรายละเอียดเพิ่มเติมที่นี่ (tl; dr มันแม่นยำมากขึ้นเนื่องจากทั้งระบบแนะนำวิธีการจับเวลาที่แม่นยำยิ่งขึ้น)

11

System.nanoTime()ไม่รองรับ JVM รุ่นเก่า หากนั่นเป็นข้อกังวลcurrentTimeMillis

คุณเกือบถูกต้องแล้ว บนเครื่อง Windows บางเครื่องcurrentTimeMillis()มีความละเอียดประมาณ 10ms (ไม่ใช่ 50ms) ฉันไม่แน่ใจว่าทำไม แต่เครื่อง Windows บางเครื่องนั้นแม่นยำเท่ากับเครื่อง Linux

ก่อนหน้านี้ฉันเคยใช้GAGETimerด้วยความสำเร็จปานกลาง


3
"JVM ที่เก่ากว่า" เหมือนที่เป็นอยู่ใช่หรือไม่ Java 1.2 หรืออะไร?
Simon Forsberg

1
System.nanoTime () เปิดตัวใน Java 1.5 ในปี 2004 Java 1.4 ได้รับการสนับสนุนเพิ่มเติมในปี 2013 ดังนั้นจึงค่อนข้างปลอดภัยที่จะพูดว่า System.nanoTime () สามารถใช้งานได้ในตอนนี้และคำตอบนี้ล้าสมัยแล้ว
Dave L.

9

อย่างที่คนอื่น ๆ บอกไว้ currentTimeMillis เป็นเวลาของนาฬิกาซึ่งเปลี่ยนไปตามการปรับเวลาตามฤดูกาลผู้ใช้เปลี่ยนการตั้งค่าเวลาวินาทีกระโดดและการซิงค์เวลาอินเทอร์เน็ต หากแอปของคุณขึ้นอยู่กับการเพิ่มค่าเวลาที่ผ่านไปอย่างน่าเบื่อคุณอาจต้องการ nanoTime แทน

คุณอาจคิดว่าผู้เล่นจะไม่เล่นซอกับการตั้งค่าเวลาระหว่างการเล่นเกมและบางทีคุณอาจพูดถูก แต่อย่าประมาทความขัดข้องเนื่องจากการซิงค์เวลาอินเทอร์เน็ตหรือผู้ใช้เดสก์ท็อประยะไกล API ของ nanoTime มีภูมิคุ้มกันต่อการหยุดชะงักประเภทนี้

หากคุณต้องการใช้เวลานาฬิกา แต่หลีกเลี่ยงความไม่ต่อเนื่องเนื่องจากการซิงค์เวลาอินเทอร์เน็ตคุณอาจพิจารณาไคลเอนต์ NTP เช่น Meinberg ซึ่ง "ปรับ" อัตรานาฬิกาเป็นศูนย์แทนการรีเซ็ตนาฬิกาเป็นระยะ

ฉันพูดจากประสบการณ์ส่วนตัว ในแอพพลิเคชั่นสภาพอากาศที่ฉันพัฒนาขึ้นฉันได้รับการสุ่มหนามความเร็วลมแบบสุ่ม ฉันใช้เวลาสักครู่ในการตระหนักว่าระยะเวลาของฉันถูกหยุดชะงักเนื่องจากพฤติกรรมของเวลานาฬิกาบนพีซีทั่วไป ปัญหาทั้งหมดของฉันหายไปเมื่อฉันเริ่มใช้ nanoTime ความสอดคล้อง (monotonicity) มีความสำคัญต่อแอปพลิเคชันของฉันมากกว่าความแม่นยำแบบดิบหรือความแม่นยำแบบสัมบูรณ์


19
"currentTimeMillis เป็นเวลานาฬิกาซึ่งเปลี่ยนไปเนื่องจากการปรับเวลาตามฤดูกาล" ... ค่อนข้างแน่ใจว่าคำสั่งนี้เป็นเท็จ System.currentTimeMillis()รายงานเวลาที่ผ่านไป (เป็นมิลลิวินาที) ตั้งแต่ Unix / Posix Epoch ซึ่งเป็น Midnight, 1 Jan 1970 UTC เนื่องจากไม่ได้ปรับ UTC เพื่อการประหยัดเวลากลางวันค่านี้จะไม่ถูกหักล้างเมื่อเพิ่มเขตเวลาท้องถิ่นหรือชดเชยเวลาตามฤดูกาลในท้องถิ่นเพื่อการประหยัดเวลากลางวัน นอกจากนี้ Java Time Scale ยังช่วยเพิ่มวินาทีในการกระโดดเพื่อให้วันยังคงมี 86,400 วินาที
กอตต์

5

ใช่ถ้าจำเป็นต้องใช้ความแม่นยำดังกล่าว System.nanoTime()แต่พึงระวังว่าคุณต้องการ Java 5+ JVM

ในระบบ XP ของฉันฉันเห็นเวลาของระบบรายงานอย่างน้อย100 microseconds 278 nanosecondsโดยใช้รหัสต่อไปนี้:

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

สำหรับกราฟิกเกมและการอัปเดตตำแหน่งที่ราบรื่นให้ใช้System.nanoTime()แทนSystem.currentTimeMillis()มากกว่าฉันเปลี่ยนจาก currentTimeMillis () เป็น nanoTime () ในเกมและได้รับการปรับปรุงที่สำคัญด้านภาพในการเคลื่อนไหวที่ราบรื่น

ในขณะที่หนึ่งมิลลิวินาทีอาจดูเหมือนราวกับว่ามันควรจะแม่นยำอยู่แล้ว แต่ก็มองไม่เห็น ปัจจัยที่nanoTime()สามารถปรับปรุง ได้แก่ :

  • ตำแหน่งพิกเซลที่ถูกต้องด้านล่างความละเอียดนาฬิกาแขวน
  • ความสามารถในการต่อต้านนามแฝงระหว่างพิกเซลถ้าคุณต้องการ
  • หน้าต่างนาฬิกาไม่ถูกต้อง
  • นาฬิกากระวนกระวายใจ (ไม่สอดคล้องกันของเมื่อนาฬิกาแขวนเห็บไปข้างหน้าจริง)

เช่นเดียวกับคำตอบอื่น ๆ ที่แนะนำ nanoTime มีค่าใช้จ่ายหากเรียกซ้ำหลายครั้งมันจะดีที่สุดถ้าเรียกมันเพียงครั้งเดียวต่อเฟรมและใช้ค่าเดียวกันเพื่อคำนวณเฟรมทั้งหมด


1

ผมเคยมีประสบการณ์ที่ดีกับnanotime ให้เวลานาฬิกาแขวนเป็นสองความยาว (วินาทีนับตั้งแต่ยุคและวินาทีภายในวินาทีนั้น) โดยใช้ไลบรารี JNI มันสามารถใช้ได้กับ JNI ชิ้นส่วน precompiled สำหรับทั้ง Windows และ Linux


1

System.currentTimeMillis()ไม่ปลอดภัยสำหรับเวลาที่ผ่านไปเนื่องจากวิธีนี้มีความไวต่อการเปลี่ยนแปลงของระบบตามเวลาจริงของระบบ คุณควรใช้System.nanoTimeคุณควรใช้โปรดดูวิธีใช้ระบบ Java:

เกี่ยวกับวิธี nanoTime:

.. วิธีนี้ให้ความแม่นยำระดับนาโนวินาที แต่ไม่จำเป็นต้องมีความละเอียดระดับนาโนวินาที (นั่นคือความถี่เปลี่ยนแปลงค่าบ่อยแค่ไหน) - ไม่มีการรับประกันใด ๆ ยกเว้นว่าความละเอียดอย่างน้อยดีเท่าของ currentTimeMillis () ..

หากคุณใช้System.currentTimeMillis()เวลาที่ผ่านไปอาจเป็นค่าลบ (ย้อนกลับ <- ถึงอนาคต)


-3

สิ่งหนึ่งที่นี่คือความไม่สอดคล้องกันของ nanoTime method มันไม่ได้ให้ค่าที่สอดคล้องกันมากสำหรับ input.currentTimeMillis เดียวกันนี้จะดีขึ้นมากในแง่ของประสิทธิภาพและความสม่ำเสมอและแม้ว่าจะไม่แม่นยำเท่ากับ nanoTime แต่ก็มีข้อผิดพลาดที่ต่ำกว่า และมีความแม่นยำมากขึ้นในมูลค่าของมัน ฉันอยากจะแนะนำให้คุณใช้ currentTimeMillis


6
ดังที่ระบุไว้ในคำตอบและความคิดเห็นอื่น ๆ currentTimeMillis อาจมีการเปลี่ยนแปลงนาฬิการะบบและเป็นทางเลือกที่ดีสำหรับการคำนวณเวลาที่ผ่านไปตั้งแต่เหตุการณ์ก่อนหน้านี้ใน JVM
เครื่องหมาย Duelin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.