読者です 読者をやめる 読者になる 読者になる

Thread.sleep(0) のオーバーヘッドがヤバイ件

Java

あるプログラムがどうも遅いので、調べてみると Thread.sleep(long) が怪しかった。もちろん、スリープしているならプロファイルで遅い結果が出てくるのも頷けるが、Thread.sleep に渡していた値は 0。
どうやら、Thread.sleep(0) のオーバーヘッドは予想以上に大きいらしい。


オーバーヘッドを調べる方法として、ループ内で実行して実行時間を測ることにした。まず何も処理しない版のループ。

public static void main(String[] args) {
    long s = System.currentTimeMillis();
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
    }
    long e = System.currentTimeMillis();
    System.out.println(e - s + "ms");
}

// 実行結果
3ms

3ms。

ここに、Thread.sleep(0) を入れて実行する。

public static void main(String[] args) {
    long s = System.currentTimeMillis();
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        Thread.sleep(0);
    }
    long e = System.currentTimeMillis();
    System.out.println(e - s + "ms");
}

// 実行結果
580473ms

遅すぎる!!!!!!!!!
その時間なんと 580 秒、10分近い。あまりの遅さに愕然とした。



で、改善版。渡された時間が 0 なら return する if 文を入れた関数でくるんであげると、劇的に改善する。実質何もしない関数呼び出しの処理しか実行されず、オーバーヘッドはかなり少ない。

public static void main(String[] args) throws IOException, InterruptedException {
    long s = System.currentTimeMillis();
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        mySleep(0);
    }
    long e = System.currentTimeMillis();
    System.out.println(e - s + "ms");
}

private static void mySleep(long time) throws InterruptedException {
    if (time == 0)
        return;

    Thread.sleep(time);
}

// 実行結果
4ms

まとめ

Thread.sleep(0) だからスリープなし版と同じ速度だぜ!!!というのは嘘。
0 を入れても遅い。