
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.207] |
![]() |
|
Сообщ.
#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 известна своим пристрастием включать в свои ОС недокументированные возможности. |
Сообщ.
#16
,
|
|
|
Накладные расходы всегда появляются при использовании исключений во время выполнения программы, а не только при возникновении исключительных ситуаций. Я вижу смысл использовать исключения только в отладочной версии, т.к. в моем случае в релизе они бесполезны. Сейчас мне в голову приходит только одно решение - написать такой код, который бы в отладочной версии проводил проверки и выбрасывал/перехватывал исключения, а в релизе все проверки убивались.
|
Сообщ.
#17
,
|
|
|
Цитата x0ras @ Накладные расходы всегда появляются при использовании исключений во время выполнения программы, а не только при возникновении исключительных ситуаций. Я вижу смысл использовать исключения только в отладочной версии, т.к. в моем случае в релизе они бесполезны. Сейчас мне в голову приходит только одно решение - написать такой код, который бы в отладочной версии проводил проверки и выбрасывал/перехватывал исключения, а в релизе все проверки убивались. Насчёт накладных расходов. Решила наконец, что If in doubt, check it out Сравниваем. Проект без SEH ![]() ![]() PUBLIC _main PUBLIC ??_C@_02DPKJAMEF@?$CFd?$AA@ ; `string' PUBLIC ??_C@_0CJ@LGAHIOIG@should?5not?5be?5here?4?5Maybe?5zero?5d@ ; `string' EXTRN _printf:NEAR EXTRN _scanf:NEAR ; COMDAT ??_C@_02DPKJAMEF@?$CFd?$AA@ ; File d:\myprojects\4test\4test.cpp CONST SEGMENT ??_C@_02DPKJAMEF@?$CFd?$AA@ DB '%d', 00H ; `string' CONST ENDS ; COMDAT ??_C@_0CJ@LGAHIOIG@should?5not?5be?5here?4?5Maybe?5zero?5d@ CONST SEGMENT ??_C@_0CJ@LGAHIOIG@should?5not?5be?5here?4?5Maybe?5zero?5d@ DB 'should no' DB 't be here. Maybe zero division?', 00H ; `string' ; Function compile flags: /Ogty CONST ENDS ; COMDAT _main _TEXT SEGMENT _a$ = -4 ; size = 4 _main PROC NEAR ; COMDAT ; 8 : { push ecx ; 9 : int a = 10; ; 10 : a = 15; ; 11 : a += 20; ; 12 : a -= 40; ; 13 : printf( "%d", a ); push -5 ; fffffffbH push OFFSET FLAT:??_C@_02DPKJAMEF@?$CFd?$AA@ mov DWORD PTR _a$[esp+12], -5 ; fffffffbH call _printf ; 14 : scanf( "%d", &a ); lea eax, DWORD PTR _a$[esp+12] push eax push OFFSET FLAT:??_C@_02DPKJAMEF@?$CFd?$AA@ call _scanf ; 15 : if( a > 0 ) mov eax, DWORD PTR _a$[esp+20] add esp, 16 ; 00000010H test eax, eax jle SHORT $L58684 ; 16 : printf( "should not be here. Maybe zero division?" ); push OFFSET FLAT:??_C@_0CJ@LGAHIOIG@should?5not?5be?5here?4?5Maybe?5zero?5d@ call _printf add esp, 4 $L58684: ; 17 : return 0; xor eax, eax ; 18 : } pop ecx ret 0 _main ENDP _TEXT ENDS END А теперь c SEH( ветка, которая раньше была в обработчике IF вынесена в EXCEPT_HANDLER Проект скомпилирован с теми же стандартными настройками на VS .NET ![]() ![]() PUBLIC _main PUBLIC ??_C@_02DPKJAMEF@?$CFd?$AA@ ; `string' PUBLIC ??_C@_0CJ@LGAHIOIG@should?5not?5be?5here?4?5Maybe?5zero?5d@ ; `string' EXTRN _printf:NEAR EXTRN _scanf:NEAR EXTRN __except_handler3:NEAR EXTRN __except_list:DWORD ; COMDAT CONST ; File d:\myprojects\4test\4test.cpp CONST SEGMENT $T58689 DD 0ffffffffH DD FLAT:$L58683 DD FLAT:$L58684 CONST ENDS ; COMDAT ??_C@_02DPKJAMEF@?$CFd?$AA@ CONST SEGMENT ??_C@_02DPKJAMEF@?$CFd?$AA@ DB '%d', 00H ; `string' CONST ENDS ; COMDAT ??_C@_0CJ@LGAHIOIG@should?5not?5be?5here?4?5Maybe?5zero?5d@ CONST SEGMENT ??_C@_0CJ@LGAHIOIG@should?5not?5be?5here?4?5Maybe?5zero?5d@ DB 'should no' DB 't be here. Maybe zero division?', 00H ; `string' ; Function compile flags: /Ogty CONST ENDS ; COMDAT _main _TEXT SEGMENT _a$ = -28 ; size = 4 [COLOR=red]__$SEHRec$ = -24 [/COLOR] ; size = 24 _main PROC NEAR ; COMDAT ; 8 : { [COLOR=red] push ebp mov ebp, esp push -1 push OFFSET FLAT:$T58689 push OFFSET FLAT:__except_handler3 mov eax, DWORD PTR fs:__except_list push eax mov DWORD PTR fs:__except_list, esp sub esp, 12 ; 0000000cH push ebx push esi push edi mov DWORD PTR __$SEHRec$[ebp], esp[/COLOR] ; 9 : int a = 10; mov DWORD PTR _a$[ebp], 10 ; 0000000aH ; 10 : __try [COLOR=red]mov DWORD PTR __$SEHRec$[ebp+20], 0[/COLOR] ; 11 : { ; 12 : a = 15; mov DWORD PTR _a$[ebp], 15 ; 0000000fH ; 13 : a += 20; mov DWORD PTR _a$[ebp], 35 ; 00000023H ; 14 : a -= 40; mov DWORD PTR _a$[ebp], -5 ; fffffffbH ; 15 : printf( "%d", a ); push -5 ; fffffffbH push OFFSET FLAT:??_C@_02DPKJAMEF@?$CFd?$AA@ call _printf ; 16 : scanf( "%d", &a ); lea eax, DWORD PTR _a$[ebp] push eax push OFFSET FLAT:??_C@_02DPKJAMEF@?$CFd?$AA@ call _scanf add esp, 16 ; 00000010H jmp SHORT $L58693 $L58683: ; 17 : } ; 18 : __except(EXCEPTION_EXECUTE_HANDLER) [COLOR=red]mov eax, 1 $L58685: ret 0 $L58684: mov esp, DWORD PTR __$SEHRec$[ebp][/COLOR] ; 19 : { ; 20 : printf( "should not be here. Maybe zero division?" ); push OFFSET FLAT:??_C@_0CJ@LGAHIOIG@should?5not?5be?5here?4?5Maybe?5zero?5d@ call _printf add esp, 4 $L58693: mov DWORD PTR __$SEHRec$[ebp+20], -1 ; 21 : } ; 22 : return 0; xor eax, eax ; 23 : } [COLOR=red] mov ecx, DWORD PTR __$SEHRec$[ebp+8] mov DWORD PTR fs:__except_list, ecx pop edi pop esi pop ebx mov esp, ebp pop ebp[/COLOR] ret 0 _main ENDP _TEXT ENDS END Бросаются в глаза две вещи: 1) большие накладные расходы на начало\завершение функции, где используются исключения 2) то, что оптимизация при использовании SEH, если и не отключается, то сильно сдаёт. 2-ое мне откровенно непонятно, в то время как первое заставило иначе взглянуть на своё использовании исключений. -юсртыхэю Цитата amk @ Раскрутка стека программы операционной системой затруднена хотя бы потому, что программа может использовать какую-то свой, не определённый в системе метод хранения необходимой информации. А так и получится, если программа строится компилятором/сборщиком сторонних фирм, а не создателя ОС. В случае с Windows это особенно актуально, так как MS известна своим пристрастием включать в свои ОС недокументированные возможности. Но многие используют VisualStudio, который вполне от производителя ОС. А Borland тоже получил какое-то тайное знания от Microsoft касательно того, как реализовать обработку исключений ( по крайней мере, Matt Pietrek так считал ). Что же до Intel-овского компилятора, то у них замечательная дружба с Microsoft и они лучше многих знают внутренние структуры ( посмотреть хотя бы на VTune ). А какие ещё компиляторы популярны под Windows, чтобы считать эту проблему актуальной? |
Сообщ.
#18
,
|
|
|
Продолжаем эксперемент:
VC 7.1 Код без исключений ![]() ![]() void main() { 00401000 push ecx 00401001 push esi //PerformanceTest(); int a = 10; a = 15; a += 20; a -= 40; printf( "%d", a ); 00401002 mov esi,dword ptr [__imp__printf (402064h)] 00401008 push 0FFFFFFFBh 0040100A push offset string "%d" (4020D8h) 0040100F mov dword ptr [esp+0Ch],0FFFFFFFBh 00401017 call esi scanf( "%d", &a ); 00401019 lea eax,[esp+0Ch] 0040101D push eax 0040101E push offset string "%d" (4020D8h) 00401023 call dword ptr [__imp__scanf (402068h)] if (a > 0) 00401029 mov eax,dword ptr [esp+14h] 0040102D add esp,10h 00401030 test eax,eax 00401032 jle main+3Eh (40103Eh) printf( "should not be here. Maybe zero division?" ); 00401034 push offset string "should not be here. Maybe zero d"... (4020ACh) 00401039 call esi 0040103B add esp,4 return; } 0040103E xor eax,eax 00401040 pop esi 00401041 pop ecx 00401042 ret Он же, но с С++-исключениями ![]() ![]() void main() { 00401000 push ecx //PerformanceTest(); int a = 10; try { a = 15; a += 20; a -= 40; printf( "%d", a ); 00401001 push 0FFFFFFFBh 00401003 push offset string "%d" (4020ACh) 00401008 mov dword ptr [esp+8],0FFFFFFFBh 00401010 call dword ptr [__imp__printf (402064h)] scanf( "%d", &a ); 00401016 lea eax,[esp+8] 0040101A push eax 0040101B push offset string "%d" (4020ACh) 00401020 call dword ptr [__imp__scanf (402068h)] } catch (...) { printf( "should not be here. Maybe zero division?" ); } return; } 00401026 xor eax,eax 00401028 add esp,14h 0040102B ret Он же, но с SEH ![]() ![]() void main() { 00401000 push ebp 00401001 mov ebp,esp 00401003 push 0FFFFFFFFh 00401005 push 4020E0h 0040100A push offset _except_handler3 (40114Eh) 0040100F mov eax,dword ptr fs:[00000000h] 00401015 push eax 00401016 mov dword ptr fs:[0],esp 0040101D sub esp,0Ch 00401020 push ebx 00401021 push esi 00401022 push edi 00401023 mov dword ptr [ebp-18h],esp //PerformanceTest(); int a = 10; 00401026 mov dword ptr [a],0Ah __try 0040102D mov dword ptr [ebp-4],0 { a = 15; 00401034 mov dword ptr [a],0Fh a += 20; 0040103B mov dword ptr [a],23h a -= 40; 00401042 mov dword ptr [a],0FFFFFFFBh printf( "%d", a ); 00401049 push 0FFFFFFFBh 0040104B push offset string "%d" (4020D8h) 00401050 call dword ptr [__imp__printf (402064h)] scanf( "%d", &a ); 00401056 lea eax,[a] 00401059 push eax 0040105A push offset string "%d" (4020D8h) 0040105F call dword ptr [__imp__scanf (402068h)] 00401065 add esp,10h 00401068 jmp $L39474+11h (401081h) } __except(EXCEPTION_EXECUTE_HANDLER) 0040106A mov eax,1 $L39481: 0040106F ret $L39474: 00401070 mov esp,dword ptr [ebp-18h] { printf( "should not be here. Maybe zero division?" ); 00401073 push offset string "should not be here. Maybe zero d"... (4020ACh) 00401078 call dword ptr [__imp__printf (402064h)] 0040107E add esp,4 00401081 mov dword ptr [ebp-4],0FFFFFFFFh } return; } 00401088 xor eax,eax 0040108A mov ecx,dword ptr [ebp-10h] 0040108D mov dword ptr fs:[0],ecx 00401094 pop edi 00401095 pop esi 00401096 pop ebx 00401097 mov esp,ebp 00401099 pop ebp 0040109A ret Что называется, почувствуйте разницу... Добавлено В связи с чем предлагаю следующее дополнение к правилам раздела: Цитата Если кто-то берется утвержать, что та или иная языковая конструкция неэффективна, неоптимальна, просаживает производительность кода, и т. д. и т. п., то любое такое утверждение должно быть подкреплено не только кодом на С++, но и ассемблерным листингом (полученным по приведенному коду), иллюстрирующим утверждение, замерами производительности или любыми другими весомыми доказательствами. Иначе имеет смысл сразу же закрывать тему за недоказанностью "обвинений". |
Сообщ.
#19
,
|
|
|
Flex Ferrum
Дополнение к правилам шикарное, за одним НО. Есть знание теоретическое и знание практическое. Может конкретно на этом примере так случилось, что результаты оказались такими неэффективными. А вдруг например на kernel-space или на другом компиляторе картина будет прямо противоположная. Если когда-нибудь занимался попыткамиотносительно точно замерить производительность, то поймёшь... Когда два теста без перекомпиляции но при перезагрузке ОС давали диаметрально разные результаты... |
Сообщ.
#20
,
|
|
|
Цитата TrefptYc @ Дополнение к правилам шикарное, за одним НО. Есть знание теоретическое и знание практическое. Так потому и предлагается спорить не голословно, а с подобающей аргументацией. Кстати, если кто-то соберется выкинуть исключение, то код main начинает выглядеть так: ![]() ![]() void main() { 00401020 push ebp 00401021 mov ebp,esp 00401023 push 0FFFFFFFFh 00401025 push offset __ehhandler$_main (401580h) 0040102A mov eax,dword ptr fs:[00000000h] 00401030 push eax 00401031 mov dword ptr fs:[0],esp 00401038 sub esp,8 0040103B push ebx 0040103C push esi 0040103D push edi 0040103E mov dword ptr [ebp-10h],esp //PerformanceTest(); int a = 10; try { a = 15; a += 20; a -= 40; printf( "%d", a ); 00401041 push 0FFFFFFFBh 00401043 push offset string "%d" (4020E8h) 00401048 mov dword ptr [ebp-4],0 0040104F mov dword ptr [a],0FFFFFFFBh 00401056 call dword ptr [__imp__printf (40206Ch)] scanf( "%d", &a ); 0040105C lea eax,[a] 0040105F push eax 00401060 push offset string "%d" (4020E8h) 00401065 call dword ptr [__imp__scanf (402070h)] 0040106B add esp,10h a /= foo___(); 0040106E jmp foo___ (401000h) printf( "%d", a ); } catch (...) { printf( "should not be here. Maybe zero division?" ); 00401073 push offset string "should not be here. Maybe zero d"... (4020BCh) 00401078 call dword ptr [__imp__printf (40206Ch)] 0040107E add esp,4 } 00401081 mov eax,offset $L37751 (401087h) 00401086 ret return; } 00401087 mov ecx,dword ptr [ebp-0Ch] 0040108A pop edi 0040108B pop esi 0040108C xor eax,eax 0040108E mov dword ptr fs:[0],ecx 00401095 pop ebx 00401096 mov esp,ebp 00401098 pop ebp 00401099 ret |
Сообщ.
#21
,
|
|
|
Flex Ferrum
Ну всё правильно. В твоём первом примере с С++-исключениями по ветке catch(...) пойти не могли, ведь, насколько я помню, С++ обработчтик не перехватывает железные исключения, а софтверных ты не кидал и деления на константук у тебя не было. поэтому компилятор просто убрал обработку исключений, как если бы он убрал конструкцию if(0){}. Так что адекватное практическое знание ещё получить нужно... |
Сообщ.
#22
,
|
|
|
Из этого, кстати, следует весьма интересный вывод. Пустой спецификатор исключений - вполне хороший способ избежать оверхеда. Т. е. если нетривиальный метод гарантирует, что не выкинет исключений (гарантия отсутствия ошибок), то имеет смысл помечать его как throw(), что позволит компилятору опимизировать его вызов.
Добавлено Вот еще два примера кода. В обоих случаях вызывается метод foo___. Но в первом случае он может кидать любые исключения, а во втором случае помечен как throw(). ![]() ![]() void main() { push ebp mov ebp,esp push 0FFFFFFFFh push offset __ehhandler$_main (4015A0h) mov eax,dword ptr fs:[00000000h] push eax mov dword ptr fs:[0],esp sub esp,8 push ebx push esi //PerformanceTest(); int a = 10; try { a = 15; a += 20; a -= 40; printf( "%d", a ); mov esi,dword ptr [__imp__printf (40206Ch)] push edi mov dword ptr [ebp-10h],esp push 0FFFFFFFBh push offset string "%d" (4020E8h) mov dword ptr [ebp-4],0 mov dword ptr [a],0FFFFFFFBh call esi scanf( "%d", &a ); lea eax,[a] push eax push offset string "%d" (4020E8h) call dword ptr [__imp__scanf (402070h)] add esp,10h a /= foo___(); call foo___ (401000h) mov ecx,eax mov eax,dword ptr [a] cdq idiv eax,ecx printf( "%d", a ); push eax push offset string "%d" (4020E8h) mov dword ptr [a],eax call esi add esp,8 return; } mov ecx,dword ptr [ebp-0Ch] pop edi pop esi xor eax,eax mov dword ptr fs:[0],ecx pop ebx mov esp,ebp pop ebp ret } catch (...) { printf( "should not be here. Maybe zero division?" ); push offset string "should not be here. Maybe zero d"... (4020BCh) call dword ptr [__imp__printf (40206Ch)] add esp,4 } mov eax,offset $L37743 (40108Bh) ret ![]() ![]() void main() { push ecx push esi //PerformanceTest(); int a = 10; try { a = 15; a += 20; a -= 40; printf( "%d", a ); mov esi,dword ptr [__imp__printf (40206Ch)] push 0FFFFFFFBh push offset string "%d" (4020BCh) mov dword ptr [esp+0Ch],0FFFFFFFBh call esi scanf( "%d", &a ); lea eax,[esp+0Ch] push eax push offset string "%d" (4020BCh) call dword ptr [__imp__scanf (402070h)] a /= foo___(); call foo___ (401000h) mov ecx,eax mov eax,dword ptr [esp+14h] cdq idiv eax,ecx printf( "%d", a ); push eax push offset string "%d" (4020BCh) mov dword ptr [esp+1Ch],eax call esi add esp,18h } catch (...) { printf( "should not be here. Maybe zero division?" ); } return; } xor eax,eax pop esi pop ecx ret |
Сообщ.
#23
,
|
|
|
Цитата Flex Ferrum @ Судя по листингу, никакой обработки нет - компилятор выкинул.Он же, но с С++-исключениями Да и с POD-типами не совсем честно. Надо внутрь try засунуть какой-нибудь не-POD тип, например, vector. ![]() Добавлено меня опередили ![]() |