Что быстрее, попробуйте catch или if-else в java (производительность WRT) - PullRequest
34 голосов
/ 16 августа 2010

Какой из них быстрее:

Либо это

try {
  n.foo();
} 
catch(NullPointerException ex) {
}

или

if (n != null) n.foo();

Ответы [ 13 ]

53 голосов
/ 16 августа 2010
if (n != null) n.foo();

быстрее.

51 голосов
/ 16 августа 2010

Вопрос не в том, что быстрее, а в правильности.

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

Если это таквозможно, что n будет null как часть нормальной бизнес-логики, затем используйте if..else, иначе throw исключение.

33 голосов
/ 16 августа 2010

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

Для записи, большинство из oherheads в использовании исключений происходит в реализации объекта исключения. В частности, при звонке на fillInStackTrace(), который должен:

  • проверяет каждый кадр стека на наличие стека текущего потока, а
  • создать структуру данных для захвата деталей фрейма стека.

В некоторых случаях вы можете уменьшить это путем повторного использования объекта исключения или путем переопределения метода fillInStackTrace() исключения конкретного приложения, чтобы сделать его недоступным. Недостатком в обоих случаях является то, что правильные трассировки стека больше не будут доступны, чтобы помочь вам отладить неожиданные исключения. (И ни один из них не применим к примеру ФП.)

Хотя создание исключений стоит дорого, создание исключений, их распространение и отлов тоже не совсем дешевы.


Есть вторая причина, по которой явное нулевое тестирование является лучшей идеей. Учтите это:

try {
    doSomething(a.field);
} catch (NullPointerException ex) {
    System.err.println("a.field is null");
}

Что произойдет, если NPE произойдет во время вызова doSomething(...), а не во время вычисления выражения a.field? Конечно, мы поймаем NPE, но мы неправильно его диагностируем, а затем попытаемся продолжить ... неправильно, предполагая, что a.field не установлено или что-то в этом роде.

Отличить «ожидаемый» NPE от «неожиданного» NPE теоретически возможно, но на практике очень сложно. Гораздо более простой и надежный подход заключается в явном тестировании ожидаемых значений null (например, с помощью оператора if) и обработке всех NPE как ошибок.

(Я уверен, что именно это @Mitch подразумевает под «обработкой исключений как исключительных», но я думаю, что это помогает разобраться с наглядным примером ...)

24 голосов
/ 16 августа 2010

Ответ на этот вопрос не так прост, как кажется, потому что это будет зависеть от того, сколько раз объект действительно равен нулю. Когда это очень редко (скажем, в 0,1% случаев), это может быть даже быстрее. Чтобы проверить это, я провел несколько тестов со следующими результатами (с клиентом Java 1.6):

Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.45 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.46 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.42 seconds
Average time of NullExceptionTest: 0.52 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.41 seconds
Average time of NullExceptionTest: 1.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.07 seconds
Average time of NullExceptionTest: 7.48 seconds

Это кажется мне довольно убедительным. NPE просто очень медленные. (Я могу опубликовать код тестирования, если хотите)

редактировать: Я только что сделал интересное открытие: при сравнительном тестировании с использованием серверной JVM результаты резко меняются:

Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.33 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.32 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.31 seconds
Average time of NullExceptionTest: 0.32 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.28 seconds
Average time of NullExceptionTest: 0.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.05 seconds
Average time of NullExceptionTest: 0.04 seconds

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

8 голосов
/ 16 августа 2010

Если n.foo() случится, чтобы внутренне выдать NPE, вы отключаетесь от долгой сессии отладки (или, что еще хуже, ваше приложение не работает в работе ..). Просто не делай этого.

Сколько нано секунд вы планируете сэкономить?

7 голосов
/ 16 августа 2010

Я заметил, что я не единственный, кто читает Информационный бюллетень Java-специалиста:)

Помимо того факта, что семантическое различие (NPE не обязательно вызывается разыменованием n, оно могло быть вызвано некоторой ошибкой в ​​foo()) и проблема читабельности (попытка / уловка более запутанна для читателя, чем if), они должны быть примерно такими же быстрыми в случае, когда n != null (с небольшой версией if / else), но когда n == null, если / еще намного быстрее. Почему?

  1. Когда n == null, ВМ должна создать новый объект исключения и заполнить свой след стека . Получение информации о трассировке стека действительно дорого, поэтому здесь версия try / catch гораздо дороже.
  2. Некоторые считают, что условные операторы медленнее, потому что они предотвращают конвейеризацию инструкций, и, избегая явного if, они думают, что им не по карману, когда n != null. Однако дело в том, что виртуальная машина будет выполнять неявную проверку NULL при разыменовании ... то есть, если JIT не может определить, что n должно быть ненулевым, что может быть в случае, если / еще версия. Это означает, что версии if / else и try / catch должны выполняться примерно одинаково. Но ...
  3. ... предложения try / catch могут влиять на то, как JIT может вызывать встроенные вызовы методов, что означает, что он не сможет оптимизировать версию try / catch, а также if / else.
3 голосов
/ 16 августа 2010

Кроме хороших ответов (используйте исключения для исключительных случаев), я вижу, что вы в основном пытаетесь избежать нулевых проверок везде.В Java 7 будет оператор «null safe», который будет возвращать null при вызове n?.foo() вместо броска NPE.Это заимствовано из Groovy языка.Также существует тенденция избегать использования null вообще в своем коде, за исключением случаев, когда это действительно необходимо (например, работа с библиотеками).Смотрите этот другой ответ для дальнейшего обсуждения этого. Избегание! = Пустые выражения

2 голосов
/ 16 августа 2010

Как правило, обработка исключений обходится дорого. VM Spec может дать вам некоторое представление о том, сколько, но в приведенном выше случае if (n != null) n.foo(); быстрее.

Хотя я согласен с Митчем Уитатом в том, что реальный вопрос - это правильность.

@ Митч Уит - В его защиту это довольно надуманный пример.:)

1 голос
/ 16 августа 2010

Конструкция если работает быстрее. Условие может быть легко переведено в машинный код (инструкции процессора).

Альтернатива ( try-catch ) требует создания объекта NullPointerException.

0 голосов
/ 20 августа 2010

if-else быстрее, потому что блок try-catch вызывает трассировку стека исключений.Вы можете принять это, поскольку блок If-Else выполняет одну инструкцию для оценки, но Try-Catch выполнит тысячи инструкций, чтобы вызвать исключение, когда это произойдет.

...