Core Java
Can synchronization be optimised away?
Overview
There is a common misconception that because the JIT is smart and synchronization can be eliminated for an object which is only local to a method that there is no performance impact.
A test comparing StringBuffer and StringBuilder
These two classes do basically the same thing except one is synchronized (StringBuffer) and the other is not. It is also a class which is often used in one method to build a String. The following test attempts to determine how much difference using one other the other can make.
static String dontOptimiseAway = null; static String[] words = new String[100000]; public static void main(String... args) { for (int i = 0; i < words.length; i++) words[i] = Integer.toString(i); for (int i = 0; i < 10; i++) { dontOptimiseAway = testStringBuffer(); dontOptimiseAway = testStringBuilder(); } } private static String testStringBuffer() { long start = System.nanoTime(); StringBuffer sb = new StringBuffer(); for (String word : words) { sb.append(word).append(','); } String s = sb.substring(0, sb.length() - 1); long time = System.nanoTime() - start; System.out.printf("StringBuffer: took %d ns per word%n", time / words.length); return s; } private static String testStringBuilder() { long start = System.nanoTime(); StringBuilder sb = new StringBuilder(); for (String word : words) { sb.append(word).append(','); } String s = sb.substring(0, sb.length() - 1); long time = System.nanoTime() - start; System.out.printf("StringBuilder: took %d ns per word%n", time / words.length); return s; }
at the end prints with -XX:+DoEscapeAnalysis
using Java 7 update 10
StringBuffer: took 69 ns per word StringBuilder: took 32 ns per word StringBuffer: took 88 ns per word StringBuilder: took 26 ns per word StringBuffer: took 62 ns per word StringBuilder: took 25 ns per word
Testing with one million words doesn’t change the results significantly.
Conclusion
- While the cost of using synchronization is small, it is measurable and if you can use StringBuilder it is preferred as it states in the Javadocs for this class.
- In theory, synchronization can be optimised away, but it is yet to be the case even in simple cases.
Reference: Can synchronization be optimised away? from our JCG partner Peter Lawrey at the Vanilla Java blog.
Micro-benchmarking is hard. Your numbers are not reflecting the results of the full-optimization of the JIT. If you run the tests 10000 times instead of 10 times the numbers change! On my computer: Initially: StringBuffer: took 67 ns per word StringBuilder: took 18 ns per word StringBuffer: took 64 ns per word StringBuilder: took 19 ns per word … but the on run 10000: StringBuffer: took 42 ns per word StringBuilder: took 28 ns per word StringBuffer: took 39 ns per word StringBuilder: took 29 ns per word The gap is much smaller. To get more consistent numbers, use a… Read more »
Well, I’d still use the faster class anyway. It’s good to know that if my method is run very often it will get faster, but if it does not run that often I want those extra milliseconds to do something else :-)
I wonder why synchronization was added in Java at the class level. When you are writing a class, you don’t know if it’s going to be used in local variables or in a static shared variable, so how can you decide about synchronization?