Thursday, November 20, 2008

Exception vs. Error Code - round 1

Many times I have heard this argument in my career that throwing exception is too expensive therefore high performance code should always favor returning error code over exception. While it was probably true on early-day JVM, I am still not a big fan of this. First of all in my opinion the biggest drawback of extensive usage of error code is that you lose the most powerful tool that allows you to highlight your main flow from the exceptional ones. Since the main flow is probably the most valuable, most executed and most read code you should always thrive to make the main flow as easy to identify as possible. As Kent Beck suggested in his Implementation Pattern book:

Expressing all paths equally would result in a bowl of worms, with flags set here and used there and return values with special meanings. Answering the basic question, "What statements are executed?" becomes an exercise in a combination of archaeology and logic. Pick the main flow. Express it clearly. Use exceptions to express other paths.

Now consider the following code sample written using error code:


def input = input()
if(input.errorCode == IO_ERROR)
return new Result(IO_ERROR, ...)
// could be more error types here

def processResult = process(input)
if(processResult.errorCode == IO_ERROR)
return new Result(IO_ERROR, ...)
// could be more error types here

def output = output()
if(output.errorCode == IO_ERROR)
return new Result(IO_ERROR, ...) // could be more error types here
else
return new Result(SUCCESS)


If we can rewrite it using Exception, it will look like this:


input()
process()
output()


* The code sample is in Groovy so the error code version is already somewhat shorter than the Java version. Although the Exception version will stay pretty much the same in both Groovy and Java.

As you can see it is obvious using Exception greatly simplify the main flow, and make it so much easier to read hence cheaper to maintain and easier to improve. But how about the performance argument that we always hear. Well... modern JVM is highly optimized for exception handling, although optimization strategy varies between vendors, some strategy focus on optimizing the normal path, others focus on the exceptional paths such as EDO, and the newest ones are adaptive at the runtime and smart enough to pick the right strategy for the specific scenario. In a simple test it took 30ms to throw 10,000 exceptions on my 1.6_07 32 bit JDK with Hotspot 10. Also remember cleaner code makes it much easier to pin-point performance bottle-neck and make improvement. In most cases, 90% of the performance slow down is usually caused by 3% of the code and in my experience the culprit was never the exception handling, and if it is then you have a much bigger design issue at hand than just merely exception handling.

What if someone argues that their main flow actually triggers the exception path way too often and it is causing significant performance degredation, for example during a DOS attack. My suggestion? Simple! If thats the case, your code is telling you this exception path is actually not exceptional but rather part of the main flow. For example if you server is expected to withstand a DOS attack then you can't really treat certain corrupted packets or pre-maturely dropped connection as exceptional cases anymore. In other words, if exception handling is causing you performance problem, you better rethink how the system is designed in the first place instead of simply replacing it with error code.

Last but not least, I would like to make it clear that I am NOT suggesting here that you should use exception as your first choice to control your flow, you should always use sequence, message, condition, iteration, and exception (in this order) to control your flow. Use exception to handle only the exceptional cases but don't simply dismiss it because some out-dated misunderstood performance concerns.

No comments: