Использование «ловли исключений» для улучшения читабельности, хорошо или плохо?

9

В разделе « Когда использовать исключение в Pragmatic Programmer» книга пишет, что вместо:

retcode = OK;
     if (socket.read(name) != OK) {
      retcode = BAD_READ;
    }
     else {
      processName(name);
       if (socket.read(address) != OK) {
        retcode = BAD_READ;
      }
       else {
        processAddress(address);
         if (socket.read(telNo) != OK) {
          retcode = BAD_READ;
        }
         else {
          //  etc, etc...
        }
      }
    }
     return retcode;

, они предпочитают:

 retcode = OK;
     try {
      socket.read(name);
      process(name);
      socket.read(address);
      processAddress(address);
      socket.read(telNo);
      //  etc, etc...
    }
     catch (IOException e) {
      retcode = BAD_READ;
      Logger.log( "Error reading individual: " + e.getMessage());
    }
     return retcode;

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

Я могу понять, что мы должны отказаться от незначительной оптимизации для более аккуратного кода (по крайней мере, в 99% случаев), однако, насколько я знаю, перехват исключений относится к классу кода, который имеет заметную задержку во время выполнения. Поэтому мне было интересно, каково оправдание того, что второй кусок кода предпочтительнее первого кода?

Или, скорее, какой код вы бы предпочли?

Pacerier
источник
мигрировал из Code Review, потому что это вопрос передовой практики, а не вопрос проверки кода
Winston Ewert
1
IMO, если вы ожидаете, что сможете прочитать эти значения из сокета, то IOEx является исключительным.
Стивен Эверс
Относится к этому обсуждению: Использование Throwable для других вещей, кроме исключений
Даниэль Р Хикс
Вы измерили?
@ ThorbjørnRavnAndersen, конечно, я читал книгу, и у меня нет реального «тестового примера» для измерения… но если мы действительно измерим его в надуманном коде, тогда, очевидно, будет
снижение

Ответы:

18

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

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

Уинстон Эверт
источник
4
Согласен, я ненавижу видеть коды ошибок на языке, который имеет исключения. Большую часть времени вы должны ловить, регистрировать, перебрасывать, не нарушая стек вызовов. И большую часть времени не имеет значения, если «исключение» происходит медленнее. Приложение имеет проблему, найдите время, чтобы справиться с ней правильно. Пока исключение является исключительным и не является частью нормальной работы или «счастливого пути» приложения, оно обычно не является запахом кода.
CaffGeek
Кстати, мне было интересно, как мы можем оправдать исключение EOFException, которое DataStreams использует для обнаружения конца строки (конец строки не является исключительным, не так ли), как показано в docs.oracle.com/javase/tutorial/essential/io/datastreams.html ?
Pacerier
@Pacerier, это может быть исключительным. Отсутствие ввода в строке может указывать на неверные данные или что-то ужасно неправильное. Я подозреваю, что предполагаемое использование состоит в том, чтобы разобрать несколько полей вне строки
Уинстон Эверт
@ Downvoter, если у вас есть проблема с ответом, пожалуйста, объясните!
Уинстон Эверт
5

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

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

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

Андреас Йоханссон
источник
5

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

Откуда ты это знаешь? Как вы определяете «заметную задержку»?

Это как раз тот смутный (и совершенно неверный) миф о производительности, который приводит к бесполезной преждевременной оптимизации.

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

Майкл Боргвардт
источник
Я из .NET, так что извините за мои знания Java, но я лично испытывал перехват исключений в .NET, вызывающий безумные задержки (величина секунд), которые были устранены путем удаления «кода
перехвата
@Pacerier, серьезно? величина секунд? Я думаю, может быть, это было много исключений? Тогда я мог видеть это.
Уинстон Эверт
3
@Pacerier: если исключения вызывают безумные задержки, ясно, что их вызывает, не исключение, и поэтому их следует обрабатывать другими способами. Ни одна система не должна занимать секунды, чтобы обработать исключение, и я думаю, что Microsoft достаточно компетентна, чтобы этого избежать.
Дэвид Торнли
5

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

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

В то же время, имейте в виду, что вы здесь делаете чтение данных из сети. Скорость 10 мегабит в секунду (медленная для локальной сети, но довольно быстрая при подключении к Интернету) - это тот же порядок, что и время чтения одного байта информации.

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

В этом случае, когда выдается исключение, ваши данные записываются в журнал. Учитывая природу ведения журнала, вполне вероятно, что вы сбрасываете этот вывод сразу же после его записи (чтобы убедиться, что он не потерян). Запись на диск (опять же) довольно медленная операция - вам нужен довольно быстрый SSD корпоративного класса, чтобы достичь даже десятков тысяч IOP / сек. Диск, используемый для регистрации, может быть легко ограничен до сотен IOP / сек.

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

Джерри Гроб
источник