|
The Java Specialists' Newsletter
Issue 129 2006-06-27
Category:
Exceptions
Java version: JDK 1.0 - 1.6 Fast Exceptions in RIFEby Dr. Heinz M. KabutzAbstract:
One of the tricks that Java allows us to employ is to change
the control flow of the application using exceptions. This
is generally strongly discouraged, since it makes the code
hard to decipher. In addition, exceptions are notoriously bad
at performance. Here is a trick used in RIFE to make this
work faster.
Welcome to the 129th edition of The Java(tm) Specialists' Newsletter. Thanks to all of you
who sent me impossible-to-solve-except-by-brute-force SuDoKu
puzzles :) With the help of my long-term friend Franz Mahrl, we
improved the SuDoKu algorithm significantly.
Upcoming Java Specialist Master Courses:
"This course embodies my Java knowledge and experience gained publishing 180 advanced Java newsletters, teaching hundreds of seminars and writing hundreds of thousands of lines of Java code." Heinz Kabutz, The Java Specialists NewsletterParis, France, Feb 9-12 2010, €2500 - click to sign up. Düsseldorf, Germany (in German), Mar 2-5 2010, €2500 - click to sign up. San Jose CA, Mar 16-19 2010, $3500 - click to sign up. Oslo, Norway, Apr 13-16 2010, Kr 24500 - click to sign up. Chania, Crete, May 25-28 2010, €2500 - click to sign up.
In-house courses if these dates or locations do not suit you - click here for more information. Fast Exceptions in RIFE
One of the cool new open-source web component frameworks that
has appeared is RIFE.
At the ServerSide Java Symposium, I attended the last few
minutes of a Birds-Of-A-Feather (BoF) with Geert Bevin, the
architect behind RIFE.
Geert mentioned something that piqued my interest: As part of
the continuations control in Java, they use exceptions to pass
the control back to the client. Or something like that. Now we
all know that when you construct an exception, it needs to build
up a stack trace. This process can slow you down, so they
change FastException to override fillInStackTrace() and do
nothing.
public class FastException extends RuntimeException {
public Throwable fillInStackTrace() {
return null;
}
}
I thought that was quite clever. Maybe not completely pure, in
that you are returning from a method call in an unexpected way.
In addition, this may cause the debugger to pause whenever the
exception is generated.
This brought back memories of a system that I wrote with my
friend Paul van Spronsen. We were trying to optimize every
byte, and so we kept an instance of the exception around for
reuse.
To try out the difference between the various approaches, we
defined a Thrower interface:
public interface Thrower {
public void causeException() throws Exception;
}
This is implemented in the SlowExceptionThrower, that simply
constructs a normal exception and throws that:
public class SlowExceptionThrower implements Thrower {
public void causeException() throws Exception {
throw new Exception();
}
}
The FastExceptionThrower makes an instance of the FastException,
which does not have a stack trace. We would expect this to be
faster than SlowExceptionThrower:
public class FastExceptionThrower implements Thrower {
public void causeException() {
throw new FastException();
}
}
The last one is a SuperFastExceptionThrower that holds an
instance of the exception and then simply throws that each time:
public class SuperFastExceptionThrower implements Thrower {
private static FastException exception = new FastException();
public void causeException() {
throw exception;
}
}
Since I am currently presenting the Java Performance Tuning
course, I would like
to introduce you to a tool that you can use to run
microbenchmarks.
JAMon is described on the Java
Performance Tuning website.
import com.jamonapi.*;
public class FastExceptionTest {
public static final int COUNT = 1000 * 1000;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
test(new SlowExceptionThrower());
test(new FastExceptionThrower());
test(new SuperFastExceptionThrower());
}
}
private static void test(Thrower t) {
Monitor mon = MonitorFactory.start(t.getClass().getName());
for (int i = 0; i < COUNT; i++) {
try {
t.causeException();
} catch (Exception ex) {}
}
mon.stop();
System.out.println(mon);
}
}
On my machine, I saw the following results:
SlowExceptionThrower: Avg=3,949 ms
FastExceptionThrower: Avg=332 ms
SuperFastExceptionThrower: Avg=253 ms
JAMon makes measuring microbenchmarks a bit more convenient. We
saw that caching exceptions for rethrowing later does have
performance advantages.
Kind regards
Heinz
Exceptions Articles
Related Java Course
|