Я не хочу дискуссии о том, когда и не бросать исключения. Я хочу решить простой вопрос. В 99% случаев аргумент об отказе от исключений вращается вокруг того, что они медленные, в то время как другая сторона заявляет (с помощью теста производительности), что скорость не является проблемой. Я прочитал множество блогов, статей и постов, касающихся одной или другой стороны. Так что это?
c#
.net
performance
exception
Горан
источник
источник
Ответы:
Я нахожусь на "не медленной" стороне - или точнее "не достаточно медленной, чтобы стоило избегать их при нормальном использовании". Я написал две короткие статьи об этом. Существуют критические замечания в отношении аспекта тестирования, которые в основном сводятся к тому, что «в реальной жизни было бы больше стека, через которое нужно было бы пропускать кэш, и т. Д.», - но использование кодов ошибок для продвижения по стеку также взорвать кеш, поэтому я не считаю это особенно хорошим аргументом.
Просто чтобы прояснить - я не поддерживаю использование исключений, когда они не логичны. Например,
int.TryParse
полностью подходит для преобразования данных от пользователя. Это уместно при чтении сгенерированного машиной файла, где сбой означает «Файл не в том формате, в котором он должен быть, я действительно не хочу пытаться справиться с этим, поскольку я не знаю, что еще может быть не так. "При использовании исключений в «только разумных обстоятельствах» я никогда не видел приложение, производительность которого значительно ухудшалась из-за исключений. По сути, исключения не должны происходить часто, если у вас нет существенных проблем с корректностью, и если у вас есть существенные проблемы с корректностью, то производительность не самая большая проблема, с которой вы сталкиваетесь.
источник
На это есть однозначный ответ от парня, который их реализовал, - Крис Брамм. Он написал отличную статью в блоге на эту тему (предупреждение - это очень долго) (warning2 - это очень хорошо написано, если вы технарь, вы прочтете его до конца, а затем должны наверстать упущенное после работы :) )
Резюме: они медленные. Они реализованы как исключения Win32 SEH, поэтому некоторые даже пересекают границу ЦП кольца 0! Очевидно, что в реальном мире вы будете выполнять много другой работы, поэтому странное исключение вообще не будет замечено, но если вы используете их для выполнения программы, ожидайте, что ваше приложение будет забито. Это еще один пример того, как маркетинговая машина MS оказывает нам медвежью услугу. Я вспоминаю, как одна из микрософтов рассказала нам, как они понесли абсолютно нулевые накладные расходы, что является полной ошибкой.
Крис приводит соответствующую цитату:
источник
Я понятия не имею, о чем говорят люди, когда говорят, что они медленные, только если их бросают.
РЕДАКТИРОВАТЬ: Если исключения не выбрасываются, то это означает, что вы делаете новое исключение () или что-то в этом роде. В противном случае исключение приведет к приостановке потока и обходу стека. Это может быть хорошо в небольших ситуациях, но на сайтах с большим трафиком использование исключений в качестве рабочего процесса или механизма пути выполнения, безусловно, вызовет проблемы с производительностью. Исключения сами по себе неплохие и полезны для выражения исключительных условий.
Рабочий процесс исключений в приложении .NET использует исключения первого и второго шансов. Для всех исключений, даже если вы перехватываете их и обрабатываете, объект исключения все еще создается, и фреймворк все еще должен пройтись по стеку, чтобы найти обработчик. Если вы ловите и перебрасываете, конечно, это займет больше времени - вы получите исключение первого шанса, перехватите его, сбросьте его, вызвав еще одно исключение первого шанса, которое затем не найдет обработчик, который затем вызывает исключение второго шанса.
Исключения также являются объектами в куче, поэтому, если вы создаете тонны исключений, это вызывает проблемы как с производительностью, так и с памятью.
Кроме того, согласно моей копии «Тестирование производительности веб-приложений Microsoft .NET», написанной командой ACE:
«Обработка исключений стоит дорого. Выполнение вовлеченного потока приостанавливается, в то время как CLR рекурсивно проходит через стек вызовов в поисках правильного обработчика исключений, и, когда он найден, у обработчика исключений и некоторого числа блоков finally должны быть все шансы на выполнение. перед обычной обработкой. "
Мой собственный опыт в этой области показал, что сокращение исключений значительно улучшило производительность. Конечно, есть и другие вещи, которые вы должны учитывать при тестировании производительности - например, если ваш дисковый ввод-вывод срабатывает или ваши запросы выполняются в считанные секунды, то это должно быть вашим фокусом. Но поиск и удаление исключений должны быть жизненно важной частью этой стратегии.
источник
Аргумент, насколько я понимаю, не в том, что бросать исключения - это плохо, они медленны сами по себе. Вместо этого речь идет об использовании конструкции throw / catch в качестве первоклассного способа управления нормальной логикой приложения вместо более традиционных условных конструкций.
Часто в обычной логике приложения вы выполняете цикл, где одно и то же действие повторяется тысячи / миллионы раз. В этом случае, с помощью некоторого очень простого профилирования (см. Класс Stopwatch), вы можете сами убедиться, что генерирование исключения вместо простого оператора if может оказаться существенно медленнее.
На самом деле я однажды прочитал, что команда .NET в Microsoft представила методы TryXXXXX в .NET 2.0 для многих базовых типов FCL именно потому, что клиенты жаловались, что производительность их приложений была настолько низкой.
Оказывается, во многих случаях это было связано с тем, что клиенты пытались преобразовать значения типа в цикле, и каждая попытка не удалась. Исключение преобразования было сгенерировано, а затем перехвачено обработчиком исключения, который затем проглотил исключение и продолжил цикл.
Microsoft теперь рекомендует использовать методы TryXXX, особенно в этой ситуации, чтобы избежать возможных проблем с производительностью.
Я могу ошибаться, но, похоже, вы не уверены в правдивости тех «эталонов», о которых вы читали. Простое решение: попробуйте сами.
источник
Мой XMPP-сервер значительно увеличил скорость (извините, никаких фактических чисел, просто наблюдений) после того, как я последовательно пытался предотвратить их возникновение (например, проверял, подключен ли сокет, прежде чем пытаться читать больше данных) и дал себе способы их избежать (упомянутые методы TryX). Это было всего около 50 активных (в чате) виртуальных пользователей.
источник
Просто чтобы добавить свой собственный недавний опыт к этому обсуждению: в соответствии с большей частью того, что написано выше, я обнаружил, что генерирование исключений является чрезвычайно медленным при повторном выполнении, даже без отладчика. Я только увеличил производительность большой программы, которую я пишу, на 60%, изменив примерно пять строк кода: переключение на модель кода возврата вместо генерирования исключений. Конечно, этот код выполнялся тысячи раз и потенциально вызывал тысячи исключений, прежде чем я его изменил. Поэтому я согласен с приведенным выше утверждением: генерировать исключения, когда что-то важное действительно идет не так, а не как способ управления потоком приложений в любых «ожидаемых» ситуациях.
источник
Если вы сравните их с кодами возврата, они будут чертовски медленными. Однако, как указывалось в предыдущих постерах, вы не хотите включать обычную работу программы, поэтому вы получаете только перфоманс, когда возникает проблема, и в подавляющем большинстве случаев производительность больше не имеет значения (поскольку исключение в любом случае подразумевает препятствие).
Их определенно стоит использовать по сравнению с кодами ошибок, их преимущества огромны.
источник
У меня никогда не было проблем с производительностью за исключением. Я часто использую исключения - я никогда не использую коды возврата, если могу. Это плохая практика и, на мой взгляд, пахнет спагетти-кодом.
Я думаю, что все сводится к тому, как вы используете исключения: если вы используете их как коды возврата (каждый вызов метода в стеке перехватывает и перебрасывает), то да, они будут медленными, потому что у вас есть издержки на каждый отдельный захват / выброс.
Но если вы выбрасываете внизу стека и ловите сверху (вы заменяете одну цепочку кодов возврата одним броском / перехватом), все дорогостоящие операции выполняются один раз.
В конце дня они являются действующей языковой функцией.
Просто чтобы доказать мою точку зрения
Пожалуйста, запустите код по этой ссылке (слишком большой для ответа).
Результаты на моем компьютере:
marco@sklivvz:~/develop/test$ mono Exceptions.exe | grep PM
10/2/2008 2:53:32 PM
10/2/2008 2:53:42 PM
10/2/2008 2:53:52 PM
Метки времени выводятся в начале, между кодами возврата и исключениями, в конце. Это занимает одинаковое время в обоих случаях. Обратите внимание, что вы должны компилировать с оптимизацией.
источник
Но моно генерирует исключение в 10 раз быстрее, чем автономный режим .net, а автономный режим .net генерирует исключение в 60 раз быстрее, чем режим отладчика .net. (Тестирующие машины имеют одинаковую модель процессора)
источник
В режиме выпуска накладные расходы минимальны.
Если вы не собираетесь использовать исключения для управления потоком (например, нелокальные выходы) рекурсивным способом, я сомневаюсь, что вы сможете заметить разницу.
источник
В Windows CLR для цепочки вызовов глубины 8 создание исключения в 750 раз медленнее, чем проверка и распространение возвращаемого значения. (см. ниже для ориентиров)
Такая высокая стоимость исключений объясняется тем, что Windows CLR интегрируется с так называемой структурной обработкой исключений Windows . Это позволяет должным образом перехватывать и генерировать исключения в разных средах выполнения и на разных языках. Тем не менее, это очень очень медленно.
Исключения во время выполнения Mono (на любой платформе) намного быстрее, потому что он не интегрируется с SEH. Однако при передаче исключений через несколько сред выполнения происходит потеря функциональности, поскольку он не использует ничего похожего на SEH.
Вот сокращенные результаты моего теста исключений и возвращаемых значений для Windows CLR.
А вот и код ..
источник
Небольшое замечание о производительности, связанной с отловом исключений.
Когда путь выполнения входит в блок try, ничего волшебного не происходит. Здесь нет инструкции try и никаких затрат, связанных с входом или выходом из блока try. Информация о блоке try хранится в метаданных метода, и эти метаданные используются во время выполнения всякий раз, когда возникает исключение. Механизм выполнения перемещается по стеку в поисках первого вызова, который содержался в блоке try. Любые накладные расходы, связанные с обработкой исключений, возникают только при возникновении исключений.
источник
При написании классов / функций для использования другими, кажется, трудно сказать, когда уместны исключения. Есть несколько полезных частей BCL, которые мне пришлось бросить и пойти на пинвоук, потому что они выдают исключения, а не возвращают ошибки. В некоторых случаях вы можете обойти это, но для других, таких как System.Management и Performance Counters, есть случаи, когда вам нужно делать циклы, в которых BCL часто генерирует исключения.
Если вы пишете библиотеку, и существует отдаленная вероятность того, что ваша функция может использоваться в цикле, и существует вероятность большого количества итераций, используйте шаблон Try .. или какой-либо другой способ показать ошибки, кроме исключений. И даже тогда трудно сказать, сколько будет вызываться ваша функция, если она используется многими процессами в общей среде.
В моем собственном коде исключения генерируются только тогда, когда вещи настолько исключительны, что необходимо пойти посмотреть на трассировку стека и посмотреть, что пошло не так, а затем исправить это. Поэтому я в значительной степени переписал части BCL, чтобы использовать обработку ошибок на основе шаблона Try .. вместо исключений.
источник