
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.3] |
![]() |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Очень много писалось и говорилось об исключениях и производительности, но время бежит, а мощности ростут, компиляторы и язык совершенствуются.
Как уменьшить потери при использовании исключений? Возможно ли? Интересует в первую очередь практическая сторона. Возможно кто-то уже решал подобные задачи, проводил тесты и т.д. Может кому-то пришлось отказаться от исключений вовсе? Хотелось бы обсудить (именно обсудить, а не разводить очередной холивар) все возможные решения проблемы. |
Сообщ.
#2
,
|
|
|
Сейчас сам пишу 3D графический движок, отказался от исключений вообще, производительность снижается заметно, гораздо лучше использовать возвращаемые значения о состоянии выпонения функции. Сам довольно мало испытывал исключения, так как я любитель наиболее оптимального кода и стараюсь использовать вставки на асемблере по максимуму (если это не очень сильно снижает скорость разработки программы).
|
Сообщ.
#3
,
|
|
|
x0ras
Исключения, конечно, снижают производительность. Но не нужно забывать о том, что исключения, в идеале, должны возникать не слишком часто. А если исключения не использовать, то придется каждый вызов функции обрамлять операторами проверки возвращаемого значения, причем эти операторы будут работать всегда, а исключение только во время возникновения ошибки. Так что иногда исключения могут оказаться быстрее, чем проверки возвращаемого значения. Ну, а про читабельность кода я уже не говорю... |
Сообщ.
#4
,
|
|
|
Цитата artalex @ Так что иногда исключения могут оказаться быстрее, чем проверки возвращаемого значения. не могут. сколько раз писалось, даже если они не возникают, они тормозят прогу. |
Сообщ.
#5
,
|
|
|
Цитата Sazabis @ Цитата (artalex @ Сегодня, 17:23) Так что иногда исключения могут оказаться быстрее, чем проверки возвращаемого значения. не могут. сколько раз писалось, даже если они не возникают, они тормозят прогу. Чем? При каком компиляторе? |
Сообщ.
#6
,
|
|
|
Давайте по порядку.
1) если исключения не возникают, то прога практически не тормозится 2) исключения В ЛЮБОМ СЛУЧАЕ будут медленне нежели обычная проверка возвращаемого значения. х0ras-а интересует, как повысить производительность. Ответ - практически никак. Писать свои исключения, менеджеры памяти и т.д. Это связано прежде всего с кодом, который генерируется для "отката". В теневой стек должны быть записаны адреса объектов, которые должны быть разрушены при ненормальной раскрутке. Это процесс довольно быстрый, но он в любом случае занимает ненулевое время. 3) раскрутка стека. Само исключение так же живёт не в стеке (хотя бывает, и живёт, в отдельных реализациях), чаще всего они лежат либо в теневом стеке, либо в куче. А с ними операции тоже не самые быстрые. 4) исключения - это шаг, на который можно и нужно идти, по крайней мере, на этапе отладки. Нп, написать что нибудь типа функций/макросов check_value(), которые в релиз версии должны быть заменены на пустые значения. 5) проверка возвращаемых значений - это прекрасно, т.к. быстрее неё ничего не будет. Но... они хороши в индивидуальной разработке. Когда ты реально можешь грамотно и польностью протестировать весь написаный код. В коммандной разработке часто используются подобные check_value. |
Сообщ.
#7
,
|
|
|
Если говорить о программировании под Windows, то там обработка исключений настолько встроена в систему ( SEH ), что 1) гарантируется минимум накладных расходов, 2)даже если вы сами не используете исключения, определённые структуры для их обработки всё равно поддерживаются. Всё равно в TEB каждого thread-а есть EXCEPTION_REGISTRATION - список обработчиков исключений. Когда вы пишите __try/__except или try/catch, то компилятор создаёт на стеке структуру EXCEPTION_REGISTRATION и добавляет ваш обработчик в список. Таким образом, при использовании исключений у вас чуть увеличивается объём использованного стека и добавляется пара ассемблерных команд в духе
![]() ![]() _asm move fs[off_EXCEPTION_REGISTRATION], handler_addr Моё утверждение следующее: пока не происходит исключение, почти нет потери производительности. Практический пример - видела тысячу и одну рекоммендацию драйверистам касательно использования SEH-а, а вряд ли кто станет возражать, что в драйвере, да ещё в обработчиках ISR-ов, время является весьма критичным ресурсом. Возможно, я некорректно понимаю, и в данной теме под использованием механизма исключений подразумевалась не обработка экстренных ситуаций и не разовые переходы ( как-то при завершении программы ), а именно регулярное использование исключений для передачи управления на другой уровень программы? |
Сообщ.
#8
,
|
|
|
Цитата Lucifer @ Чем? При каком компиляторе? компилятор не важен, а чем, тут уже лучше меня написли. сдаеться мне, автор и сам понимает это. Цитата x0ras @ Как уменьшить потери при использовании исключений? Вопрос в скорости, позволят ли современные машины обрабатывать код с исключениями быстро ? Тут уже дело перформенс тестов: допустимо такое время или нет. А потери всегда будут равноценными, при наличии одного кол-ва обработчиков, для ветви кода <- имхо. Или есть другие мнения ? |
Сообщ.
#9
,
|
|
|
Цитата TrefptYc @ Если говорить о программировании под Windows, то там обработка исключений настолько встроена в систему ( SEH ), что 1) гарантируется минимум накладных расходов, 2)даже если вы сами не используете исключения, определённые структуры для их обработки всё равно поддерживаются. Проблема в другом - это не C++-ные исключения. А потому при их выстреливании никаких побочных эффектов, гарантированных для исключений С++ (как то раскрутка стека, вызов деструкторов и т. п.) не происходит. |
Сообщ.
#10
,
|
|
|
Цитата Flex Ferrum @ Проблема в другом - это не C++-ные исключения. А потому при их выстреливании никаких побочных эффектов, гарантированных для исключений С++ (как то раскрутка стека, вызов деструкторов и т. п.) не происходит. Позвольте, позвольте! Выдержка из одной из статей Мэтта Питрика (Matt Pietrek) касательно SEH. Цитата I'm going to avoid the issue of true C++ exception handling, which uses catch() instead of _except. Under the hood, true C++ exception handling is implemented very similarly to what I'll describe here И как это нет раскрутки стека или деструкторов? А если обработчик вызывается с флагом EH_UNWINDING Цитата What does EH_UNWINDING mean? When an exception callback is invoked with the EH_UNWINDING flag, the operating system is giving the handler function an opportunity to do any cleanup it needs to do. What sort of cleanup? A perfect example is that of a C++ class destructor. When a function's exception handler declines to handle an exception, control typically doesn't exit from that function in a normal manner. Now, consider a function with a C++ class declared as a local variable. The C++ specification says that the destructor must be called. The exception handler callback with the EH_UNWINDING flag is the opportunity for the function to do cleanup work such as invoking destructors and _finally blocks. |
Сообщ.
#11
,
|
|
|
Цитата TrefptYc @ И как это нет раскрутки стека или деструкторов? А если обработчик вызывается с флагом EH_UNWINDING Ну, значит я отстал от жизни. |
Сообщ.
#12
,
|
|
|
В SEH в Win32 ЕСТЬ раскрутка стека... Компилятор MSVC++ даже стандартные исключения С++ (throw, catch) преобразует в вызовы _try, _except. З.Ы. я точно не помню, как именно, надо перечитать Джеффри Рихтера
![]() |
Сообщ.
#13
,
|
|
|
Что значит, обработка исключений встроена в систему? И как это может отражаться на производительности?
Если а) исключительная ситуации в программе перехватывается операционной системой, то даже не знаю как из этой ситуации выбираться, так как выглядит она совершенно неуправляемой; б) при возникновении исключительной ситуации программа выполняет системный вызов для размотки стека, тогда вижу две основные проблемы: первая, системный вызов - это неизбежная потеря времени, при том что ОС не может выполнить связанную с ситуацией работу быстрее, чем сама программа; вторая, в ОС вынужденно встраиваются обработчики исключительных ситуаций всех языков, допускающих обработку исключений, что увеличивает её размер и делает её ещё неповоротливей. в) система обнаруживает исключительные ситуации во время системных вызовов и предупреждает о них вызывающую программу, что позволяет последней адекватно реагировать на ошибки, ну это делали все операционные системы, чуть ли не с момента появления самого понятия "операционная система". Или к разработчикам Windows эта очевидная мысль пришла только сейчас? В DOS такой механизм действовал. г) система позволяет выбрасывать исключения из Callback-функций, корректно обрабатывая их. Идея выглядит здраво, но по некотором размышлении её придётся ограничить, чтобы выбрасывать можно было определённое множество исключений. Таким образом определённо бракуются а и б, нормально поддерживается в и частично г. Лично я стараюсь избегать исключений, анализируя ситуацию заранее. Если например я знаю, что функция не обрабатывает отрицательных параметров, то для обработки их создаю альтернативную ветку, а не перехватываю исключение, даже если оно из неё выбрасывается. |
Сообщ.
#14
,
|
|
|
Цитата Что значит, обработка исключений встроена в систему? И как это может отражаться на производительности? Обработка исключений встроена в систему значит то, что система берет на себя обязанность (или часть обязанности) по раскрутке стека. Также система рпедоставляет функции для программного выкидывания исключений. Цитата а) исключительная ситуации в программе перехватывается операционной системой, то даже не знаю как из этой ситуации выбираться, так как выглядит она совершенно неуправляемой; Именно операционной системой она и перехватывается. По крайней мере для аппаратных исключений. Аппаратные исключения по сути есть прерывания процессора. Подозреваю, что и программные исключения тоже могут ими являться, хотя могут просто вызываться соответствующие обработчики. Цитата Когда вы пишите __try/__except или try/catch, то компилятор создаёт на стеке структуру EXCEPTION_REGISTRATION и добавляет ваш обработчик в список. Таким образом, при использовании исключений у вас чуть увеличивается объём использованного стека и добавляется пара ассемблерных команд в духе ![]() А если учесть, что во всей программе всего несколько блоков try/catch, то издержки несущественны. Более того, обычно программа без использования исключений строится следующим способом: ![]() ![]() int f1_1() { ... } int f1_2() { ... } int f1_3() { ... } int f2_1() { if(!f1_1()) // return error; if(!f1_2()) // return error; if(!f1_3()) // return error; ... } int f2_2() { if(!f1_1()) // return error; if(!f1_2()) // return error; if(!f1_3()) // return error; ... } int f() { if(!f2_1()) { // return error; if(!f2_2()) // return error; ... } Суть в том, что у нас много вложенных вызовов (обычно "высокоуровневые" функции обращаются к функциям "более низкого уровня"), и в каждом случае значение проверяется с помощью if. Количество этих проверок несравнимо больше, чем количество блоков try/catch. При этом операция условного перехода заметно влияет на производительность (по сравнению с mov, например). При использовании исключений проверка ошибок с помощью if присутсвтует только на самом "нижнем" уровне. Остальным функциям в большинстве случаев это не требуется. Далее, если всё-таки вываливается исключение. Тут говорить нечего, конечно это накладно по сравнению с проверкой условия. НО! После выброса исключения обычно задумываться о производительности не приходится. Исключения составляют, пожалуй, лишь программы, обслуживающие много пользователей, в ходе выполнения которых возникает нехватка системных ресурсов (памяти, например), и надо обеспечить остальным пользователям (которым хватило ресурсов) быстрое выполнение. Однако в таких случаях разумнее установить настраиваемое ограничение на количество пользователей/ресрусов, т. к. например Windows при нехватке памяти может просто убить процесс без всяких исключений. Кроме того, используя аппаратные исключения SEH (ошибки доступа к памяти), можно нашаманить более быстрое приложение, которое работает с большими объемами данных (пример у Рихтера). |
Сообщ.
#15
,
|
|
|
Вообще-то аппаратные исключения по стандарту языка C++ как бы и не совсем исключения. По крайней мере их перехват затруднён. Думаю часто хватило бы просто возможности оповестить программу о возникновении такой ситуации.
Раскрутка стека программы операционной системой затруднена хотя бы потому, что программа может использовать какую-то свой, не определённый в системе метод хранения необходимой информации. А так и получится, если программа строится компилятором/сборщиком сторонних фирм, а не создателя ОС. В случае с Windows это особенно актуально, так как MS известна своим пристрастием включать в свои ОС недокументированные возможности. |