20

This question already has an answer here:

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?


10 답변


6

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.


  • Are you sure, you tested with private boolean isSoAndSo() { return false; } In that case I'd be surprised about your results. - Kai Huppmann
  • 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. - Henry B
  • No Problem & thanks for all your work on this question! - Kai Huppmann
  • No problem, glad I was helpful. - Henry B
  • 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

11

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.


  • Logical fallacy - They could just be cheap! - alex
  • Nope... they are not :-) (but I had to find the quote...) - TofuBeer
  • Exceptions are expensive, indeed. I've created a benchmark where exceptions happen only in 1% of the calls, which is a more or less realistic case. Even in these circumstances, performance penalties are very high, not 70 times, but still very high. See source code and benchmark results here. - Richard Gomes

11

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.


  • Maybe you can create new exceptions without storing it if your exception constructor calls super(msg, cause, /*enableSuppression*/false, /*writableStackTrace*/false); - giampaolo

8

The slow part about exceptions is building the stack trace (in the constructor of java.lang.Throwable), which depends on stack depth. Throwing in itself is not slow.

Use exceptions to signal failures. The performance impact then is negligible and the stack trace helps to pin-point the failure's cause.

If you need exceptions for control flow (not recommended), and profiling shows that exceptions are the bottleneck, then create an Exception subclass that overrides fillInStackTrace() with an empty implementation. Alternatively (or additionally) instantiate only one exception, store it in a field and always throw the same instance.

The following demonstrates exceptions without stack traces by adding one simple method to the micro benchmark (albeit flawed) in the accepted answer:

public class DidNotWorkException extends Exception {
  public Throwable fillInStackTrace() {
      return this;
  }
}

Running it using the JVM in -server mode (version 1.6.0_24 on Windows 7) results in:

Exception:99ms
Boolean:12ms

Exception:92ms
Boolean:11ms

The difference is small enough to be ignorable in practice.


  • @Dave Jarvis - if those are two separate runs and representative results, I would NOT call 900% execution time "hardly any difference"... If we're all coding Hello Worlds, then yeah go crazy with exceptions... - im so confused
  • That rather looks like a factor of 10 instead of hardly a difference. If you use this in many places, this will hurt a lot. - Arne Babenhauserheide

4

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 :)


  • +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

3

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.


  • 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

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.


3

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.


  • 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. - Lawrence Dol
  • 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

0

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.


0

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

If no, what if the DoSomething() method has to do a 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

Linked


Related

Latest