调用Thread.sleep()后,System.nanoTime()测量经过的时间的准确度会降低
我在这里遇到一个非常不寻常的问题。 似乎调用Thread.sleep(n),其中n> 0将导致以下System.nanoTime()调用不太可预测。
下面的代码演示了这个问题。
在我的计算机上运行它(rMBP 15“2015,OS X 10.11,jre 1.8.0_40-b26)输出以下结果:
Control: 48497 Random: 36719 Thread.sleep(0): 48044 Thread.sleep(1): 832271
在运行Windows 8的虚拟机上(VMware Horizon,Windows 8.1,为1.8.0_60-b27):
Control: 98974 Random: 61019 Thread.sleep(0): 115623 Thread.sleep(1): 282451
但是,在企业服务器上运行它(VMware,RHEL 6.7,jre 1.6.0_45-b06):
Control: 1385670 Random: 1202695 Thread.sleep(0): 1393994 Thread.sleep(1): 1413220
令人惊讶的是我期望的结果。
很明显,Thread.sleep(1)会影响下面代码的计算。 我不知道为什么会这样。 有人有线索吗?
谢谢!
public class Main { public static void main(String[] args) { int N = 1000; long timeElapsed = 0; long startTime, endTime = 0; for (int i = 0; i < N; i++) { startTime = System.nanoTime(); //search runs here endTime = System.nanoTime(); timeElapsed += endTime - startTime; } System.out.println("Control: " + timeElapsed); timeElapsed = 0; for (int i = 0; i < N; i++) { startTime = System.nanoTime(); //search runs here endTime = System.nanoTime(); timeElapsed += endTime - startTime; for (int j = 0; j < N; j++) { int k = (int) Math.pow(i, j); } } System.out.println("Random: " + timeElapsed); timeElapsed = 0; for (int i = 0; i < N; i++) { startTime = System.nanoTime(); //search runs here endTime = System.nanoTime(); timeElapsed += endTime - startTime; try { Thread.sleep(0); } catch (InterruptedException e) { break; } } System.out.println("Thread.sleep(0): " + timeElapsed); timeElapsed = 0; for (int i = 0; i < N; i++) { startTime = System.nanoTime(); //search runs here endTime = System.nanoTime(); timeElapsed += endTime - startTime; try { Thread.sleep(2); } catch (InterruptedException e) { break; } } System.out.println("Thread.sleep(1): " + timeElapsed); } }
基本上我在while循环中运行一个搜索,它通过调用Thread.sleep()在每次迭代中rest一下。 我想从运行搜索所花费的总时间中排除hibernate时间,因此我使用System.nanoTime()来记录开始和结束时间。 但是,如上所述,这不起作用。
有办法解决这个问题吗?
谢谢你的任何输入!
这是一个复杂的主题,因为JVM使用的定时器高度依赖CPU和OS,并且随Java运行时版本而变化。 VM还可能限制它们传递给guest虚拟机的CPUfunction,这可能会改变相对于裸机设置的选择。
您可能需要阅读以下内容,但请注意,其中一些文章可能有点过时,多年来TSC可靠性有了很大提高:
- OpenJDK Bug 8068730通过Java 8中引入的Date和Time API在Java 9中展示更精确的系统时钟
- http://shipilev.net/blog/2014/nanotrusting-nanotime/
- https://btorpey.github.io/blog/2014/02/18/clock-sources-in-linux/
- https://stas-blogspot.blogspot.de/2012/02/what-is-behind-systemnanotime.html
- https://en.wikipedia.org/wiki/Time_Stamp_Counter (特别是CPUfunction,linux命名法中的
constant_tsc tsc_reliable nonstop_tsc
) - 还有一些特定于平台的选项会影响时钟精度,例如
UseLinuxPosixThreadCPUClocks
和AssumeMonotonicOSTimers
- 随着更新的硬件/操作系统function的推出,使用哪些计时器也会在JVM版本之间发生变化
- http://vanillajava.blogspot.de/2012/04/yield-sleep0-wait01-and-parknanos1.html
在linux上你应该检查tsc相关的/proc/cpuinfo
标志和选中的/sys/devices/system/clocksource/*/current_clocksource
我可以建议至少两种可能的原因:
- 省电。 执行busy循环时,CPU以其最高性能状态运行。 但是,在
Thread.sleep
之后,它可能会进入节能状态之一 ,频率和电压会降低。 在CPU不会立即恢复到最大性能之后,这可能需要几纳秒到几微秒。 - 调度。 由于
Thread.sleep
导致线程被调度后,它将在计时器事件之后再次执行,这可能与用于System.nanoTime
的计时器有关。
在这两种情况下,您无法直接解决这个问题 – 我的意思是Thread.sleep
也会影响您的实际应用程序中的计时。 但是,如果测量的有用工作量足够大,则不准确性可以忽略不计。
不一致可能不是来自Java,而是来自不同的操作系统和虚拟机“primefaces”或系统时钟本身。
根据官方.nanoTime()
文档:
除了分辨率至少与currentTimeMillis()的分辨率一样好之外,不做任何保证
资源
…我可以从个人的角度知道这是因为在某些操作系统和虚拟机中,系统本身不支持“primefaces”时钟,后者是更高分辨率所必需的。 (我会在发现它后立即发布链接以获取此信息……已经很长时间了。)