視窗
使用QueryPerformanceCounter API實現功能,眾所周知該API存在一些問題。 它可能會飛速發展 ,有人報告說在多處理器計算機上的速度可能非常慢 ,等等。我花了一些時間上網嘗試查找QueryPerformanceCounter的工作原理和作用。 關于該主題尚無明確結論,但是有一些帖子可以簡要介紹其工作原理。 我會說,最有用的,大概是說和那人。 當然,只要稍作搜索,就能找到更多,但信息大致相同。
因此,如果可用,則實現似乎正在使用HPET 。 如果不是,則它將TSC與CPU之間的值進行某種形式的同步。 有趣的是, QueryPerformanceCounter承諾返回的值將以恒定的頻率增加。 這意味著,在使用TSC和多個CPU的情況下,可能不僅會遇到一些困難,不僅因為CPU可能具有不同的TSC值,而且可能具有不同的頻率。 請牢記所有注意事項Microsoft 建議使用SetThreadAffinityMask來阻塞將QueryPerformanceCounter調用到單個處理器的線程,這顯然不會在JVM中發生。
LINUX
Linux與Windows非常相似,除了它更加透明(我設法下載了源代碼:))。 該值從帶有CLOCK_MONOTONIC標志的clock_gettime中讀取(對于真正的男人,源可從Linux源的vclock_gettime.c中獲得)。 使用TSC或HPET 。 與Windows的唯一區別是Linux甚至不嘗試同步從不同CPU讀取的TSC值,而是按原樣返回它。 這意味著該值可以在讀取時依賴于CPU的依賴關系上來回跳。 另外,與Windows簽約時,Linux不會保持更改頻率恒定。 另一方面,它絕對應該提高性能。
索拉里斯
Solaris很簡單。 我相信通過gethrtime可以實現與linux差不多相同的clock_gettime實現。 區別在于Solaris保證計數器不會跳回,這在Linux上是可能的,但是有可能返回相同的值。 從源代碼可以看出,這種保證是使用CAS實現的,它需要與主存儲器同步,并且在多處理器機器上可能相對昂貴。 與Linux相同,更改率可能有所不同。
結論
結論是多云之王。 開發人員必須意識到功能不是完美的,它可以向后或向前跳躍。 它可能不會單調變化,并且變化率會隨CPU時鐘速度的變化而變化。 而且,它并沒有許多人想象的那么快。 在我的Windows 7計算機上進行單線程測試時,它僅比System.currentTimeMillis()快約10%,而在多線程測試中,線程數與CPU數相同,只是相同。 因此,總的來說,它所提供的只是分辨率的提高,這在某些情況下可能很重要。 最后要注意的是,即使CPU頻率沒有變化,也不要認為您可以將該值可靠地映射到系統時鐘,請參見此處的詳細信息。
附錄
附錄包含針對不同操作系統的功能實現。 源代碼來自OpenJDK v.7。
的Solaris
// gethrtime can move backwards if read from one cpu and then a different cpu
// getTimeNanos is guaranteed to not move backward on Solaris
inline hrtime_t getTimeNanos() {if (VM_Version::supports_cx8()) {const hrtime_t now = gethrtime();// Use atomic long load since 32-bit x86 uses 2 registers to keep long.const hrtime_t prev = Atomic::load((volatile jlong*)&max_hrtime);if (now <= prev) return prev; // same or retrograde time;const hrtime_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&max_hrtime, prev);assert(obsv >= prev, "invariant"); // Monotonicity// If the CAS succeeded then we're done and return "now".// If the CAS failed and the observed value "obs" is >= now then// we should return "obs". If the CAS failed and now > obs > prv then// some other thread raced this thread and installed a new value, in which case// we could either (a) retry the entire operation, (b) retry trying to install now// or (c) just return obs. We use (c). No loop is required although in some cases// we might discard a higher "now" value in deference to a slightly lower but freshly// installed obs value. That's entirely benign -- it admits no new orderings compared// to (a) or (b) -- and greatly reduces coherence traffic.// We might also condition (c) on the magnitude of the delta between obs and now.// Avoiding excessive CAS operations to hot RW locations is critical.// See http://blogs.sun.com/dave/entry/cas_and_cache_trivia_invalidatereturn (prev == obsv) ? now : obsv ;} else {return oldgetTimeNanos();}
}
的Linux
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;}
}
視窗
jlong os::javaTimeNanos() {if (!has_performance_count) {return javaTimeMillis() * NANOS_PER_MILLISEC; // the best we can do.} else {LARGE_INTEGER current_count;QueryPerformanceCounter(¤t_count);double current = as_long(current_count);double freq = performance_frequency;jlong time = (jlong)((current/freq) * NANOS_PER_SEC);return time;}
}
參考:
- System.nanoTime()背后是什么? 來自我們的JCG合作伙伴 ? Stas博客上的 Stanislav Kobylansky。
- 熱點虛擬機內部:時鐘,計時器和計劃事件
- 當心QueryPerformanceCounter()
- 為Windows實施持續更新的高分辨率時間提供程序
- 游戲計時和多核處理器
- 高精度事件計時器(維基百科)
- 時間戳計數器(維基百科)
翻譯自: https://www.javacodegeeks.com/2012/02/what-is-behind-systemnanotime.html