Sunday, April 7, 2013

JVM Biased Locking and micro benchmark

This is a short article that focuses on HotSpot JVM Biased Locking feature. Before explaining what is it, let's take a simple example:
package org.javabenchmark;

public class SynchronizeCounter {
    
    private static long count = 0l;
    
    public synchronized void increment() {
        count = count + 1;
    }
    
    public long getCount() {
        return count;
    }
}
This class manages a thread safe counter. The code uses the synchronized keyword to ensure that only one thread at a time can invoke the increment() method.

Now, a trivial micro benchmark with only one thread incrementing the counter:
package org.javabenchmark;

import org.junit.Test;

public class OneThreadCounterTest {

    @Test
    public void incrementCounterLoop() {
        
        SynchronizeCounter counter = new SynchronizeCounter();
        long time = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            counter.increment();
        }
        time = System.currentTimeMillis() - time;
        System.out.printf("Counter: %d - Elapsed time: %d ms\n", counter.getCount(), time);
    }
}

The output is: Counter: 10000000 - Elapsed time: 67 ms

Next, the same test but with a 4.5s wait before:
package org.javabenchmark;

import org.junit.Test;

public class OneThreadCounterTest {

    @Test
    public void incrementCounterLoop() throws InterruptedException {

        Thread.sleep(4500);
        
        SynchronizeCounter counter = new SynchronizeCounter();
        long time = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            counter.increment();
        }
        time = System.currentTimeMillis() - time;
        System.out.printf("Counter: %d - Elapsed time: %d ms\n", counter.getCount(), time);
    }
}

The output is: Counter: 10000000 - Elapsed time: 12 ms
Wow, that is a big improvement considering that the only difference between the two tests is the line with Thread.sleep(..).
So, what happened ?

In the second case, the code benefits from the biased locking: we are in the context of un-contended lock, meaning that a lock mechanism is used but without any concurrency (single thread). In that case, the JVM uses biased locking and the running thread does not have to rely on expensive atomic instructions to acquire the lock.

Biased locking is enabled by default but only after 4 seconds, that is why the second test benefits from it. You can suppress this 4 seconds wait with the following JVM option:
-XX:BiasedLockingStartupDelay=0

Conclusion

When dealing with un-contended micro-benchmark be aware of the biased locking startup delay and use the previous JVM option to disabled it.

2 comments:

  1. I am running your test on the 64 bit windows 7 machine and i don't see any difference in the elapsed time with or without the wait delay. Has something changed in Java7??

    ReplyDelete
    Replies
    1. I just tried with Java 7:

      java version "1.7.0_17"
      Java(TM) SE Runtime Environment (build 1.7.0_17-b02)
      Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)


      Counter: 10000000 - Elapsed time: 63 ms
      Counter: 10000000 - Elapsed time: 20 ms

      Are you testing with an Oracle JDK ?

      Delete