views:

578

answers:

10

Do you know how expensive exception throwing and handling in java is?

We had several discussions about the real cost of exceptions in our team. Some avoid them as often as possible, some say the loss of performance by using exceptions is overrated.

Today I found the following piece of code in our software:

private void doSomething()
{
    try
    {
      doSomethingElse();
    }
    catch(DidNotWorkException e)
    {
       log("A Message");
    }
    goOn();
}
private void doSomethingElse()
{
   if(isSoAndSo())
   {
      throw new DidNotWorkException();
   }
   goOnAgain();
}

How is the performance of this compared to

private void doSomething()
{
    doSomethingElse();
    goOn();
}
private void doSomethingElse()
{
   if(isSoAndSo())
   {
      log("A Message");
      return;
   }
   goOnAgain();
}

I don't want to discuss code aesthetic or anything, it's just about runtime behaviour! Do you have real experiences/measurements?

+6  A: 

Exceptions are not free... so they are expensive :-)

The book Effective Java covers this in good detail.

  • Item 39 Use exceptions only for exceptional conditions.
  • Item 40 Use exceptions for recoverable conditions

The author found that exceptions resulted in the code tunning 70 times slower for his test case on his machine with his particular VM and OS combo.

(sorry for the edits... had to find the exact quote and hit submit too soon by mistake)

TofuBeer
Logical fallacy - They could just be cheap!
alex
Nope... they are not :-) (but I had to find the quote...)
TofuBeer
+3  A: 

I have no real measurements, but throwing an exception is more expensive.

Ok, this is a link regarding the .NET framework, but I think the same applies to Java as well:

exceptions & performance

That said, you should not hesitate to use them when appropriate. That is : do not use them for flow-control, but use them when something exceptional happend; something that you didn't expect to happen.

Frederik Gheysels
Totally right -- if you use exceptions for exceptional cases, you're fine. If you throw several per second, it'll slow you down, because they just aren't designed for flow control like that.
ojrac
Hmm, why the downgrade ? If an answer is downgraded, you should motivate why.
Frederik Gheysels
+3  A: 

I think if we stick to using exceptions where they are needed (exceptional conditions), the benefits far outweigh any performance penalty you might be paying. I say might since the cost is really a function of the frequency with which exceptions are thrown in the running application.
In the example you give, it looks like the failure is not unexpected or catastrophic, so the method should really be returning a bool to signal its success status rather than using exceptions, thus making them part of regular control flow.
In the few performace improvement works that I have been involved in, cost of exceptions has been fairly low. You would be spending far more time time in improving the complexity of common, hightly repeating operations.

Mohit Chakraborty
+4  A: 

This is inherently JVM specific, so you should not blindly trust whatever advice is given, but actually measure in your situation. It shouldn't be hard to create a "throw a million Exceptions and print out the difference of System.currentTimeMillis" to get a rough idea.

For the code snippet you list, I would personally require the original author to thoroughly document why he used exception throwing here as it is not the "path of least surprises" which is crucial to maintaining it later.

(Whenever you do something in a convoluted way you cause unneccesary work to be done by the reader in order to understand why you did it like that instead of just the usual way - that work must be justified in my opinion by the author carefully explaining why it was done like that as there MUST be a reason).

Exceptions are a very, very useful tool, but should only be used when necessary :)

Thorbjørn Ravn Andersen
+1 for JVM specific. There were huge performance problems with try-catch blocks in Java 1.4 for example (mostly corrected in later releases).
cletus
+1 for JVM specific and actually testing it. Java has come a long way in speed.
Kiv
A: 

I think you're asking this from slightly the wrong angle. Exceptions are designed to be used to signal exceptional cases, and as a program flow mechanism for those cases. So the question you should be asking is, does the "logic" of the code call for exceptions.

Exceptions are generally designed to perform well enough in the use for which they are intended. If they're used in such a way that they're a bottleneck, then above all, that's probably an indication that they're just being used for "the wrong thing" full stop-- i.e. what you have underlyingly is a program design problem rather than a performance problem.

Conversely, if the exception appears to be being "used for the right thing", then that probably means it'll also perform OK.

Neil Coffey
+4  A: 

The slowest part of throwing an exception is filling in the stack trace.

If you pre-create your exception and re-use it, the JIT may optimize it down to "a machine level goto."

All that having been said, unless the code from your question is in a really tight loop, the difference will be negligible.

Ron
+3  A: 

Thank you for all the responses.

I finally followed Thorbjørn's suggestion and wrote a little test programm, measuring the performance myself. The result is: No difference between the two variants (in matters of performance).

