System.nanoTime () ไร้ประโยชน์อย่างสมบูรณ์หรือไม่


153

ดังที่บันทึกไว้ในบล็อกโพสต์ระวังของ System.nanoTime () ใน Java , บนระบบ x86, System.nanoTime ของ Java () ส่งคืนค่าเวลาโดยใช้ตัวนับCPU ที่เฉพาะเจาะจง ตอนนี้ให้พิจารณากรณีต่อไปนี้ที่ฉันใช้เพื่อวัดเวลาการโทร:

long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;

ขณะนี้ในระบบมัลติคอร์อาจเป็นไปได้ว่าหลังจากการวัดเวลา 1 เธรดจะถูกกำหนดเวลาให้กับโปรเซสเซอร์อื่นซึ่งมีตัวนับน้อยกว่า CPU ก่อนหน้านี้ ดังนั้นเราสามารถรับค่าใน time2 ซึ่งน้อยกว่า time1 ดังนั้นเราจะได้รับค่าลบใน timeSpent

เมื่อพิจารณาถึงกรณีนี้แล้ว System.nanotime นั้นไร้ประโยชน์มากในตอนนี้หรือไม่?

ฉันรู้ว่าการเปลี่ยนเวลาของระบบไม่ส่งผลกระทบต่อ nanotime นั่นไม่ใช่ปัญหาที่ฉันอธิบายข้างต้น ปัญหาคือ CPU แต่ละตัวจะเก็บตัวนับที่แตกต่างกันนับตั้งแต่เปิดตัว ตัวนับนี้สามารถต่ำกว่าบน CPU ตัวที่สองเมื่อเทียบกับ CPU ตัวแรก เนื่องจากเธรดสามารถถูกกำหนดตารางเวลาโดยระบบปฏิบัติการไปยัง CPU ตัวที่สองหลังจากรับ time1 ค่าของ timeSpent อาจไม่ถูกต้องและอาจเป็นค่าลบ


2
ฉันไม่มีคำตอบ แต่ฉันเห็นด้วยกับคุณ บางทีมันควรจะถือเป็นข้อบกพร่องใน JVM
Aaron Digulla

2
โพสต์นั้นไม่ถูกต้องและไม่ได้ใช้ TSC ช้า แต่คุณต้องอยู่กับ: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6440250 TSC ยังสามารถทำประโยชน์ผ่านไฮเปอร์ไวเซอร์ แต่ก็ช้าอีกครั้ง
bestsss

1
และแน่นอนว่าคุณสามารถเรียกใช้ในเครื่องเสมือนที่ CPU สามารถแสดงผลกลางเซสชันได้: D
จำกัด การแก้ไข

คำตอบ:


206

คำตอบนี้ถูกเขียนในปี 2011 จากมุมมองของสิ่งที่ Sun JDK ของเวลาทำงานบนระบบปฏิบัติการของเวลาจริง นานมาแล้ว! คำตอบของ leventovนำเสนอมุมมองที่เป็นปัจจุบันมากขึ้น

โพสต์นั้นผิดและnanoTimeปลอดภัย มีความคิดเห็นเกี่ยวกับโพสต์ที่เชื่อมโยงไปยังโพสต์บล็อกโดย David Holmes , ผู้ชายเรียลไทม์และการทำงานพร้อมกันที่ Sun มันบอกว่า:

System.nanoTime () จะดำเนินการโดยใช้ QueryPerformanceCounter / QueryPerformanceFrequency API [... ] กลไกเริ่มต้นที่ใช้โดย QPC จะถูกกำหนดโดยเลเยอร์ Abstraction ฮาร์ดแวร์ (HAL) [... ] เริ่มต้นการเปลี่ยนแปลงนี้ไม่เพียง แต่ข้ามฮาร์ดแวร์ รุ่น ตัวอย่างเช่น Windows XP Service Pack 2 เปลี่ยนสิ่งต่าง ๆ เพื่อใช้ตัวจับเวลาการจัดการพลังงาน (PMTimer) แทนตัวจับเวลาโปรเซสเซอร์ (TSC) เนื่องจากปัญหากับ TSC ที่ไม่ได้ซิงโครไนซ์กับโปรเซสเซอร์ที่แตกต่างกันในระบบ SMP และเนื่องจากความถี่ สามารถแตกต่างกัน (และความสัมพันธ์กับเวลาที่ผ่านไป) ขึ้นอยู่กับการตั้งค่าการจัดการพลังงาน

ดังนั้นใน Windows นี้เป็นปัญหาขึ้นจน WinXP SP2 แต่มันก็ไม่ได้ตอนนี้

ฉันไม่สามารถหาส่วน II (หรือมากกว่า) ที่พูดถึงแพลตฟอร์มอื่น ๆ ได้ แต่บทความดังกล่าวมีข้อสังเกตว่า Linux พบและแก้ไขปัญหาเดียวกันในลักษณะเดียวกันพร้อมลิงก์ไปยังคำถามที่พบบ่อยสำหรับ clock_gettime (CLOCK_REALTIME)ซึ่งพูดว่า:

  1. clock_gettime (CLOCK_REALTIME) สอดคล้องกับโปรเซสเซอร์ / คอร์ทั้งหมดหรือไม่ (ส่วนโค้งสำคัญหรือไม่เช่น ppc, แขน, x86, amd64, sparc)

มันควรหรือก็ถือว่ารถ

อย่างไรก็ตามใน x86 / x86_64 เป็นไปได้ที่จะเห็น TSCs ที่ไม่ถูกซิงค์หรือแปรผันทำให้เกิดเวลาที่ไม่สอดคล้องกัน 2.4 เมล็ดไม่มีการป้องกันสิ่งนี้จริง ๆ และเมล็ด 2.6 ต้นไม่ได้ทำที่นี่เช่นกัน ตั้งแต่ 2.6.18 และมีตรรกะในการตรวจจับสิ่งนี้ดีกว่าและเรามักจะกลับไปที่แหล่งสัญญาณนาฬิกาที่ปลอดภัย

ppc มีฐานเวลาที่ซิงค์เสมอดังนั้นจึงไม่ควรเป็นปัญหา

ดังนั้นหากการเชื่อมโยงของโฮล์มสามารถอ่านได้ว่ามีการnanoTimeโทรหมายความว่าclock_gettime(CLOCK_REALTIME)มันปลอดภัยที่ ish เท่ากับเคอร์เนล 2.6.18 บน x86 และบน PowerPC เสมอ (เพราะ IBM และ Motorola ต่างจาก Intel จริง ๆ แล้วรู้วิธีการออกแบบไมโครโปรเซสเซอร์)

ไม่มีการกล่าวถึง SPARC หรือ Solaris แต่อย่างใด และแน่นอนเราไม่รู้ว่า IBM JVM ทำอะไร แต่ Sun JVM บน Windows และ Linux ที่ทันสมัยได้รับสิทธินี้

แก้ไข: คำตอบนี้ขึ้นอยู่กับแหล่งที่มาอ้างอิง แต่ฉันก็ยังกังวลว่ามันอาจจะผิดทั้งหมด ข้อมูลที่ทันสมัยมากขึ้นจะมีค่ามาก ฉันเพิ่งเจอลิงก์ไปยังบทความใหม่สี่ปีเกี่ยวกับนาฬิกาของ Linuxซึ่งอาจเป็นประโยชน์


13
แม้ WinXP SP2 ก็ดูเหมือนจะประสบ ใช้งานตัวอย่างรหัสต้นฉบับโดยที่void foo() { Thread.sleep(40); }ฉันมีเวลาติดลบ (-380 ms!) โดยใช้Athlon 64 X2 4200+โปรเซสเซอร์เดียว
Luke Usherwood

ฉันไม่คิดว่าจะมีการปรับปรุงในเรื่องนี้ WRT พฤติกรรมบน Linux, BSD หรือแพลตฟอร์มอื่น ๆ ?
Tomer Gabel

6
คำตอบที่ดีควรเพิ่มลิงค์ไปยังการสำรวจล่าสุดของหัวข้อนี้: shipilev.net/blog/2014/nanotrusting-nanotime
Nitsan Wakart

1
@ SOFe: โอ้นั่นเป็นความอัปยศ มันอยู่ในที่เก็บถาวรของเว็บโชคดี ฉันจะดูว่าฉันสามารถติดตามเวอร์ชันปัจจุบันหรือไม่
Tom Anderson

1
หมายเหตุ: OpenJDK ไม่รักษาข้อมูลจำเพาะจนถึง OpenJDK 8u192 ดูbugs.openjdk.java.net/browse/JDK-8184271 ตรวจสอบให้แน่ใจว่าใช้อย่างน้อยเป็น OpenJDK 8 เวอร์ชันใหม่หรือ OpenJDK 11+
leventov

35

ฉันค้นหานิดหน่อยและพบว่าหากมีใครที่คลั่งไคล้แล้วก็ใช่ว่ามันอาจจะไร้ประโยชน์ ... ในบางสถานการณ์ ... ขึ้นอยู่กับว่าเวลาของคุณเป็นอย่างไร ...

ตรวจสอบข้อความนี้จากเว็บไซต์ Java Sun:

นาฬิกาเรียลไทม์และ System.nanoTime () ทั้งคู่ขึ้นอยู่กับการเรียกของระบบเดียวกันและทำให้นาฬิกาเดียวกัน

ด้วย Java RTS API ที่อิงตามเวลาทั้งหมด (ตัวอย่างเช่น Timers, Periodic Threads, Deadline Monitoring และอื่น ๆ ) จะขึ้นอยู่กับตัวจับเวลาความละเอียดสูง และร่วมกับลำดับความสำคัญตามเวลาจริงพวกเขาสามารถมั่นใจได้ว่ารหัสที่เหมาะสมจะถูกดำเนินการในเวลาที่เหมาะสมสำหรับข้อ จำกัด ตามเวลาจริง ในทางตรงกันข้าม Java SE APIs ทั่วไปเสนอเพียงไม่กี่วิธีที่สามารถจัดการกับเวลาที่มีความละเอียดสูงโดยไม่มีการรับประกันการดำเนินการในเวลาที่กำหนด การใช้ System.nanoTime () ระหว่างจุดต่าง ๆ ในรหัสเพื่อทำการวัดเวลาที่ผ่านไปควรแม่นยำเสมอ

Java ยังมีข้อแม้สำหรับวิธี nanoTime () :

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

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

ดังนั้นเพื่อตอบคำถามของคุณ ... no nanoTime () ไม่ไร้ประโยชน์ .... มันไม่ใช่วิธีการที่รอบคอบที่สุดที่จะใช้ในทุกสถานการณ์


3
> วิธีการนี้ดีพอแม้ว่าค่าส่งคืนผลลัพธ์จะเป็นค่าลบ ฉันไม่ได้สิ่งนี้ถ้าค่าของ timespent เป็นค่าลบมันจะมีประโยชน์อย่างไรในการวัดเวลาที่ใช้ใน foo ()?
pdeva

3
มันโอเคเพราะสิ่งที่คุณเป็นห่วงคือค่าสัมบูรณ์ของความแตกต่าง นั่นคือถ้าการวัดของคุณเป็นเวลา t โดยที่ t = t2 - t1 คุณต้องการทราบ | t | .... ดังนั้นถ้าค่าเป็นลบ ... แม้ว่าปัญหามัลติคอร์จะมีผลกระทบน้อยมาก นาโนวินาทีอยู่ดี
mezoid

3
หากต้องการสำรองข้อมูล @Aaron: ทั้ง t2 และ t1 อาจเป็นค่าลบ แต่ (t2-t1) ต้องไม่เป็นค่าลบ
jfs

2
แอรอน: นั่นคือสิ่งที่ฉันเป็น t2-t1 ไม่ควรลบมิฉะนั้นเราจะมีบั๊ก
pdeva

12
@pdeva - แต่คุณเข้าใจผิดในสิ่งที่หมอพูด คุณกำลังเพิ่มที่ไม่ใช่ปัญหา มีบางช่วงเวลาที่ถือว่าเป็น "0" ค่าที่ส่งคืนโดย nanoTime () นั้นถูกต้องสัมพันธ์กับเวลานั้น มันเป็นเส้นเวลาที่เพิ่มขึ้นอย่างน่าเบื่อ คุณอาจได้รับชุดตัวเลขจากส่วนลบของไทม์ไลน์นั้น -100, -99, -98(ค่าที่ใหญ่กว่าอย่างเห็นได้ชัดมากในทางปฏิบัติ) พวกเขาไปในทิศทางที่ถูกต้อง (เพิ่มขึ้น) ดังนั้นจึงไม่มีปัญหาที่นี่
ToolmakerSteve

18

ไม่จำเป็นต้องอภิปรายเพียงใช้แหล่งที่มา ที่นี่ SE 6 สำหรับ Linux สร้างข้อสรุปของคุณเอง:

jlong os::javaTimeMillis() {
  timeval time;
  int status = gettimeofday(&time, NULL);
  assert(status != -1, "linux error");
  return jlong(time.tv_sec) * 1000  +  jlong(time.tv_usec / 1000);
}


jlong os::javaTimeNanos() {
  if (Linux::supports_monotonic_clock()) {
    struct timespec tp;
    int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp);
    assert(status == 0, "gettime error");
    jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec);
    return result;
  } else {
    timeval time;
    int status = gettimeofday(&time, NULL);
    assert(status != -1, "linux error");
    jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec);
    return 1000 * usecs;
  }
}

7
มีประโยชน์เฉพาะในกรณีที่คุณรู้ว่า API ที่ใช้ทำอะไร API ที่ใช้ถูกนำไปใช้โดยระบบปฏิบัติการ รหัสนี้ถูกต้อง wrt ข้อมูลจำเพาะของ API ที่ใช้ (clock_gettime / gettimeofday) แต่อย่างที่คนอื่น ๆ ชี้ให้เห็นระบบปฏิบัติการที่ไม่ทันสมัยบางระบบมีการใช้งานแบบบั๊กกี้
Blaisorblade

18

ตั้งแต่ Java 7 System.nanoTime()รับประกันว่าปลอดภัยด้วยข้อกำหนดของ JDK System.nanoTime()Javadoc ของทำให้ชัดเจนว่าการเรียกใช้ทั้งหมดที่ตรวจพบภายใน JVM (นั่นคือข้ามเธรดทั้งหมด) เป็นแบบโมโนโทน:

ค่าที่ส่งคืนแสดงถึง nanoseconds ตั้งแต่บางเวลาคงที่ แต่กำเนิดโดยพลการ (อาจเป็นในอนาคตดังนั้นค่าอาจเป็นค่าลบ) ต้นกำเนิดเดียวกันถูกใช้โดยการเรียกใช้เมธอดนี้ทั้งหมดในอินสแตนซ์ของเครื่องเสมือน Java อินสแตนซ์ของเครื่องเสมือนอื่น ๆ มีแนวโน้มที่จะใช้ต้นกำเนิดที่แตกต่างกัน

การดำเนินการ JVM / JDK มีหน้าที่รับผิดชอบในการขจัดความไม่สอดคล้องที่อาจสังเกตได้เมื่อเรียกยูทิลิตี้ระบบปฏิบัติการพื้นฐาน (เช่นที่กล่าวถึงในคำตอบของ Tom Anderson )

ส่วนใหญ่ของคำตอบเก่าอื่น ๆ สำหรับคำถามนี้ (เขียนใน 2009-2012) แสดง FUD ที่อาจเกี่ยวข้องกับ Java 5 หรือ Java 6 แต่ไม่เกี่ยวข้องกับ Java รุ่นที่ทันสมัยอีกต่อไป

อย่างไรก็ตามมันมีค่าที่จะกล่าวถึงแม้ว่าจะรับประกันnanoTime()ความปลอดภัยของJDK แต่ก็มีข้อบกพร่องหลายอย่างใน OpenJDK ที่ทำให้ไม่สนับสนุนการรับประกันนี้ในบางแพลตฟอร์มหรือในบางสถานการณ์ (เช่นJDK-8040140 , JDK-8184271 ) ไม่มีข้อผิดพลาดที่เปิด (open) ใน OpenJDK wrt nanoTime()ในขณะนี้ แต่การค้นพบข้อผิดพลาดดังกล่าวใหม่หรือการถดถอยในการเปิดตัว OpenJDK รุ่นใหม่ไม่ควรทำให้ใครตกใจ

