суббота, 25 апреля 2009 г.

Как работают исключения С++ MSVC 2003/2008

Вот ссылочка на достаточно понятный пост про обработку исключений C++:
How a C++ compiler implements exception handling

Upd: Вот еще парочка очень неплохих вводных статей:
О компиляции /EHsc (часть 1)
О компиляции /EHsc (часть 2)


Upd2: По внутренностям SEH-исключений есть хорошая статья Matt'a Pietrek'a, правда старенькая (1997), с тех пор особо ничего для прикладного программиста не изменилось.

Ясно, что использование C++-исключений довольно серьезно замедляет выполнение программы (до 10-15%), автор последних постов тут совершенно прав.
Вопрос только в том - какая область использования у разрабатываемого приложения?
Для gamedev'а на консолях рассуждения о вреде исключений могут иметь смысл - согласен.
Вот с Google C++ Style Guide уже далеко не все столь однозначно.
Нужно четко понимать, что:
  • наличие кодовой базы, неспособной обрабатывать исключения, постепенно сокращается

  • стандартная библиотека и Boost (впрочем, в Google он почти не применяется) все равно будут работать с исключениями, причем интенсивно - таким образом, разработчику все равно придется писать код, устойчивый к возникновению исключений, хотя бы обеспечивающий базовую гарантию Абрамса

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


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

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

Ярким примером того, как действовать не нужно, служит одна из базовых библиотек, на основе которых реализуется картография Transas - там аллокатор при исчерпании доступного пула памяти бросает исключение, обертки системных объектов типа mutex'a или event'a при неудаче бросают исключения и т.д.
В результате в момент загрузки электронной карты в память у нас идет целая череда исключений, каждое из которых, мягко говоря, в обработке обходится дорого.

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

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

После исправления этого ляпа выполнение этого кода ускорилось примерно на два порядка.

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

Комментариев нет: