Несколько раз натыкался на рассуждения о вредности использования Try ... Catch для производительности. А сегодня даже поспорил с коллегой по этому поводу: он утверждал, что само по себе использование Try для этой производительности вредно. Мне же всегда казалось, что влиять на производительность может только блок Catch, так как там нужно как минимум стек раскручивать.
Было решено сделать тесты и вынести рекомендации :) Результатами тестов и этими самыми рекомендациями я и поделюсь, хотя уверен, это все уже не раз было, да и каждый это сам сделать сможет... Словом, на новизну ни сокрального знания, ни идеи не претендую.
Итак, сделали функцию, которая принимала параметром число и возвращала его произведение самого на себя.
Поместили ее в цикл из 1 600 000 итераций. Использовалась функция так:
long res = Calculate(i);
//чтобы результат типа использовался
if (res < 0) throw new InvalidOperationException();
Этот код мы поместили один раз в Try ... Catch (Catch там сработать не мог, но нам нужно было протестировать влияние самого Try), а второй раз без него. Итого 1 раз 1 600 000 итераций с Try, другой - без. И так 50 раз для чистоты эксперимента.
Разница в этом случае ни разу не превысила 00:00:00.0023953, кроме того, примерно в 1/10 случае она оказывалась отрицательной. То есть, будем считать, что разницы-то никакой и нету :)
Теперь о вреде Catch.
Здесь я сделал такой же тест, но вместо умножения чисел я внутри цикла парсил число из строки. Int32.Parse в одном случае и Int32.TryParse в другом. Строка всегда была "неправильной", то есть Int32.Parse всегда возбуждала исключение, которое я перехватывал и возвращал 0. Int32.TryParse никогда не возбуждает исключения, поэтому в блок Catch в этом случае мы не попадаем.
Здесь я сделал всего 1600 итераций, так как иначе пришлось бы слишком долго ждать.
Выводы следующие:
With Catch: 00:00:04.2675105
Without Catch: 00:00:00.0012498
Diff: 00:00:04.2662607
Итак, рекомендации:
-
Используйте Try ... Catch и Try ... Finally конструкции без страха повредить производительности своего приложения до тех пор, пока это не противоречит пункту "2".
-
Не используйте Try ... Catch конструкции для того, чтобы проверить формат данных или преобразовать что-то во что-то, как было в указанном примере с числами. Иными словами, не используйте Try ... Catch для обработки штатных ситуаций.
-
Используйте Try ... Catch для работы именно с исключительными ситуациями; не выбрасывайте исключение только для того, чтобы выше его перехватить и обработать как штатную ситуацию. Исключение должно возбуждаться только тогда когда с точки зрения подсистемы произошла действительно исключительная ситуация и подсистема просто не в состоянии разрулить ее самостоятельно. В противном случае пользуйтесь возвращаемыми значениями.
Вот и все.
P.S. Весь код собирался и тестировался в режиме Release.