5

我有一点性能问题,在使用 try catch 子句时,最好指定您可以获得的确切异常,还是只使用异常更好?例子:

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

或者如果你不介意什么样的例外:

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

因为我知道您可以使用不同的异常来触发不同的效果,但我只是要求性能。

4

5 回答 5

4

根据我的测试,性能没有显着差异

每次运行尝试每个场景一千万次,然后比较运行时间(以纳秒为单位)以及四舍五入的秒数。这实际上与我最初的假设相反,因为我认为抓住 aThrowable会显示出明显的改善。

我也开始意识到这部分可能是由于优化器的影响,因此我创建了一个更复杂的示例,其中包括下面的伪随机数,认为这将减轻优化器对代码的任何潜在影响。

(我不会教你如何正确使用catch块,因为问题是关于性能的,而不是最佳实践。)

大量数据低于这一点!

运行 1 结果:

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

运行 2 结果:

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

运行 3 次结果:

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

运行 4 个结果:

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

运行 5 个结果:

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

代码

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)");

    }

}

更复杂的伪随机代码

我创建它是为了确保优化器不会通过意识到代码块将始终流向catch. 通过尝试Integer.parseInt()随机选择的String(但总是无效的),这意味着编译器直到运行时才能知道通过for()循环的给定运行是否有效。

正如第一个实验所预期的那样,三种情况之间没有显着差异。

运行 1 结果:

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

运行 2 结果:

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

运行 3 次结果:

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

运行 4 个结果:

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

运行 5 个结果:

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

代码

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)");

    }

}
于 2013-06-17T16:32:29.653 回答
2

我建议这里的正确答案是根据程序原因使用适当的异常处理,而不是性能。如果(忽略性能)捕获 a then 会更合适NullPointerException

例外应该是例外情况。它们应该很少发生,因此异常处理中的性能应该不如正确性重要。

如果您的应用程序定期预测这种情况,那么它应该通过异常以外的某种机制来处理它。如果您担心性能,尤其如此,因为抛出异常总是很昂贵。

于 2013-06-17T16:28:32.507 回答
1

在这种情况下,两者都不是。try/catch 比较昂贵,应该谨慎使用。手动检查 null 比捕获 NullPointerException 更好。

于 2013-06-17T16:51:50.910 回答
0

如果性能在任何方面与异常捕获相关,那么它不是异常。

它所做的只是一个测试,所以我怀疑它有什么真正的区别。

无论如何,捕获异常应该非常罕见。

如果你在做

try 
{
    whatever
} 
catch (MyException10 ex10) 
{
    whatever
}
catch (MyException9 ex9) 
{
    whatever
}
...
catch (MyException1 ex1) 
{
    whatever
}

然后你可以争辩潜在的性能改进,但这将是重新编写你的代码,所以一点不会引发无数异常......

于 2013-06-17T16:21:43.960 回答
0

我能想到的 try/catch 块的最佳性能建议是:让它们变小。

如果您有一段代码可以抛出三个异常,请执行以下操作:

try {
    piece1();
} catch (Exception1 e) {
    // whatever
}

try {
    piece2();
} catch (Exception2 e) {
    // whatever
}

try {
    piece3();
} catch (Exception3 e) {
    // whatever
}

这样,你可以//return早点。并以应有的方式对待每个异常。您很少“不介意”捕获到什么异常。breakcontinue

另请注意,过于宽泛的catch块可能会“隐藏”您不希望抛出的异常。特别是,RuntimeException当您捕获时,所有 s 都会被捕获Exception(包括NullPointerException,ArrayIndexOutOfBoundsException和其他细节)。这不好(tm)。

于 2013-06-17T16:27:01.360 回答