Even though I didn't ask about code aesthetics or something, i.e. what the intention of exceptions was etc. most of you addressed also that topic. But in reality things are not always that clear... In the case under consideration the code was born a long time ago when the situation in which the exception is thrown seemed to be an exceptional one. Today the library is used differently, behaviour and usage of the different applications changed, test coverage is not very well, but the code still does it's job, just a little bit too slow (That's why I asked for performance!!). In that situation, I think, there should be a good reason for changing from A to B, which, in my opinion, can't be "That's not what exceptions were made for!".

It turned out that the logging ("A message") is (compared to everything else happening) very expensive, so I think, I'll get rid of this.

EDIT:

The test code is exactly like the one in the original post, called by a method testPerfomance() in a loop which is surrounded by System.currentTimeMillis()-calls to get the execution time...but:

I reviewed the test code now, turned of everything else (the log statement) and looping a 100 times more, than before and it turns out that you save 4.7 sec for a million calls when using B instead of A from the original post. As Ron said fillStackTrace is the most expensive part (+1 for that) and you can save nearly the same (4.5 sec) if you overwrite it (in the case you don't need it, like me). All in all it's still a nearly-zero-difference in my case, since the code is called 1000 times an hour and the measurements show I can save 4.5 millis in that time...

So, my 1st answer part above was a little misleading, but what I said about balancing the cost-benefit of a refactoring remains true.

Kai
In your test, how ofen is the exception thrown? Every time or very few times?
TofuBeer
Can you post the code for your tests? As well as the JVM and OS you are on? (I find it hard to belive that the time is zero or very small given that most JVM vendors won't bother optimizing the exception case).
TofuBeer
oh... and if this is in the production code that you are seeing no change in speed... 1) why are you throwing an exception every time a method gets called? 2) how often is it getting called (if it is not very often then the time won't be large)
TofuBeer
Unless you tested in a loop which ran for 5+ seconds (and execute a million or so iterations) you have not tested this correctly. Search SO for "stopwatch" for advice on writing a stopwatch benchmark.
Software Monkey
1000 times an hour definatly won't be noticeable. If you read the example in teh Effective Java book (the link is to the google books site, so it is the text) you will see a much more pathalogical version (that people have used in production code - eek).
TofuBeer
+1  A: 

The slow thing about exceptions is building the stack trace (in the constructor of java.lang.Throwable), which in turn depends on how deep your stack happens to be. The throwing in itself is not slow (it's even possible that the JVM uses exceptions for control flow at the machine code level).

If you use exceptions to signal exceptional situations (e.g. failures), then there is no need to be concerned about their performance. And the stack trace will give lots of good information about where the failure occurred.

If you need to use exceptions for control flow (not recommended, because it makes the code harder to understand), and the profiler shows that throwing the exceptions is your bottleneck (of course you always profile before thinking about optimizing, right? ;)), then you can create an exception class which does not build the stack trace:

Create a class which extends an exception class, and override the fillInStackTrace() method with an empty implementation. Then the exception will be very fast to construct and throw.

Esko Luontola
A: 

Let's say exception won't occur when trying to execute 1) and 2) statements. Are there ANY performance hits between those two sample-codes ?

If no, what if DoSomething() method has to do huuuge amount of work (loads of calls to other methods, etc.)?

1)

try { DoSomething(); } catch (...) { ... }

2)

DoSomething();

in Java try is free - throw costs (and try isn't really free... but let's forget I said that :-) So if the exception isn't thrown then there is no speed hit. Part of the logic is that throw should not be used for flow control so it is not optimized - it is for the abnormal case not the common one.
TofuBeer
+1  A: 

I haven't bothered to read up on Exceptions but doing a very quick test with some modified code of yours I come to the conclusion that the Exception circumstance quite a lot slower than the boolean case.

I got the following results:

Exception:20891ms
Boolean:62ms

From this code:

public class Test {
    public static void main(String args[]) {
            Test t = new Test();
            t.testException();
            t.testBoolean();
    }
    public void testException() {
            long start = System.currentTimeMillis();
            for(long i = 0; i <= 10000000L; ++i)
                    doSomethingException();
            System.out.println("Exception:" + (System.currentTimeMillis()-start) + "ms");
    }
    public void testBoolean() {
            long start = System.currentTimeMillis();
            for(long i = 0; i <= 10000000L; ++i)
                    doSomething();
            System.out.println("Boolean:" + (System.currentTimeMillis()-start) + "ms");
    }

    private void doSomethingException() {
        try {
          doSomethingElseException();
        } catch(DidNotWorkException e) {
           //Msg
        }
    }
    private void doSomethingElseException() throws DidNotWorkException {
       if(!isSoAndSo()) {
          throw new DidNotWorkException();
       }
    }
    private void doSomething() {
        if(!doSomethingElse())
            ;//Msg
    }
    private boolean doSomethingElse() {
       if(!isSoAndSo())
          return false;
       return true;
    }
    private boolean isSoAndSo() { return false; }
    public class DidNotWorkException extends Exception {}
}

I foolishly didn't read my code well enough and previously had a bug in it (how embarassing), if someone could triple check this code I'd very much appriciate it, just in case I'm going senile.

My specification is:

  • Compiled and run on 1.5.0_16
  • Sun JVM
  • WinXP SP3
  • Intel Centrino Duo T7200 (2.00Ghz, 977Mhz)
  • 2.00 GB Ram

In my opinion you should notice that the non-exception methods don't give the log error in doSomethingElse but instead return a boolean so that the calling code can deal with a failure. If there are multiple areas in which this can fail then logging an error inside or throwing an Exception might be needed.

PintSizedCat
Are you sure, you tested with private boolean isSoAndSo() { return **false**; }In that case I'd be surprised about your results.
Kai
Oh god, you know, I'm having one of those bloody days :( The results I gave were true though, if the exception isn't thrown there's a 2x difference.
PintSizedCat
Kai
No problem, glad I was helpful.
PintSizedCat
This comparison is unfair because a) the "if" in doSomethingElse() is optimized out by any decent compiler and b) even if it wasn't, it would not be noticable due to branch prediction functionality of modern CPUs. Usually, the condition is false most of the times and true in exceptional situation. The CPU's branch prediction expects the condition to evaluate to false and loads the instructions for the else branch, but when it finds that the condition is true (i.e. exceptional situation), it has to flush its pipeline and load the then branch statements, which can cost some dozen CPU cycles.
Stefan Majewsky