Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specify an exception or not?

i have a little performance question, when working with a try catch clause, it's better to specify the exact exception you can get or just using exception it's better? Example:

try {
    whatever
} catch (NullPointerException ex) {
    whatever
}

or if you don't mind what kind of exception:

try {
    whatever
} catch (Exception ex) {
    whatever
}

Because i know that you can use different exceptions to trigger different effects, but i'm just asking for performance.

like image 962
Goty Metal Avatar asked Jun 17 '13 16:06

Goty Metal


People also ask

How do you specify an exception?

How to Specify an Exception. If you don't handle an exception within a method, it will be propagated within the call stack. And if it's a checked exception, you also need to specify that the method might throw the exception. You can do that by adding a throws clause to the method declaration.

What are the 3 types of exceptions?

There are three types of exception—the checked exception, the error and the runtime exception.

What is a exception specification?

Exception specifications are a C++ language feature that indicate the programmer's intent about the exception types that can be propagated by a function. You can specify that a function may or may not exit by an exception by using an exception specification.


2 Answers

According to my tests, there is no significant difference in performance.

Each run attempts ten million of each scenario and then compares the run times in nanoseconds as well as rounded seconds. This is actually contrary to my original hypothesis, as I thought that catching a Throwable would show a marked improvement.

I also began to realize that part of this may be due to the optimizer's influence, and so I have created a more convoluted example which includes pseudo-random numbers below, thinking that this will mitigate any potential influence the optimizer is having on the code.

(I won't lecture you about proper use of catch blocks, as the question is specifically about performance, not best practices.)

Lots of Data Below This Point!

Run 1 Results:

Exception: 7196141955 (7.196s)
NumberFormatException: 7736401837 (7.736s)
Throwable: 6818656505 (6.819s)

Run 2 Results:

Exception: 7262897545 (7.263s)
NumberFormatException: 7056116050 (7.056s)
Throwable: 7108232206 (7.108s)

Run 3 Results:

Exception: 7088967045 (7.089s)
NumberFormatException: 7020495455 (7.020s)
Throwable: 7192925684 (7.193s)

Run 4 Results:

Exception: 6916917328 (6.917s)
NumberFormatException: 7690084994 (7.690s)
Throwable: 6906011513 (6.906s)

Run 5 Results:

Exception: 7247571874 (7.248s)
NumberFormatException: 6818511040 (6.819s)
Throwable: 6813286603 (6.813s)

Code

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Test {

    private static final int TRIALS = 10000000;
    private static final int NANOS_IN_SECOND = 1000000000;
    private static final int DECIMAL_PRECISION = 3;
    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;

    public static void main(String[] args) {

        long firstStart = System.nanoTime();

        for(int i = 0; i < TRIALS; i++) {
            try {
                throw new NumberFormatException();
            }
            catch(Exception e) {

            }
        }

        long firstEnd = System.nanoTime();

        long secondStart = System.nanoTime();

        for(int i = 0; i < TRIALS; i++) {
            try {
                throw new NumberFormatException();
            }
            catch(NumberFormatException e) {

            }
        }

        long secondEnd = System.nanoTime();

        long thirdStart = System.nanoTime();

        for(int i = 0; i < TRIALS; i++) {
            try {
                throw new NumberFormatException();
            }
            catch(Throwable e) {

            }
        }

        long thirdEnd = System.nanoTime();

        long exception = firstEnd - firstStart;
        long numberFormatException = secondEnd - secondStart;
        long throwable = thirdEnd - thirdStart;

        BigDecimal exceptionSeconds = new BigDecimal((double)exception / (double)NANOS_IN_SECOND);
        BigDecimal numberFormatExceptionSeconds = new BigDecimal((double)numberFormatException / (double)NANOS_IN_SECOND);
        BigDecimal throwableSeconds = new BigDecimal((double)throwable / (double)NANOS_IN_SECOND);

        exceptionSeconds = exceptionSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
        numberFormatExceptionSeconds = numberFormatExceptionSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
        throwableSeconds = throwableSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);

        System.out.println("Exception: " + exception + " (" + exceptionSeconds + "s)");
        System.out.println("NumberFormatException: " + numberFormatException + " (" + numberFormatExceptionSeconds + "s)");
        System.out.println("Throwable: " + throwable + " (" + throwableSeconds + "s)");

    }

}

