tags:

views:

189

answers:

6

Consider these 2 pieces of code (you can assume execeptionObj is of type Object, but we know it's an instance of Throwable):

1)

logger.log(Level.ERROR, (Throwable) exceptionObj,
    ((Throwable) exceptionObj).getMessage());

2)

Throwable t = new Throwable((Throwable)exceptionObj);
logger.log(Level.ERROR, t, t.getMessage());

During a code review for a project I'm working on, one reviewer is saying that the first way is not as efficient as the second way because it involves 2 casts. I just wondered what you thought. It seems like creating a new instance would involve some overhead as well.

+6  A: 

Given the cost of an exception and merely of printing out the contents, I would suggest this debate is somewhat specious.

Brian Agnew
The cost of printing out stuff is often underestimated (by myself included) because it seems so trivial.
Bart van Heukelom
But the cost of "printing" here is irrespective of the cost of casting. The cast happens before the log method is ever reached, so I don't think your point is really valid here.
dcp
My point is that the cost of casting is subsumed by the cost of printing
Brian Agnew
Agreed, I was replying to Bart van Heukelom, sorry for the confusion.
dcp
+11  A: 

The two pieces of code do different things. In the second, you are no longer passing exceptionObj, but "an unspecified Throwable caused by exceptionObj". I don't think that's what you want.

Did you mean the first line of the second one to be:

Throwable t = (Throwable) exceptionObj;

It is very marginally more efficient, I'd guess, but would not let this decide the issue. Which is more readable is the key, and I think the (modified) second one is more readable.

Do you really have to case to Throwable? surely the logger takes a Throwable already?

Sean Owen
No, I meant what I have written. Here are the reviewer's exact words: "you'd better create a instance, using 2 casting is not efficient". And the cast is necessary because I'm using the following signature for the logger call: Log.log(Level, Throwable, String)
dcp
@dcp: casting twice will pretty much always be faster than creating an Object. Also, your "creating an instance" code doesn't compile.
Joachim Sauer
@dcp By using Sean's modification of your second example you reduce it to one cast. That has got to be better than creating an unnecessary object.
Kris
@dcp assuming he/she didn't mean "create a reference", then the reviewer is definitely wrong. Creating a new object is certainly slower than a cast. We're talking microseconds either way. The real issue is correctness -- you're tacking on useless info to the exception chain in the log.
Sean Owen
Upvoted for pointing out that readability is more important than premature (incorrect) assumptions about performance.
Adriaan Koster
+1  A: 
  1. you will notice very little to no performance difference between the two. Especially since it is exception handling code that will most likely not run in a tight loop
  2. Example 2 only compiles when exceptionObj is either a String or a Throwable. If it's a String, then the first example would not compile at all, if it's a Throwable, then your casts are unnecessary in the first example
  3. creating a new Throwable just to avoid casts is a terrible thing, because it changes what your code actually does.
Joachim Sauer
As I mentioned in my question, I already know it's a Throwable because I check it beforehand (using instanceof). But could you elaborate some on point #3, how does it change what the code does?
dcp
Because originally, you passed the original typed exception. When you create the new Throwable() you will pass a generic exception with its own brand new stack trace that contains your original exception as the cause.
PSpeed
@dcp: in one case you're pass a specific object (let's call it `x`) to a method, in another case, you pass another object to a method. Two different things.
Joachim Sauer
+2  A: 
Throwable t = (Throwable) exceptionObj;
logger.log(Level.ERROR, t, t.getMessage());
Bart van Heukelom
+4  A: 

Why not cast once and use twice?

Throwable t = (Throwable) exceptionObj;

It'll be difficult to tell the actual answer without profiling and using heavy load.

Padmarag
+1  A: 
logger.log(Level.ERROR, (Throwable) exceptionObj,
    ((Throwable) exceptionObj).getMessage());

This in theory will incur the cost of two type casts. In practice, the JIT compiler is may well optimize this to native code that performs only one typecast.

Throwable t = new Throwable((Throwable)exceptionObj);
logger.log(Level.ERROR, t, t.getMessage());

Here we replace two typecasts with one typecast and an object creation. However, the object that is created is a Throwable, and if you look at the constructor for Throwable you will notice that it calls fillInStackTrace(). This method creates a bunch of other objects to record information about the stack, and that is likely to be a lot more expensive than performing a (successful) type cast. Besides, there is a good chance that that typecast will have been optimized away.

Obviously, a thorough investigation would entail profiling the two versions of the code and examining the native code generated by the JIT compiler ...

(And as others have noted, the effect of the two pieces of code are different. The first one logs the original exception, and the second one logs a new Throwable with the original exception as a nested exception.)

Stephen C