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
*/