โดยที่ในใจรหัสที่ใช้nanoTime()สำหรับการบล็อกที่กำหนดเวลาการรอช่วงเวลาการหมดเวลา ฯลฯ ควรปฏิบัติต่อความแตกต่างของเวลาเชิงลบ (timeouts) เป็นศูนย์แทนที่จะเป็นโยนข้อยกเว้น การปฏิบัตินี้ยังเป็นที่นิยมเพราะมันมีความสอดคล้องกับพฤติกรรมของทุกวิธีการรอเวลาที่กำหนดในทุกชั้นในjava.util.concurrent.*เช่นSemaphore.tryAcquire(), Lock.tryLock(),BlockingQueue.poll()ฯลฯ

อย่างไรก็ตามnanoTime()ยังควรใช้การบล็อกที่กำหนดเวลาการรอช่วงเวลาการหมดเวลาและอื่น ๆcurrentTimeMillis()เนื่องจากสิ่งหลังเป็นเรื่องของปรากฏการณ์ "เวลาย้อนหลัง" (เช่นเนื่องจากการแก้ไขเวลาของเซิร์ฟเวอร์) เช่นcurrentTimeMillis()ไม่เหมาะสำหรับการวัดช่วงเวลา เลย ดูคำตอบนี้สำหรับข้อมูลเพิ่มเติม

แทนการใช้nanoTime()สำหรับการวัดเวลาโค้ดโดยตรงเฉพาะการเปรียบเทียบกรอบและโปรควรนำมาใช้เช่นJMHและasync-Profilerในโหมดผนังนาฬิกาโปรไฟล์


10

ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้พัฒนาห้องสมุดนี้

คุณอาจจะชอบสิ่งนี้ดีกว่า:

http://juliusdavies.ca/nanotime/

แต่จะคัดลอกไฟล์ DLL หรือ Unix .so (วัตถุที่ใช้ร่วมกัน) ไปยังไดเรกทอรีหลักของผู้ใช้ปัจจุบันเพื่อให้สามารถเรียก JNI ได้

ข้อมูลพื้นหลังบางอย่างอยู่ในเว็บไซต์ของฉันที่:

http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html


@Julius คุณลบห้องสมุดออกจากไซต์ของคุณหรือไม่
Nitin Dandriyal

6

Linux แก้ไขความคลาดเคลื่อนระหว่าง CPU แต่ Windows ไม่สามารถแก้ไขได้ ฉันขอแนะนำให้คุณสมมติว่า System.nanoTime () นั้นมีความแม่นยำเพียงประมาณ 1 ไมโครวินาที วิธีง่ายๆในการรับเวลาที่นานขึ้นคือการโทร foo () 1,000 ครั้งขึ้นไปและหารเวลาด้วย 1,000


2
คุณสามารถให้การอ้างอิง (พฤติกรรมบน Linux และ Windows) ได้หรือไม่?
jfs

น่าเสียดายที่วิธีการที่นำเสนอโดยทั่วไปจะไม่แม่นยำสูงเนื่องจากแต่ละเหตุการณ์ที่ตกลงไปในช่องอัปเดตนาฬิกา +/- 100ms มักจะส่งคืนค่าศูนย์สำหรับการดำเนินการย่อยที่สอง ผลรวมของการดำเนินการ 9 รายการต่อระยะเวลาเท่ากับศูนย์คือดีศูนย์หารด้วยเก้าคือ ... ศูนย์ ในทางกลับกันการใช้ System.nanoTime () จะให้ระยะเวลาเหตุการณ์ที่ค่อนข้างแม่นยำ (ไม่เป็นศูนย์) ซึ่งจะสรุปและหารด้วยจำนวนเหตุการณ์จะให้ค่าเฉลี่ยที่แม่นยำสูง
ดาร์เรล Teague

@DarrellTeague รวม 1,000 เหตุการณ์และเพิ่มพวกเขาเป็นเช่นเดียวกับเวลาสิ้นสุดเพื่อสิ้นสุด
Peter Lawrey

