Что быстрее, попробуйте catch или if-else в java (производительность WRT)

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

Либо это

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

или же

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

13 ответов

Решение

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

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

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

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

быстрее.

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

Для записи, большинство 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 подразумевает под "обработкой исключений как исключительных", но я думаю, что это помогает разобраться с наглядным примером...)

Ответ на этот вопрос не так прост, как кажется, потому что это будет зависеть от того, сколько раз объект действительно равен нулю. Когда это очень редко (скажем, в 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, если это действительно не исключение.

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

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

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

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

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

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

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

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

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

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

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

Этот вопрос недавно обсуждался доктором Хайнцем:

http://javaspecialists.eu/webinars/recordings/if-else-npe-teaser.mov

Во-первых, если... тогда... еще лучше, по многим причинам, указанным другими авторами.

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

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

Определенно вторая форма намного быстрее. в try-catch сценарий, он бросает exception который делает new Exception() какой-то формы. Тогда catch вызывается блок, который является вызовом метода и должен выполнять любой код в нем. Вы поняли.

Другие вопросы по тегам