It's perfect.
It's unbelievable.
It's a miracle
It's a TV dinner.
It's Fuwjax.

Email Twitter Facebook Google+ LinkedIn Github Stackoverflow Steam Youtube Creative Commons License

Maybe you can tell from my previous post that I’m completely fed up with Java Exception FUD. I attempted to point out my major reasons behind using exceptions liberally without directly attacking the arguments from the anti-exception folks. I can’t even begin to address all the ridiculousness out there.

But perhaps you, gentle reader, don’t care about the pros or cons. You just want to see for yourself whether Exceptions are expensive or not. I told you my anecdotal evidence on performance of infrequent failure reporting performance efficiency. But as an informed, astute reader you really care about performance where it matters, in the high frequency, high failure case. You deserve an answer. But you deserve one that actually matters to you. I run my tests on a 4 year old Mac Book Pro. It’s Intel, but it’s only 2 cores. And it looks absolutely nothing like your high end Linux server blade, or your Windows 7 VM running on a virtual service environment on who-knows-what box in central Connecticut.

So who cares about my anecdotal evidence or some fancy numbers somewhere. Here is my microbenchmark for Java Exceptions. Why not see what it looks like on your environment?

Just in case you don’t want to investigate the code, let me explain the output, in particular the table. There are 6 strategies, each with a “Time” and a “Fail %”. The “Fail %” is really just a check to make sure I haven’t done anything too crazy on the implementation, so don’t worry as much about that. The key is the “Time”; this is the median of the batch average execution time for batches after the warm up period. Roughly speaking, this is a best-we-can-get-but-still-biased indicator for the 50th percentile of the distribution of execution times for each of the strategies.

The key here is that you can’t place too much weight on the relative values until they start getting consistently higher or lower. I’ll try to work out a confidence test for comparisons between the strategies, but I’m not sure that there’s a decent test for median averages. At any rate, try running the test a dozen or so times to get a feel for the variance. Keep in mind that the time units are in nanoseconds, so we’re a little limited in how we can generate these times within the JVM.

The strategies are as follows: The Control strategy generates a validation failure message but does not detect whether the returned value is a success or failure. The Detect strategy does not generate any failure information, but does detect failure by checking for null. The Exception strategy throws a runtime exception with the failure message. The NoTrace strategy also throws a runtime exception, but that exception overrides fillInStackTrace() to disable stack tracing. The Result strategy returns a typed result object with an isSuccess() method. The Sentinel strategy returns two different types, the valid value on success and the failure information on failure, and expects the caller to perform an instanceof check to determine success.

To summarize my personal results on my computer, There is little cost surrounding building the failure information string (compare Control to Detect), as long as the values being concatenated are already strings. In other words, StringBuilder is pretty optimized. There is little cost in generating a new object that is immediately used in a return/throw statement (compare NoTrace, Result, and Sentinel). My assumption is that this is due to the new performance optimizations surrounding the allocation of objects and/or the fact that object creation/access can now be inlined. The performance of raw exceptions is about 2 to 3 times worse than the other strategies pretty much all the time, but disabling the stack trace gives very comparable performance until the failure rate is about 25% or so. Since disabling the stack trace requires a subclass (without any bytecode manipulation), this can only be used effectively on locally written exceptions. In particular, when creating business failure exceptions, simply overloading this method will give comparable performance to other solutions while using the language feature exactly as it is intended.

Posted with : De Machina