From: Doug Lea To: jch@cs.cmu.edu Subject: minor synchronization performance tweak Date: Thu, 21 Nov 1996 09:53:14 -0500 Hi; you might be interested in this for your optimization pages. Even though synchronized void foo() { anything(); } and void foo() { synchronized(this) { anything(); } } are defined as equivalent in Java, the first way is slightly but reliably faster in JDK1.02. This is because methods that are declared as synchronized set the ACC_SYNCHRONIZED flag in the .class file, in which case, synch is automatically performed by the interpretor upon call, return, and, if applicable, throw/catch. On the other hand, if the method itself is not declared as synchronized, the synchronization is arranged by having the compiler surround code with monitorenter and monitorexit bytecodes that are interpreted one-by-one as encountered. This takes more code space and is slower. I'll append a little test file that demonstrates about a 10% speed advantage in the case where the called method only executes a simple line of code. Lots of other variations are also included just out of curiosity; for example: * checking interaction with `final' and use of interfaces (answer: not much) * checking if adding the need for try/catches accentuates differences. (answer: not much difference). * checking if it is faster to internally call a synchronized final utility method than to have a synchronized block (answer: to close to call). A couple of sample runs are listed at the end. Of course, this is not a very dramatic effect, but it is the only performance tweak I know of that applies to synchronized methods. I wonder if the same patterns hold for JIT-produced code? -- Doug Lea | dl@cs.oswego.edu | dl@cat.syr.edu | 315-341-2688 | FAX 315-341-5424 Computer Science Dept, SUNY Oswego, Oswego, NY 13126 | http://g.oswego.edu/dl/ Also: Software Eng. Lab, NY CASE Center, Syracuse Univ, Syracuse NY 13244 /* File: T1121.java Some simple performance tests last update: Thu Nov 21 09:13:10 1996 Doug Lea (dl at gee) */ // just an arbitrary exception class class E1121 extends Exception {} // an interface describing the methods in the following class interface I1121 { // implement as final methods public void finalSynch(); // declare method as synchronized public void finalBlock(); // use block synchronization public void finalUnsynch(); // no synchronization // implement as non-final methods public void nonfinalSynch(); public void nonfinalBlock(); public void nonfinalUnsynch(); // implement by calling final version public void relaySynch(); public void relayBlock(); public void relayUnsynch(); // declare, but never throw E1121 public void finalSynchCanThrow() throws E1121; public void finalBlockCanThrow() throws E1121; public void finalUnsynchCanThrow() throws E1121; public void nonfinalSynchCanThrow() throws E1121; public void nonfinalBlockCanThrow() throws E1121; public void nonfinalUnsynchCanThrow() throws E1121; public void relaySynchCanThrow() throws E1121; public void relayBlockCanThrow() throws E1121; public void relayUnsynchCanThrow() throws E1121; } // The class holding all the versions of the method class C1121 implements I1121 { long v1_ = 0; // All methods implement: if (v1_ >= 0) ++v1_; // where v1_ is never < 0 public final synchronized void finalSynch() { if (v1_ >= 0) ++v1_; } public final void finalUnsynch() { if (v1_ >= 0) ++v1_; } public final void finalBlock() { synchronized(this) { if (v1_ >= 0) ++v1_; } } public void nonfinalUnsynch() { if (v1_ >= 0) ++v1_; } public synchronized void nonfinalSynch() { if (v1_ >= 0) ++v1_; } public void nonfinalBlock() { synchronized(this) { if (v1_ >= 0) ++v1_; } } public void relayUnsynch() { finalUnsynch(); } public void relaySynch() { finalSynch(); } public void relayBlock() { finalBlock(); } public final void finalUnsynchCanThrow() throws E1121 { if (v1_ >= 0) ++v1_; else throw new E1121(); } public final synchronized void finalSynchCanThrow() throws E1121 { if (v1_ >= 0) ++v1_; else throw new E1121(); } public final void finalBlockCanThrow() throws E1121 { synchronized(this) { if (v1_ >= 0) ++v1_; else throw new E1121(); } } public void nonfinalUnsynchCanThrow() throws E1121 { if (v1_ >= 0) ++v1_; else throw new E1121(); } public synchronized void nonfinalSynchCanThrow() throws E1121 { if (v1_ >= 0) ++v1_; else throw new E1121(); } public void nonfinalBlockCanThrow() throws E1121 { synchronized(this) { if (v1_ >= 0) ++v1_; else throw new E1121(); } } public void relaySynchCanThrow() throws E1121 { finalSynchCanThrow(); } public void relayBlockCanThrow() throws E1121 { finalBlockCanThrow(); } public void relayUnsynchCanThrow() throws E1121 { finalUnsynchCanThrow(); } } public class T1121 { static final long n = 1000000; // loop iterations per test public static void main(String[] args) { C1121 t = new C1121(); I1121 it = t; // to allow calls via interface long start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) t.finalUnsynch(); System.out.println("unsynch\tfinal\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) t.nonfinalUnsynch(); System.out.println("unsynch\tnofinal\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) it.nonfinalUnsynch(); System.out.println("unsynch\tifc\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) t.relayUnsynch(); System.out.println("unsynch\trelay\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) t.finalUnsynchCanThrow(); } catch (E1121 ex) {} System.out.println("unsynch\tfinal\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) t.nonfinalUnsynchCanThrow(); } catch (E1121 ex) {} System.out.println("unsynch\tnofinal\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) it.nonfinalUnsynchCanThrow(); } catch (E1121 ex) {} System.out.println("unsynch\tifc\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) t.relayUnsynchCanThrow(); } catch (E1121 ex) {} System.out.println("unsynch\trelay\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) t.finalSynch(); System.out.println("synch\tfinal\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) t.nonfinalSynch(); System.out.println("synch\tnofinal\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) it.nonfinalSynch(); System.out.println("synch\tifc\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) t.relaySynch(); System.out.println("synch\trelay\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) t.finalSynchCanThrow(); } catch (E1121 ex) {} System.out.println("synch\tfinal\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) t.nonfinalSynchCanThrow(); } catch (E1121 ex) {} System.out.println("synch\tnofinal\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) it.nonfinalSynchCanThrow(); } catch (E1121 ex) {} System.out.println("synch\tifc\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) t.relaySynchCanThrow(); } catch (E1121 ex) {} System.out.println("synch\trelay\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) t.finalBlock(); System.out.println("block\tfinal\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) t.nonfinalBlock(); System.out.println("block\tnofinal\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) it.nonfinalBlock(); System.out.println("block\tifc\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (long i = 0; i < n; ++i) t.relayBlock(); System.out.println("block\trelay\tnoex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) t.finalBlockCanThrow(); } catch (E1121 ex) {} System.out.println("block\tfinal\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) t.nonfinalBlockCanThrow(); } catch (E1121 ex) {} System.out.println("block\tnofinal\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) it.nonfinalBlockCanThrow(); } catch (E1121 ex) {} System.out.println("block\tifc\tex:\t " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); try { for (long i = 0; i < n; ++i) t.relayBlockCanThrow(); } catch (E1121 ex) {} System.out.println("block\trelay\tex:\t " + (System.currentTimeMillis() - start) + "ms"); } } /* Sample runs: (ultrasparc 170, solaris 2.5, JDK1.02) % java T1121 unsynch final noex: 3902ms unsynch nofinal noex: 5040ms unsynch ifc noex: 5028ms unsynch relay noex: 4883ms unsynch final ex: 4180ms unsynch nofinal ex: 4831ms unsynch ifc ex: 4890ms unsynch relay ex: 5542ms synch final noex: 11786ms synch nofinal noex: 11396ms synch ifc noex: 11892ms synch relay noex: 13289ms synch final ex: 11299ms synch nofinal ex: 11221ms synch ifc ex: 11649ms synch relay ex: 12817ms block final noex: 13510ms block nofinal noex: 12986ms block ifc noex: 13597ms block relay noex: 14427ms block final ex: 13232ms block nofinal ex: 13216ms block ifc ex: 13604ms block relay ex: 14901ms % java T1121 unsynch final noex: 3895ms unsynch nofinal noex: 5050ms unsynch ifc noex: 5048ms unsynch relay noex: 4867ms unsynch final ex: 4192ms unsynch nofinal ex: 4844ms unsynch ifc ex: 4876ms unsynch relay ex: 5568ms synch final noex: 11788ms synch nofinal noex: 11440ms synch ifc noex: 11858ms synch relay noex: 13243ms synch final ex: 11287ms synch nofinal ex: 11228ms synch ifc ex: 11697ms synch relay ex: 12762ms block final noex: 13447ms block nofinal noex: 13017ms block ifc noex: 13472ms block relay noex: 14415ms block final ex: 13214ms block nofinal ex: 13185ms block ifc ex: 13569ms block relay ex: 14896ms */