Skip to content

Instantly share code, notes, and snippets.

@bric3
Created September 7, 2016 09:45
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bric3/314c3d01a80e5e3c158965dcd459a8a5 to your computer and use it in GitHub Desktop.
Save bric3/314c3d01a80e5e3c158965dcd459a8a5 to your computer and use it in GitHub Desktop.
Trying different ways to accurately sleep in java
public class Sleeper {
private static final long SLEEP_PRECISION = TimeUnit.MILLISECONDS.toNanos(2);
private static final long SPIN_YIELD_PRECISION = TimeUnit.MILLISECONDS.toNanos(2);
public static void main(String... args) throws InterruptedException {
final BlockingQueue SLEEPER = new ArrayBlockingQueue(1);
System.out.println("Blocking Queue");
for (int i = 0; i < 100; i++) {
long start = System.nanoTime();
SLEEPER.poll(20, TimeUnit.MILLISECONDS);
long time = System.nanoTime() - start;
System.out.printf("Sleep %5.1f%n", time / 1e6);
}
System.out.println("Thread.sleep");
for (int i = 0; i < 100; i++) {
long start = System.nanoTime();
TimeUnit.MILLISECONDS.sleep(20);
long time = System.nanoTime() - start;
System.out.printf("Sleep %5.1f%n", time / 1e6);
}
System.out.println("sleepNanos");
for (int i = 0; i < 100; i++) {
long start = System.nanoTime();
sleepNanos(20_000_000);
long time = System.nanoTime() - start;
System.out.printf("Sleep %5.1f%n", time / 1e6);
}
System.out.println("parkNanos");
for (int i = 0; i < 100; i++) {
long start = System.nanoTime();
LockSupport.parkNanos(20_000_000);
long time = System.nanoTime() - start;
System.out.printf("Sleep %5.1f%n", time / 1e6);
}
}
/* Spin-yield loop based alternative to Thread.sleep
* Based on the code of Andy Malakov
* http://andy-malakov.blogspot.fr/2010/06/alternative-to-threadsleep.html
*/
public static void sleepNanos(long nanoDuration) throws InterruptedException {
final long end = System.nanoTime() + nanoDuration;
long timeLeft = nanoDuration;
do {
if (timeLeft > SLEEP_PRECISION) {
Thread.sleep(1);
} else {
if (timeLeft > SPIN_YIELD_PRECISION) {
Thread.yield();
}
}
timeLeft = end - System.nanoTime();
if (Thread.interrupted())
throw new InterruptedException();
} while (timeLeft > 0);
}
}
@bric3
Copy link
Author

bric3 commented Sep 7, 2016

The sleepNanos method seems to be the most accurate, but may consume more CPU, to be HDRHistogramed

@ChristianSchwarz
Copy link

@bric3

This approch is precise but wastes alot CPU time. On my machine the stopwatch showed always 20ms.

System.out.println("burn");
for (int i = 0; i < 100; i++) {
    long start = System.nanoTime();
    burn(20);
    long time = System.nanoTime() - start;
    System.out.printf("Sleep %5.1f%n", (time / 1e6));
}
private static void burn(int millis) {
    long deadline = System.nanoTime()+TimeUnit.MILLISECONDS.toNanos(millis);
    while(System.nanoTime()<deadline){};
}

@ChristianSchwarz
Copy link

I have rewritten the test a little bit. Now it prints min,max and average derivation in ms.

https://gist.github.com/ChristianSchwarz/2dbe3e2a15572b3927735569d2a35704

This are my results:

Blocking Queue
Min 7,0
Max 13,6
Avr 11,2

Thread.sleep
Min 6,0
Max 15,2
Avr 11,1

sleepNanos
Min 0,0
Max 14,6
Avr 2,4

parkNanos
Min 10,5
Max 11,9
Avr 11,2

burn
Min 0,0
Max 0,0
Avr 0,0

@Danstahr
Copy link

Danstahr commented Sep 14, 2018

Sorry for resurrecting an old thread.

For the burn() method, it's worth noting that it's a kind of a self fulfilling prophecy as you're using nanoTime() to measure the time elapsed. nanoTime() itself has its cost and skew (more details in https://shipilev.net/blog/2014/nanotrusting-nanotime/#_timers) and while it's - in most cases - the best approximation of actual time we have, it is not the actual time itself. So saying there's no error is a (while) lie 😄 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment