Вопреки общепринятым теориям, try
/ catch
может иметь существенное влияние на производительность, и вот, выбрасывается ли исключение!
- Отключает некоторые автоматические оптимизации (по проекту) , а в некоторых случаях вводит отладочный код, как вы можете ожидать от средства отладки . Всегда найдутся люди, которые не согласятся со мной по этому вопросу, но язык требует этого, и разборка показывает это, так что эти люди по определению словаря бредовые .
- Это может отрицательно повлиять на обслуживание. Это на самом деле самая важная проблема здесь, но, поскольку мой последний ответ (который был почти полностью посвящен этому) был удален, я постараюсь сосредоточиться на менее существенная проблема (микрооптимизация) в отличие от более существенной проблемы (макрооптимизация).
Первый был рассмотрен в нескольких сообщениях в блогах Microsoft MVP на протяжении многих лет, и я надеюсь, что вы можете легко их найти, но StackOverflow заботится о столько о контенте , поэтому предоставлю ссылки на некоторые из них как наполнитель свидетельство:
Также есть этот ответ , который показывает разницу между дизассемблированным кодом с использованием и без try
/ catch
.
Кажется настолько очевидным, что - это накладные расходы, которые явно наблюдаются при генерации кода, и эти накладные расходы даже, кажется, признаются людьми, которые ценят Microsoft! И все же я повторяю интернет ...
Да, существуют десятки дополнительных инструкций MSIL для одной тривиальной строки кода, и это даже не распространяется на отключенную оптимизацию, так что технически это микрооптимизация.
Я опубликовал ответ несколько лет назад, который был удален, поскольку он был сосредоточен на производительности программистов (макро-оптимизация).
Это прискорбно, поскольку отсутствие экономии времени на процессор в течение нескольких наносекунд, скорее всего, компенсирует многие часы ручной оптимизации людьми. За что ваш начальник платит больше: час вашего времени или час при работающем компьютере? В какой момент мы выдернем вилку и признаем, что пришло время просто купить более быстрый компьютер ?
Очевидно, мы должны оптимизировать наши приоритеты , а не только наш код! В своем последнем ответе я обратил внимание на различия между двумя фрагментами кода.
Использование try
/ catch
:
int x;
try {
x = int.Parse("1234");
}
catch {
return;
}
// some more code here...
Не используется try
/ catch
:
int x;
if (int.TryParse("1234", out x) == false) {
return;
}
// some more code here
Рассмотрим с точки зрения разработчика технического обслуживания, который с большей вероятностью будет тратить ваше время, если бы не профилирование / оптимизация (описанная выше), что, вероятно, даже не потребовалось бы, если бы не было try
/ catch
проблема, затем при прокрутке исходного кода ... У одной из них есть четыре лишние строки стандартного мусора!
По мере того, как в класс вводится все больше и больше полей, весь этот шаблонный мусор накапливается (как в исходном, так и в разобранном коде) намного выше разумных уровней. Четыре дополнительные строки на поле, и они всегда одни и те же строки ... Разве нас не учили избегать повторения? Я полагаю, что мы могли бы скрыть try
/ catch
за какой-то самодельной абстракцией, но ... тогда мы могли бы также просто избегать исключений (то есть использовать Int.TryParse
).
Это даже не сложный пример; Я видел попытки создания новых классов в try
/ catch
. Учтите, что весь код внутри конструктора может быть дисквалифицирован из-за определенных оптимизаций, которые в противном случае были бы автоматически применены компилятором. Что может быть лучше для теории, что медленный * компилятор , в отличие от компилятор делает именно то, что ему говорят ?
Предполагая, что указанным конструктором выдается исключение, и в результате возникает некоторая ошибка, разработчик с плохим техническим обслуживанием затем должен отследить его. Это может быть не такой простой задачей, так как в отличие от спагетти-кода goto кошмара, try
/ catch
может вызвать беспорядок в трех измерениях , так как он может двигаться вверх стек не только в других частях того же метода, но и в других классах и методах, и все это будет наблюдать разработчик технического обслуживания, трудный путь ! И все же нам говорят, что "Гото опасно", хе!
В конце, о котором я упоминаю, try
/ catch
имеет свое преимущество: оно предназначено для отключения оптимизаций ! Это, если хотите, помощь в отладке ! Это то, для чего оно было разработано, и это то, что должно использоваться как ...
Думаю, это тоже положительный момент. Его можно использовать для отключения оптимизаций, которые в противном случае могли бы нанести вред безопасным, нормальным алгоритмам передачи сообщений для многопоточных приложений, и для выявления возможных условий гонки;) Это единственный сценарий, который я могу придумать, использовать try / catch. Даже у этого есть альтернативы.
Какие оптимизации отключают try
, catch
и finally
?
A.K.A
Как try
, catch
и finally
полезны в качестве средств отладки?
они препятствуют написанию. Это исходит из стандарта:
12.3.3.13 Операторы try-catch
Для выписки stmt вида:
try try-block
catch ( ... ) catch-block-1
...
catch ( ... ) catch-block-n
- Состояние определенного присваивания v в начале try-block совпадает с состоянием определенного присваивания v в начале STMT .
- Состояние определенного присваивания v в начале catch-block-i (для любого i ) такое же, как состояние определенного присваивания v в начале stmt .
- Состояние определенного присваивания v в конечной точке stmt определенно назначается, если (и только если) v определенно назначается в конце -точка try-block и каждого catch-block-i (для каждого i от 1 до n ).
Другими словами, в начале каждого try
оператора:
- все назначения, сделанные видимым объектам до ввода оператора
try
, должны быть завершены, что требует блокировки потока для запуска, что делает его полезным для отладки условий гонки!
- компилятору не разрешено:
- исключить неиспользуемые назначения переменных, которые определенно были назначены до оператора
try
- реорганизовать или объединить любое из его внутренних назначений (т.е. см. Мою первую ссылку, если вы еще этого не сделали).
- поднимать назначения над этим барьером, чтобы задерживать присвоение переменной, которая, как она знает, не будет использоваться позднее (если вообще будет), или превентивно перемещать более поздние назначения вперед, чтобы сделать возможной другую оптимизацию ...
Аналогичная история сохраняется для каждого catch
утверждения; предположим, что в вашем операторе try
(или в вызываемом им конструкторе или функции и т. д.) вы присваиваете этой бессмысленной переменной (скажем, garbage=42;
), компилятор не может удалить этот оператор, нет Неважно, насколько это не имеет отношения к наблюдаемому поведению программы. Назначение должно иметь завершено до ввода блока catch
.
Для чего это стоит, finally
рассказывает аналогично унизительную историю:
12.3.3.14 Операторы try-finally
Для попробуйте оператор stmt формы:
try try-block
finally finally-block
• Состояние определенного присваивания v в начале try-block совпадает с состоянием определенного присваивания v в начале STMT .
• Состояние определенного присваивания v в начале finally-block совпадает с состоянием определенного присваивания v в начале stmt .
• Определенное состояние присваивания v в конечной точке stmt определенно назначается, если (и только если) либо:
o v определенно назначается в конечной точке try-block
o v определенно назначается в конечной точке finally-block
Если выполняется передача потока управления (например, оператор goto ), который начинается в try-block и заканчивается вне try-block , тогда v также считается определенно назначенным для этой передачи потока управления, если v определенно назначено в конечной точке finally-block . (Это не единственный случай, если - если v определенно назначено по другой причине для этой передачи потока управления, то оно все еще считается определенно назначенным.)
12.3.3.15 Операторы try-catch-finally
Анализ определенного присваивания для try - catch - finally оператор формы:
try try-block
catch ( ... ) catch-block-1
...
catch ( ... ) catch-block-n
finally finally-block
выполняется так, как если бы оператор был try - finally оператор, включающий try - catch оператор:
try {
try
try-block
catch ( ... ) catch-block-1
...
catch ( ... ) catch-block-n
}
finally finally-block