调用Thread.sleep()后,System.nanoTime()测量经过的时间的准确度会降低

时间:2015-11-15 04:18:20

标签: java performance 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 Horizo​​n,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()在每次迭代中休息一下。我想从运行搜索的总时间中排除休眠时间,因此我使用System.nanoTime()来记录开始和结束时间。但是,正如您在上面提到的那样,这并不能很好地发挥作用。

有没有办法解决这个问题?

感谢您的任何意见!

3 个答案:

答案 0 :(得分:3)

这是一个复杂的主题,因为JVM使用的定时器高度依赖CPU和OS,并且随Java运行时版本而变化。虚拟机还可能限制它们传递给访客的CPU功能,这可能会改变相对于裸机设置的选择。

您可能需要阅读以下内容,但请注意,其中一些文章可能有点过时,多年来TSC的可靠性有了很大提高:

在linux上,你应该检查与tsc相关的/proc/cpuinfo标志和选中的/sys/devices/system/clocksource/*/current_clocksource

答案 1 :(得分:2)

我可以至少提出两种可能的原因:

  1. 省电。执行busy循环时,CPU以最高性能状态运行。但是,在Thread.sleep之后,它可能会落入power-saving states之一,频率和电压会降低。在CPU没有立即恢复到最大性能之后,这可能需要几纳秒到几微秒。
  2. 调度。在线程由于Thread.sleep而被调度后,在可能与定时器相关的定时器事件后,它将被安排再次执行用于System.nanoTime
  3. 在这两种情况下,您无法直接解决此问题 - 我的意思是Thread.sleep也会影响您的实际应用中的时间安排。但是,如果测得的有用工作量足够大,那么不准确性就可以忽略不计了。

答案 2 :(得分:0)

不一致可能不是来自Java,而是来自不同的操作系统和虚拟机“atomic - ”或系统时钟本身。

根据官方.nanoTime()文件:

  

除了分辨率至少同样好之外,不做任何保证   就像currentTimeMillis()

一样

source

...我可以从个人的角度知道这是因为在某些操作系统和虚拟机中,系统本身不支持“原子”时钟,这是更高分辨率所必需的。 (我会在发现它后立即发布链接以获取此信息......已经很长时间了。)