@DarrellTeague System.nanoTime () มีความแม่นยำถึง 1 ไมโครวินาทีหรือดีกว่า (ไม่ใช่ 100,000 ไมโครวินาที) ในระบบส่วนใหญ่ การหาค่าเฉลี่ยของการทำงานจำนวนมากนั้นมีความเกี่ยวข้องเมื่อคุณลดลงเหลือไม่กี่วินาทีและในบางระบบเท่านั้น
Peter Lawrey

1
ขออภัยเนื่องจากมีความสับสนเกี่ยวกับภาษาที่ใช้ในเหตุการณ์ "ข้อสรุป" ใช่ถ้าเวลามีการทำเครื่องหมายที่จุดเริ่มต้นของการพูด 1,000 การดำเนินการย่อยที่สองพวกเขาทำงานและเวลาจะถูกทำเครื่องหมายอีกครั้งในตอนท้ายและหาร - ที่จะทำงานสำหรับระบบภายใต้การพัฒนาบางอย่างเพื่อให้ได้ระยะเวลาที่ดี เหตุการณ์
Darrell Teague

5

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

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

และฉันทดสอบกับเครื่องมัลติคอร์เท่านั้น


5

ฉันเห็นเวลาที่ผ่านไปในแง่ลบที่รายงานจากการใช้ System.nanoTime () เพื่อความชัดเจนรหัสที่เป็นปัญหาคือ:

    long startNanos = System.nanoTime();

    Object returnValue = joinPoint.proceed();

    long elapsedNanos = System.nanoTime() - startNanos;

และตัวแปร 'elapsedNanos' มีค่าเป็นลบ (ฉันแน่ใจว่าการโทรระหว่างกลางใช้เวลาน้อยกว่า 293 ปีเช่นกันซึ่งเป็นจุดที่เกินสำหรับ nanos ที่เก็บไว้ในระยะยาว :)

สิ่งนี้เกิดขึ้นโดยใช้ IBM v1.5 JRE 64 บิตบนฮาร์ดแวร์ IBM P690 (multi-core) ที่รัน AIX ฉันเพิ่งเห็นข้อผิดพลาดนี้เกิดขึ้นเพียงครั้งเดียวดังนั้นจึงดูเหมือนว่าหายากมาก ฉันไม่ทราบสาเหตุ - เป็นปัญหาเฉพาะฮาร์ดแวร์ข้อบกพร่อง JVM - ฉันไม่รู้ ฉันไม่ทราบความหมายของความถูกต้องของ nanoTime () โดยทั่วไป

เพื่อตอบคำถามเดิมฉันไม่คิดว่า nanoTime จะไร้ประโยชน์ - มันมีช่วงเวลาไม่กี่มิลลิวินาที แต่มีความเสี่ยงที่เกิดขึ้นจริง (ไม่ใช่เชิงทฤษฎี) ที่เกิดจากความไม่ถูกต้องซึ่งคุณต้องคำนึงถึง


อนิจจาน่าจะเป็นปัญหา OS / ฮาร์ดแวร์ เอกสารระบุว่าค่าหลักอาจเป็นค่าลบ แต่ (ค่าลบที่ใหญ่กว่าลบด้วยค่าที่น้อยกว่า) ควรเป็นค่าบวก อันที่จริงสมมติฐานที่ว่าในเธรดเดียวกันการเรียก nanoTime () ควรส่งคืนค่าบวกหรือค่าลบเสมอ ไม่เคยเห็นสิ่งนี้ในระบบ Unix และ Windows หลายปีที่ผ่านมา แต่ฟังดูเป็นไปได้โดยเฉพาะอย่างยิ่งถ้าฮาร์ดแวร์ / ระบบปฏิบัติการแยกการทำงานแบบปรมาณูระหว่างโปรเซสเซอร์
Darrell Teague

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

2

ดูเหมือนจะไม่เป็นปัญหาสำหรับ Core 2 Duo ที่ใช้ Windows XP และ JRE 1.5.0_06

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

