Running on Java 17-ea+15-1230 (Preview)
Home of The JavaSpecialists' Newsletter

129Fast Exceptions in RIFE

Author: Dr. Heinz M. KabutzDate: 2006-06-27Java Version: 1.3Category: Exceptions
 

Abstract: 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.

We have three upcoming LIVE virtual classes in April and May 2021:

  1. Refactoring to Streams and Lambdas for US$ 497 on April 6-7 2021 @ 9am-1pm Frankfurt Time.
  2. Extreme Java - Advanced Topics Java 17 Edition for EUR 1299 on April 19-20 2021 @ 9am-5pm Frankfurt Time. (almost sold out)
  3. Design Patterns Deep Dive for US$ 497 on May 11-12 2021 @ 7-11am Los Angeles Time.

My favourite course at the moment is the Refactoring to Streams and Lambdas course. We spend 8 hours ripping apart a 330k LOC application and replacing bits with more modern code. Too much fun! We still have a few places available for next week. The Advanced Topics Course is also very interesting. It is almost sold out though, so please grab the seats if you would like to join.

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

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack 21

Superpack 21 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...

Java Emergency?

If your system is down, we will review it for 15 minutes and give you our findings for just 1 € without any obligation.