Core Java

Let’s pause for a Microsecond

A lot of benchmarks in low latency Java applications involve having to measure a system under a certain load. This requires maintaining a steady throughput of events into the system as opposed to pumping events into a system at full throttle with no control whatsoever.

One of the tasks I often have to do is pause a producer thread for a short period inbetween events. Typically this amount of time will be single digit microseconds.

So how do you pause a Thread for this amount of time?  Most Java developers think instantly of Thread.sleep(). But that’s not going to work because Thread.sleep() only goes down to milliseconds and that’s an order of magnitude longer than the amount of time required for our pause in microseconds.

I saw an answer on StackOverflow pointing the user to TimeUnit.MICROSECONDS.sleep() in order to sleep for less than a millisecond.  This is clearly incorrect, to quote from the JavaDoc:

Performs a Thread.sleep using this time unit. This is a convenience method that converts time arguments into the form required by the Thread.sleep method.

So you’re not going to be able to get better than a 1 millisecond pause , similar to Thread.sleep(1). (You can prove this trying the example on the code below).

The reason for this is that this method of pausing, namely putting a thread to sleep and waking it up, is never going to be fast or accurate enough to go lower than a millisecond.

Another question we should be introducing at this point is how accurate is Thread.sleep(1) anyway? We’ll come back to this in later.

Another option when we want to pause for a microsecond is to use LockSupport.parkNanos(x).  Using the following code to park for 1 microsecond actually takes ~10us.  It’s way better than TimeUnit.sleep() / Thread.sleep() but not really fit for purpose.  After 100us it does get into the same ball park with only a 50% variation.

package nanotime;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * Created by daniel on 28/10/2015.
 */
public class NanoTimer {
    public static void main(String[] args) throws InterruptedException {
        long[] samples = new long[100_000];
        int pauseInMillis = 1;

        for (int i = 0; i < samples.length; i++) {
            long firstTime = System.nanoTime();
            LockSupport.parkNanos(pauseInMicros);
            long timeForNano = System.nanoTime() - firstTime;
            samples[i] = timeForNano;
        }

        System.out.printf("Time for LockSupport.parkNanos() %.0f\n", Arrays.stream(samples).average().getAsDouble());
    }
}

The answer to our problems is to use System.nanoTime(). By busy waiting on a call to System.nanoTime we will be able to pause for a single microsecond.  We’ll see the code for this in a second but first let’s understand the accuracy of System.nanosecond(). Critically, how long does it take to perform the call to System.nanoSecond().

Here’s some code that will do exactly this:

package nanotime;

public class NanoTimer {
    public static void main(String[] args) throws InterruptedException {
        long[] samples = new long[1_000_000];

        for (int i = 0; i < samples.length; i++) {
            long firstTime = System.nanoTime();
            long timeForNano = System.nanoTime() - firstTime;
            samples[i] = timeForNano;
        }

        System.out.printf("Time for call to nano %.0f nanseconds", Arrays.stream(samples).average().getAsDouble());
    }
}

The numbers will vary from one machine to another on my MBP I get ~40 nanoseconds.

That tells us that we should be able to measure to an accuracy of around 40 nanoseconds. Therefore, measuring 1 microsecond (1000 nanoseconds) should easily be possible.

This is the busy waiting approach ‘pausing’ for a microsecond:

package nanotime;

import java.util.Arrays;
/**
 * Created by daniel on 28/10/2015.
 */
public class NanoTimer {
    public static void main(String[] args) throws InterruptedException {
        long[] samples = new long[100_000];
        int pauseInMicros = 1;

        for (int i = 0; i < samples.length; i++) {
            long firstTime = System.nanoTime();
            busyWaitMicros(pauseInMicros);
            long timeForNano = System.nanoTime() - firstTime;
            samples[i] = timeForNano;
        }

        System.out.printf("Time for micro busyWait %.0f\n", Arrays.stream(samples).average().getAsDouble());

    }

    public static void busyWaitMicros(long micros){
        long waitUntil = System.nanoTime() + (micros * 1_000);
        while(waitUntil > System.nanoTime()){
            ;
        }
    }
}

The code waits for a microsecond and then times how long it has waited.  On my machine I get 1,115 nanoseconds which is within ~90% accurate.

As you wait longer the accuracy increases, 10 microseconds takes 10,267 which is ~97% accurate and 100 microseconds takes 100,497 nanoseconds which is ~99.5% accurate.

What about Thread.sleep(1), how accurate is that?

Here’s the code for that:

package nanotime;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * Created by daniel on 28/10/2015.
 */
public class NanoTimer {
    public static void main(String[] args) throws InterruptedException {
        long[] samples = new long[100_000];
        int pauseInMillis = 1;

        for (int i = 0; i < samples.length; i++) {
            long firstTime = System.nanoTime();
            Thread.sleep(pauseInMicros);
            long timeForNano = System.nanoTime() - firstTime;
            samples[i] = timeForNano;
        }

        System.out.printf("Time for micro sleep %.0f\n", Arrays.stream(samples).average().getAsDouble());
    }
}

The average time in nanoseconds for 1 millisecond sleep is 1,295,509.  That only ~75% accurate.  It’s probably good enough for nearly everything but if you want an exact millisecond pause you are far better off with a busy wait.  Of course you need to remember that busy waiting, by definition keeps your thread busy and will costs you a CPU.

Summary Table

Pause Method1us10us100us1000us/1ms10,000us/10ms
TimeUnit.Sleep()1284.61293.81295.71292.711865.3
LockSupport.parkNanos()8.128.4141.81294.311834.2
BusyWaiting1.110.1100.21000.210000.2

Conclusions

  • If you want to pause for less than a millisecond you need to busy wait
  • System.nanoSecond() takes ~40ns
  • Thread.sleep(1) is only 75% accurate
  • Busy waiting on more than 10us and above is almost 100% accurate
  • Busy waiting will tie up a CPU
Reference: Let’s pause for a Microsecond from our JCG partner Daniel Shaya at the Rational Java blog.

Daniel Shaya

Daniel has been programming in Java since it was in beta. Working predominantly in the finance industry he has created real time trading and margin risk applications. He is currently a director at OpenHFT where we are building next generation Java low latency products.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Mike Vander Pluym
Mike Vander Pluym
8 years ago

Thanks for the great article. Btw, just thought I would mention that I noticed that you referred to “System.nanosecond()” a couple times where it looked like you meant “System.nanoTime()”.

Daniel Shaya
Daniel Shaya
8 years ago

Indeed…. Can’t change it here but will update the source http://www.rationaljava.com

Back to top button