[แก้ไข] ฉันเดาว่ามันจะเกิดขึ้นเฉพาะกับตัวประมวลผลที่แยกกันทางกายภาพนั่นคือตัวนับถูกซิงโครไนซ์สำหรับหลายคอร์ในตัวดายเดียวกัน


2
มันอาจจะไม่เกิดขึ้นตลอดเวลา แต่เนื่องจากวิธีการใช้ nanotime () นั้นมีความเป็นไปได้เสมอ
pdeva

ฉันเดาว่ามันจะเกิดขึ้นกับตัวประมวลผลที่แยกจากกันทางกายภาพเท่านั้นนั่นคือตัวนับจะถูกซิงโครไนซ์สำหรับหลายคอร์ในตัวดายเดียวกัน
starblue

แม้จะขึ้นอยู่กับการใช้งานเฉพาะ IIRC แต่นั่นเป็นสิ่งที่ระบบปฏิบัติการควรจะดูแล
Blaisorblade

1
ตัวนับ RDTSC บนหลายคอร์ของโพรเซสเซอร์ x86 เดียวกันไม่จำเป็นต้องทำการซิงโครไนซ์ - ระบบที่ทันสมัยบางรุ่นให้คอร์ที่แตกต่างกันทำงานด้วยความเร็วที่แตกต่างกัน
จูลส์

2

ไม่มันไม่ใช่ ... มันขึ้นอยู่กับ CPU ของคุณตรวจสอบตัวจับเวลาเหตุการณ์ความแม่นยำสูงสำหรับวิธี / สิ่งต่าง ๆ ที่ได้รับการปฏิบัติตาม CPU

โดยทั่วไปอ่านแหล่งที่มาของ Java ของคุณและตรวจสอบรุ่นของคุณกับฟังก์ชั่นและถ้ามันทำงานกับ CPU คุณจะทำงานบน

IBM ยังแนะนำให้คุณใช้เพื่อการเปรียบเทียบประสิทธิภาพ (โพสต์ปี 2008 แต่ได้รับการอัพเดต)


ตามที่กำหนดไว้ทั้งหมด bahaviour, "caveat emptor!"
David Schmitt

2

ฉันกำลังเชื่อมโยงกับสิ่งที่เป็นหลักคือการสนทนาเดียวกันกับที่ Peter Lawrey ให้คำตอบที่ดี ทำไมฉันถึงได้เวลาลบโดยใช้ System.nanoTime ()

หลายคนกล่าวว่าใน Java System.nanoTime () อาจส่งคืนเวลาติดลบ ฉันขอโทษที่ทำซ้ำสิ่งที่คนอื่นพูดไปแล้ว

  1. nanoTime () ไม่ใช่นาฬิกา แต่เป็นตัวนับรอบ CPU
  2. ค่าส่งคืนถูกหารด้วยความถี่เพื่อให้ดูเหมือนเวลา
  3. ความถี่ของ CPU อาจผันผวน
  4. เมื่อเธรดของคุณถูกกำหนดเวลาไว้ใน CPU อื่นมีโอกาสที่จะได้รับ nanoTime () ซึ่งส่งผลให้เกิดความแตกต่างในเชิงลบ นั่นเป็นเหตุผล ตัวนับข้าม CPU จะไม่ถูกซิงโครไนซ์
  5. ในหลายกรณีคุณอาจได้รับผลลัพธ์ที่ทำให้เข้าใจผิดมาก แต่คุณจะไม่สามารถบอกได้เพราะเดลต้านั้นไม่ติดลบ ลองคิดดู
  6. (ไม่ได้รับการยืนยัน) ฉันคิดว่าคุณอาจได้รับผลลบแม้กระทั่งใน CPU เดียวกันหากมีการจัดลำดับคำแนะนำใหม่ เพื่อป้องกันสิ่งนั้นคุณจะต้องเรียกใช้อุปสรรคหน่วยความจำที่ทำให้เป็นลำดับคำแนะนำของคุณ

มันจะเจ๋งถ้า System.nanoTime () ส่งคืน coreID ที่ใช้งาน


