Explicit catches versus bulk catch + type check?

Tom Rodriguez tom.rodriguez at oracle.com
Fri Oct 14 12:24:16 PDT 2011


On Oct 13, 2011, at 9:01 PM, Charles Oliver Nutter wrote:

> Ok Hotspot guys, I have a challenge for the JIT. I want to basically do this:
> 
> try {
>  ...
> } catch (Exception e) {
>  if (!(e instanceof SomeException)) throw e;
>  ... handle e as SomeException
> }
> 
> instead of explicitly adding exception-handling for SomeException.
> There's a few reasons for this:
> 
> * Wiring up exception-handling in method handles is rather cumbersome.
> If I could do it with a single wrapper it would make my life easier.
> * Similar to the "multicatch" problem in Java 7, I often want nearly
> the same boilerplate code for multiple exception types.
> * Because Ruby is dynamically typed, we can't statically determine the
> *actual* expected exception type at compile time, and so we must do a
> bulk catch with type checks.
> 
> My question, then, is whether Hotspot can see through this (assuming
> everything inlines) and see that although we're doing a bulk catch
> only certain cases *actually* handle the resulting exception.

Are you expecting some performance penalty from doing this?  Generally speaking if you are throwing exceptions at all then you've already lost a bunch of performance.  The lookups and unwinding are probably negligible in the general case.

If the exception is thrown from a callee, out of sight of the current compilation, then the only thing that's used for deciding where to dispatch is the exception handler table in the method.  There JIT isn't even involved in that case until the exception reaches the current nmethod.  Take a look at exception_handler_for_return_address, which finds the PC to dispatch to in the caller frame.  

So in your case you might jump to the exception handler, do the type check and then simply unwind as you would have before.  C2 does something similar to what you've done when dispatching exceptions.  A single try with multiple catches sends all exceptions to a single entry point in the compiled code which then type checks them and dispatches to the right location, as you've done.  catch_inline_exceptions in doCall.cpp does this.  C1 relies on the exception handler table and dispatches them individually, with a separate entry point for each type mentioned in a catch.  For locally thrown and caught exceptions C2 maybe convert that directly into control flow and possibly simplify type checks along the way.  C1 still goes the long route.

So basically I would be surprised if doing what you're doing would hurt the speed of throwing and catching exceptions by a measurable amount in real programs.

Where it might hurt you is with compiled code complexity, assuming that you are spreading your try/catch over large blocks of code that might not really need handlers.  Remember that every exception handler creates extra control flow edges which can complicate code generation.  This is very hard to quantify and may or may not matter but it seemed worth mentioning.  Basically it can increase compile time and possibly result in worse register allocations but that's a very vague worry.

Anyway, if you're worried abut it, I would suggested writing some test cases that match what your usage and see if you can measure differences.  There could be pathologies here we're unaware of.

tom

> 
> Thoughts?
> 
> - Charlie
> _______________________________________________
> mlvm-dev mailing list
> mlvm-dev at openjdk.java.net
> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev



More information about the mlvm-dev mailing list