More Convoluted, Pseudo-Random Code

I created this in order to make sure the optimizer wasn't simply "ignoring" the entire throw/catch process by realizing that the code block will always flow through to the catch. By attempting an Integer.parseInt() on a randomly-selected String (but always an invalid one), this means the compiler can't know until runtime whether a given run through the for() loops is valid or not.

As expected from the first experiment, there is no significant difference between the three scenarios.

Run 1 Results:

Exception: 10988431371 (10.988s)
NumberFormatException: 11360698958 (11.361s)
Throwable: 10539041505 (10.539s)

Run 2 Results:

Exception: 12468860076 (12.469s)
NumberFormatException: 11852429194 (11.852s)
Throwable: 11859547560 (11.860s)

Run 3 Results:

Exception: 10618082779 (10.618s)
NumberFormatException: 10718252324 (10.718s)
Throwable: 10327709072 (10.328s)

Run 4 Results:

Exception: 11031135405 (11.031s)
NumberFormatException: 10689877480 (10.690s)
Throwable: 10668345685 (10.668s)

Run 5 Results:

Exception: 11513727192 (11.514s)
NumberFormatException: 11581826079 (11.582s)
Throwable: 12488301109 (12.488s)

Code

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;

public class Test {

    private static final int TRIALS = 10000000;
    private static final int NANOS_IN_SECOND = 1000000000;
    private static final int DECIMAL_PRECISION = 3;
    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;

    private static final String[] TEST_STRINGS = {
        "lawl",
        "rofl",
        "trololo",
        "foo",
        "bar"
    };

    private static final Random RANDOM = new Random();



    public static void main(String[] args) {

        long firstStart = System.nanoTime();

        for(int i = 0; i < TRIALS; i++) {
            try {
                Integer.parseInt(TEST_STRINGS[RANDOM.nextInt(TEST_STRINGS.length)]);
            }
            catch(Exception e) {

            }
        }

        long firstEnd = System.nanoTime();

        long secondStart = System.nanoTime();

        for(int i = 0; i < TRIALS; i++) {
            try {
                Integer.parseInt(TEST_STRINGS[RANDOM.nextInt(TEST_STRINGS.length)]);
            }
            catch(NumberFormatException e) {

            }
        }

        long secondEnd = System.nanoTime();

        long thirdStart = System.nanoTime();

        for(int i = 0; i < TRIALS; i++) {
            try {
                Integer.parseInt(TEST_STRINGS[RANDOM.nextInt(TEST_STRINGS.length)]);
            }
            catch(Throwable e) {

            }
        }

        long thirdEnd = System.nanoTime();

        long exception = firstEnd - firstStart;
        long numberFormatException = secondEnd - secondStart;
        long throwable = thirdEnd - thirdStart;

        BigDecimal exceptionSeconds = new BigDecimal((double)exception / (double)NANOS_IN_SECOND);
        BigDecimal numberFormatExceptionSeconds = new BigDecimal((double)numberFormatException / (double)NANOS_IN_SECOND);
        BigDecimal throwableSeconds = new BigDecimal((double)throwable / (double)NANOS_IN_SECOND);

        exceptionSeconds = exceptionSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
        numberFormatExceptionSeconds = numberFormatExceptionSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);
        throwableSeconds = throwableSeconds.setScale(DECIMAL_PRECISION, ROUNDING_MODE);

        System.out.println("Exception: " + exception + " (" + exceptionSeconds + "s)");
        System.out.println("NumberFormatException: " + numberFormatException + " (" + numberFormatExceptionSeconds + "s)");
        System.out.println("Throwable: " + throwable + " (" + throwableSeconds + "s)");

    }

}
like image 192
asteri Avatar answered Oct 12 '22 16:10

asteri


I would suggest that the correct answer here is to use the appropriate exception handling per programmatic reasons as opposed to performance. If (ignoring performance) it would be more appropriate to catch a NullPointerException then do that.

Exceptions are supposed to be the exceptional case. They should happen rarely therefor performance in exception processing should be less important than correctness.

If your application is anticipating this situation regularly then it should handle it via some mechanism other than an Exception. This is especially true if you are worried about performance because throwing exceptions is ALWAYS expensive.

like image 21
John B Avatar answered Oct 12 '22 18:10

John B