# Monday, 30 June 2008
« Exception Performance Part 1 | Main | PDC »
Exception Performance Part 2

Last time we saw that CLR exception handling is significantly slower than HotSpot exception handling. This time we'll look at two very variations of the ExceptionPerf1 microbenchmark that significantly affect performance.

I've highlighted the changes.

Variation 1

public class ExceptionPerf2 {
  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
      try {
        Integer.parseInt("");
      }
      catch (NumberFormatException x) {
      }
    }
    long end = System.currentTimeMillis();
    System.out.println(end - start);
  }
}

Variation 2

public class ExceptionPerf3 {
  static NumberFormatException exc;
  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
      try {
        Integer.parseInt(null);
      }
      catch (NumberFormatException x) {
        exc = x;
      }
    }
    long end = System.currentTimeMillis();
    System.out.println(end - start);
  }
}

Results:

      HotSpot 1.6 x86     .NET 1.1 SP1     .NET 2.0 SP1 x86     Mono 1.9 x86
ExceptionPerf1 111 14743 3590 537
ExceptionPerf2 140 15735 10761 36309
ExceptionPerf3 112 14946 9728 24107


.NET/Mono results with IKVM 0.36

Why do these small changes have such a big perf impact?

Both these changes result in additional stack trace data being collected. IKVM has some optimizations that prevent gathering stack traces in very specific circumstances. Normally when you create a Java exception object, the Throwable constructor will call Throwable.fillInStackTrace(). However, since this is a very expensive operation, IKVM tries to remove this call when it is unnecessary (i.e. when it sees that you immediately throw the exception and the exception type doesn't override Throwable.fillInStackTrace()). Additionally, in Java an exception object will always have the complete stack trace, but a .NET exception only has the stack frames from the throw to the catch site. This means that at the catch site IKVM will collect the rest of the stack trace, unless the exception object isn't used (as in the ExceptionPerf1 microbenchmark).

The time it takes to collect a stack traces obviously depends on the call stack depth, so let's look at a microbenchmark to measure that effect:

class ExceptionPerf4 {
  public static void main(String[] args) {
    nest(Integer.parseInt(args[0]));
  }

  static void nest(int depth) {
    if (depth > 0) {
      nest(depth - 1);
    } else {
      run();
    }
  }

  static void run() {
    Exception x = new Exception();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
      x.fillInStackTrace();
    }
    long end = System.currentTimeMillis();
    System.out.println(end - start);
  }
}

Results:

Depth     HotSpot 1.6 x86     .NET 1.1 SP1     .NET 2.0 SP1 x86     Mono 1.9 x86
1 64 2930 4611 19377
10 85 3814 6787 34895
100 380 12500 27935  
1000 3543      

For the curious, the IKVM implementation of Throwable.fillInStackTrace() is essentially new System.Diagnostics.StackTrace(true);

Next time we'll wrap things up.

Monday, 30 June 2008 09:22:06 (W. Europe Daylight Time, UTC+02:00)  #    Comments [1]