2013-06-17 2 views
5

У меня есть небольшой вопрос о производительности, когда вы работаете с предложением try catch, лучше указать точное исключение, которое вы можете получить или просто используя исключение, лучше? Пример:Укажите исключение или нет?

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

или, если вы не возражаете, что это за исключением:

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

Потому что я знаю, что вы можете использовать различные исключения, чтобы вызвать различные эффекты, но я просто прошу представление.

+0

Мое предположение заключается в том, что производительность одинакова, за исключением случаев, когда вы ловите «Throwable», поскольку что-то более конкретное, чем это потребует от программы проверки типа «Throwable». Однако это всего лишь предположение. – asteri

+0

Почему бы вам не попробовать сравнить себя и опубликовать результаты здесь. – anubhava

+5

@anubhava Потому что каждый раз, когда я пытаюсь проверить что-то, мне говорят, что я не тестирую должным образом. Ха-ха :) – asteri

ответ

0

Если производительность каким-либо образом имеет значение с точки зрения исключения прилова, это не исключение.

Все, что он делает, это тест, поэтому я сомневаюсь, что он имеет какое-либо значение.

Ловля Исключение должно быть очень редким в любом случае.

Если вы делали

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

Тогда можно утверждать, потенциальное улучшение производительности, но это было бы переработать код так, один бит не бросайте многочисленное исключение ...

+1

Я думаю, что OP больше интересуется процессом catch-catch в общем случае, и если Java будет рассматривать catching 'Exception' иначе, чем ловлю' MoreSpecificException'. – arshajii

+0

Иногда можно зацикнуться, используя API, где невозможно избежать большого количества исключений. Например, может быть интерфейс словаря, который включает метод «получить элемент», который генерирует, если элемент не существует, но не содержит других средств проверки того, существует ли элемент. Тот факт, что такой интерфейс может быть плохо разработан, не обязательно позволяет его изменить. В таких случаях эффективность обработки исключений может быть очень важной. – supercat

+0

Это должно быть один черт дерева наследования, и вам нужно будет много исключать исключения.В этом случае они не являются исключительными. Если вы получите фаршированный API, как упомянуто supercat, может быть, но я бы посмотрел на то, что не путается, пытаясь обработать их более эффективно. Стоимость теста является тривиальной по сравнению с самим фактическим исключением. –

0

Лучших показателей совет для блока try/catch, который я могу придумать, заключается в том, чтобы сделать их маленькими.

Если у вас есть кусок кода, который может бросить, скажем, три исключения, сделайте следующее:

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

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

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

Таким образом, вы можете return/break/continue рано. И рассматривайте каждое исключение как способ лечения. Редко, что вы «не против», какое исключение поймали.

Обратите также внимание, что чрезмерно широкий блок catch может «скрыть» исключения, которые вы не ожидаете, могут быть выброшены. В частности, все RuntimeException с пойманы, когда вы ловите Exception (и это включает в себя NullPointerException, ArrayIndexOutOfBoundsException и другие тонкости). И это не хорошо (тм).

2

Я бы предположил, что правильным ответом здесь является использование соответствующей обработки исключений по программным причинам, а не производительности. Если (игнорируя производительность) было бы уместнее поймать NullPointerException, тогда сделайте это.

Исключения должны быть исключительным случаем. Они должны происходить редко, потому что производительность в обработке исключений должна быть менее важной, чем правильность.

Если ваше приложение регулярно рассматривает эту ситуацию, оно должно обрабатывать его с помощью какого-либо механизма, кроме исключения. Это особенно верно, если вы беспокоитесь о производительности, потому что бросание исключений ВСЕГДА дорого.

4

Согласно моим испытаниям, существует нет существенной разницы в производительности.

Каждый пробег пробует десять миллионов каждого сценария, а затем сравнивает время выполнения в наносекундах, а также округлые секунды. Это на самом деле противоречит моей первоначальной гипотезе, поскольку я думал, что поймать Throwable продемонстрирует заметное улучшение.

Я также начал понимать, что часть этого может быть связана с влиянием оптимизатора, и поэтому я создал более запутанный пример, который включает в себя псевдослучайные числа ниже, полагая, что это уменьшит любое потенциальное влияние, которое оптимизатор имеет по коду.

(я не буду читать лекции вам о правильном использовании catch блоков, поскольку речь идет именно о производительности, не лучшие практики.)

Много данных ниже этой точки!

Выполнить 1 Результаты:

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

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

Run 4 Результаты:

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

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

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

Run 4 Результаты:

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

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

    } 

} 
+0

+1 за усилия, чтобы написать этот тест и доверять мне, кажется, что это правильный бенчмаркинг: P – anubhava

+0

Спасибо за все это тестирование, что очень помогает мне :) –

1

В этом случае ни. try/catch сравнительно дорогая и должна использоваться экономно. Вам лучше вручную проверять значение null, чем перехватывать исключение NullPointerException.

Смежные вопросы