1
คะแนนทั้งหมดยกเว้น 3 และ 5 ผิด 1. nanoTime () ไม่ได้เป็นเคาน์เตอร์วงจร CPU มันเป็นเวลานาโน 2. วิธีสร้างค่า nanoTime เฉพาะแพลตฟอร์ม 4. ไม่ความแตกต่างไม่สามารถเป็นค่าลบได้ตามข้อมูลจำเพาะ nanoTime () สมมติว่า OpenJDK ไม่มีข้อบกพร่อง wrt nanoTime () และไม่มีข้อผิดพลาดที่ไม่สามารถแก้ไขได้ในขณะนี้ 6. การโทร nanoTime ไม่สามารถจัดลำดับใหม่ภายในเธรดได้เนื่องจากเป็นวิธีเนทีฟและ JVM จะเคารพการสั่งซื้อโปรแกรม JVM ไม่เรียงลำดับการเรียกใช้เมธอดดั้งเดิมเนื่องจากไม่ทราบว่าเกิดอะไรขึ้นภายในตัวพวกเขาดังนั้นจึงไม่สามารถพิสูจน์ได้ว่าการเรียงลำดับใหม่นั้นจะปลอดภัย
leventov

เกี่ยวกับ 5. nanoTime () ผลลัพธ์ที่แตกต่างอาจทำให้เข้าใจผิดอย่างแน่นอน แต่ไม่ใช่ด้วยเหตุผลที่นำเสนอในจุดอื่น ๆ ใน asnwer นี้ แต่ด้วยเหตุผลที่นำเสนอที่นี่: shipilev.net/blog/2014/nanotrusting-nanotime
leventov

กระแทกแดกดันเกี่ยวกับ 6 ได้มีข้อผิดพลาดใน OpenJDK เฉพาะเนื่องจาก reorderings: bugs.openjdk.java.net/browse/JDK-8184271 ใน OpenJDK นั้น nanoTime () เป็นรูปแบบที่แท้จริงและได้รับอนุญาตให้จัดลำดับใหม่นั่นเป็นข้อผิดพลาด
leventov

@ leventov, nanotime () ปลอดภัยไหมที่จะใช้? ความหมายมันไม่สามารถส่งคืนค่าลบและถูกต้อง ~ เมื่อเวลาผ่านไป ฉันไม่เห็นจุดที่เปิดเผยฟังก์ชั่น API ที่เต็มไปด้วยปัญหา โพสต์นี้เป็นหลักฐานเริ่มต้นในปี 2009 และยังคงให้ความเห็นในปี 2019 สำหรับสิ่งที่สำคัญภารกิจฉันคิดว่าคนพึ่งพาบัตรเวลาเช่น Symmetricom
Vortex

1
# 4 ของคุณพูดเกี่ยวกับความแตกต่างเชิงลบไม่ใช่ค่า: "มีโอกาสที่จะได้รับ nanoTime () ซึ่งทำให้เกิดความแตกต่างในเชิงลบ"
leventov

1

Java เป็นแพลตฟอร์มข้ามแพลตฟอร์มและ nanoTime ขึ้นอยู่กับแพลตฟอร์ม ถ้าคุณใช้ Java - เมื่อไม่ใช้ nanoTime ฉันพบข้อบกพร่องจริงในการใช้งาน jvm ที่แตกต่างกันด้วยฟังก์ชั่นนี้


0

เอกสาร Java 5 ยังแนะนำให้ใช้วิธีนี้เพื่อจุดประสงค์เดียวกัน

วิธีนี้สามารถใช้เพื่อวัดเวลาที่ผ่านไปและไม่เกี่ยวข้องกับความคิดอื่น ๆ ของระบบหรือเวลานาฬิกาแขวน

Java 5 API Doc


0

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


-3

nanoTimeไม่ปลอดภัยอย่างยิ่งสำหรับเวลา ฉันลองใช้กับอัลกอริทึมการทดสอบขั้นพื้นฐานและให้คำตอบซึ่งห่างกันหนึ่งวินาทีสำหรับอินพุตเดียวกัน อย่าใช้วิธีที่ไร้สาระ ฉันต้องการสิ่งที่ถูกต้องและแม่นยำมากขึ้นกว่ามิลลิวินาทีเวลาได้รับ nanoTimeแต่ไม่ดีเท่า


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