Re[8]: Аргумент - результат компиляции.
От: Alexander G Украина  
Дата: 13.01.10 08:41
Оценка:
Здравствуйте, gear nuke, Вы писали:

GN>Это, конечно, совершенно понятно: агрументов в защиту твоей теории нет


Хорошо, если не понимаешь цитаты MSDN и Саттера, вот аргумент кодом.

Чтобы не дать компилятору проигнорировать throw() и его отсутствие, разместим вызовы
void cb1(); и void cb2() throw(); в одной единнице трансляции, а реализацию в другой, при этом /LTCG отключим, в остальном — дефолтные параметры сборки релиза.

Итак, 1.cpp

#include "stdafx.h"

struct X 
{
  X() { puts("ctor"); }
  ~X() { puts("dtor"); }
};


void cb1(); 
void cb2() throw(); 

void test1()
{
  X x;
  cb1();
}

void test2()
{
  X x;
  cb2();
}

int main()
{
  test1();
  test2();
}

2.cpp

void cb1() {}
void cb2() throw(){}


Результат:

int main()
{
00401070  push        esi  
  test1();
00401071  call        test1 (401010h) 
  test2();
00401076  mov         esi,dword ptr [__imp__puts (4020A0h)] 
0040107C  push        offset string "ctor" (402170h) 
00401081  call        esi  
00401083  call        cb2 (4010A0h) 
00401088  push        offset string "dtor" (402178h) 
0040108D  call        esi  
0040108F  add         esp,8 
}
00401092  xor         eax,eax 
00401094  pop         esi  
00401095  ret


test2 заинлайнился, и видно, что оптимизатор предположил, что cb2 никогда не бросит.


Из листинга test1 вполне видно, что исключение из cb1 ожидается.
void test1()
{
00401010  push        0FFFFFFFFh 
00401012  push        offset __ehhandler$?test1@@YAXXZ (4018C8h) 
00401017  mov         eax,dword ptr fs:[00000000h] 
0040101D  push        eax  
0040101E  push        ecx  
0040101F  push        esi  
00401020  mov         eax,dword ptr [___security_cookie (403004h)] 
00401025  xor         eax,esp 
00401027  push        eax  
00401028  lea         eax,[esp+0Ch] 
0040102C  mov         dword ptr fs:[00000000h],eax 
  X x;
00401032  mov         esi,dword ptr [__imp__puts (4020A0h)] 
00401038  push        offset string "ctor" (402170h) 
0040103D  call        esi  
0040103F  mov         dword ptr [esp+18h],0 
  cb1();
00401047  call        cb1 (4010A0h) 
}
0040104C  push        offset string "dtor" (402178h) 
00401051  call        esi  
00401053  add         esp,8 
00401056  mov         ecx,dword ptr [esp+0Ch] 
0040105A  mov         dword ptr fs:[0],ecx 
00401061  pop         ecx  
00401062  pop         esi  
00401063  add         esp,10h 
00401066  ret


Сами cb1 и cb2 расположились по одному адресу.
void cb1() {}
004010A0  ret


Вывод 1: пустой throw() привёл к оптимизации.

Включаем /d1ESrt

Всё остаётся так же, только cb2 отделилась от cb1 и изменилась на:

void cb2() throw(){}
004010B0  push        0FFFFFFFFh 
004010B2  push        offset __ehhandler$?cb2@@YAXXZ (4018F0h) 
004010B7  mov         eax,dword ptr fs:[00000000h] 
004010BD  push        eax  
004010BE  mov         dword ptr fs:[0],esp 
004010C5  mov         ecx,dword ptr [esp] 
004010C8  mov         dword ptr fs:[0],ecx 
004010CF  add         esp,0Ch 
004010D2  ret


Вывод 2: с /d1ESrt пустой throw приводит к пессимизации
Русский военный корабль идёт ко дну!
Re[9]: Аргумент - результат компиляции.
От: gear nuke  
Дата: 13.01.10 11:43
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Хорошо, если не понимаешь цитаты MSDN и Саттера


Я их понимаю, просто не так, как тебе хочется. Наверное, потому что я видел реализацию диспетчера исключений и имею представление о его работе, а не увы.

AG>вот аргумент кодом.


Мне, в общем-то, хватит теоретических умозаключений, зачем оно бы так могло бы быть.

А код... посмотрим. Кстати, зачем лишняя сущность struct X? А как на счёт throw() в её *structors? Это повлияет на листинги

AG>
int main()
AG>{
AG>00401070  push        esi  
AG>  test1();
AG>00401071  call        test1 (401010h) 
AG>  test2();
AG>00401076  mov         esi,dword ptr [__imp__puts (4020A0h)] 
AG>0040107C  push        offset string "ctor" (402170h) 
AG>00401081  call        esi  
AG>00401083  call        cb2 (4010A0h) 
AG>00401088  push        offset string "dtor" (402178h) 
AG>0040108D  call        esi  
AG>0040108F  add         esp,8 
AG>}
AG>00401092  xor         eax,eax 
AG>00401094  pop         esi  
AG>00401095  ret    
AG>

AG>test2 заинлайнился

Причина: SEH фрейм мешает инлайну, в test2 он не устанавливается, поэтому инлайн возможен.

AG>, и видно, что оптимизатор предположил, что cb2 никогда не бросит.


Откуда видно? Мне видно, что диспетчер исключений вызовет unexpected() когда cb2() бросит. Такова его логика — не будет найден хендлер, будет вызван unexpected() за счёт того, что CRT вызвает SetUnhandledExceptionFilter. Если функция не ловит исключение, это делает вызывающий код — оптимизатор здесь его не видит.

AG>Из листинга test1 вполне видно, что исключение из cb1 ожидается.

AG>
AG>void test1()
AG>{
AG>00401010  push        0FFFFFFFFh 
AG>00401012  push        offset __ehhandler$?test1@@YAXXZ (4018C8h) 
AG>00401017  mov         eax,dword ptr fs:[00000000h] 
AG>0040101D  push        eax  
AG>0040101E  push        ecx  
AG>0040101F  push        esi  
AG>00401020  mov         eax,dword ptr [___security_cookie (403004h)] 
AG>00401025  xor         eax,esp 
AG>00401027  push        eax  
AG>00401028  lea         eax,[esp+0Ch] 
AG>0040102C  mov         dword ptr fs:[00000000h],eax 
AG>  X x;
AG>00401032  mov         esi,dword ptr [__imp__puts (4020A0h)] 
AG>00401038  push        offset string "ctor" (402170h) 
AG>0040103D  call        esi  
AG>0040103F  mov         dword ptr [esp+18h],0 
AG>  cb1();
AG>00401047  call        cb1 (4010A0h) 
AG>}
AG>0040104C  push        offset string "dtor" (402178h) 
AG>00401051  call        esi  
AG>00401053  add         esp,8 
AG>00401056  mov         ecx,dword ptr [esp+0Ch] 
AG>0040105A  mov         dword ptr fs:[0],ecx 
AG>00401061  pop         ecx  
AG>00401062  pop         esi  
AG>00401063  add         esp,10h 
AG>00401066  ret      
AG>

Тут много мусора. security cookie можно отключить /GS- (вообще, впринципе и 2.cpp не обязателен, листинг все равно будет)

Что же здесь видно. В выделенной строке сохраняется exception state, признак, что объект x сконструирован и при возникновении исключения нужно вызвать его деструктор. Хотя, про деструктор станет видно, если смотреть цепочку
__ehhandler$?test1@@YGXXZ:
    mov    eax, OFFSET __ehfuncinfo$?test1@@YGXXZ
    jmp    ___CxxFrameHandler3

__unwindtable$?test1@@YGXXZ DD 0ffffffffH ;; exception state до которого выполняется раскрутка
    DD    FLAT:__unwindfunclet$?test1@@YGXXZ$0
__ehfuncinfo$?test1@@YGXXZ DD 019930522H
    DD    01H
    DD    FLAT:__unwindtable$?test1@@YGXXZ
    DD    2 DUP(00H)
    DD    2 DUP(00H)
    DD    00H
    DD    00H

__unwindfunclet$?test1@@YGXXZ$0:
    lea    ecx, DWORD PTR _x$[ebp]
    jmp    ??1X@@QAE@XZ                ; X::~X


Что касается неразрушения объекта в test2 — это как-то противоречит Стандарту? Попробуй завернуть внутренности testX, или main в try блок, деструкторы будут добавлены в таблицу раскрутки.

AG>Вывод 1: пустой throw() привёл к оптимизации.


А не "дефолтные параметры сборки релиза" привели ли к оптимизации?

AG>Включаем /d1ESrt


AG>Всё остаётся так же, только cb2 отделилась от cb1 и изменилась на:


AG>
AG>void cb2() throw(){}
AG>004010B0  push        0FFFFFFFFh 
AG>004010B2  push        offset __ehhandler$?cb2@@YAXXZ (4018F0h) 
AG>004010B7  mov         eax,dword ptr fs:[00000000h] 
AG>004010BD  push        eax  
AG>004010BE  mov         dword ptr fs:[0],esp 
AG>004010C5  mov         ecx,dword ptr [esp] 
AG>004010C8  mov         dword ptr fs:[0],ecx 
AG>004010CF  add         esp,0Ch 
AG>004010D2  ret      
AG>


AG>Вывод 2: с /d1ESrt пустой throw приводит к пессимизации


Да я уже несколько постов пишу, что /d1ESrt использован мною для пессимизации А ты как хотел, включить оптимизацию, и трактовать её заслуги как последствия (не)использования спецификаторов?

В моём варианте, как раз оптимизация полностью отключена. Поэтому становится видно влияние других, определяющих факторов, а именно того, что написано в исходном тексте.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[10]: Аргумент - результат компиляции.
От: Alexander G Украина  
Дата: 13.01.10 12:09
Оценка:
Здравствуйте, gear nuke, Вы писали:

GN>А код... посмотрим. Кстати, зачем лишняя сущность struct X?


Лишняя сущность struct X как раз нужна для того, чтобы показать оптимизацию для cb2.
У неё деструктор вызывается не в раскрутке стека, а как обычный вызов функции, и если cb2 таки бросит исключение, то dtor не вызовется.

GN>А как на счёт throw() в её *structors? Это повлияет на листинги

В названных условиях — никак не повлияет, код один в один, даже адреса те же.

AG>>
int main()
AG>>{
AG>>00401070  push        esi  
AG>>  test1();
AG>>00401071  call        test1 (401010h) 
AG>>  test2();
AG>>00401076  mov         esi,dword ptr [__imp__puts (4020A0h)] 
AG>>0040107C  push        offset string "ctor" (402170h) 
AG>>00401081  call        esi  
AG>>00401083  call        cb2 (4010A0h) 
AG>>00401088  push        offset string "dtor" (402178h) 
AG>>0040108D  call        esi  
AG>>0040108F  add         esp,8 
AG>>}
AG>>00401092  xor         eax,eax 
AG>>00401094  pop         esi  
AG>>00401095  ret    
AG>>

AG>>test2 заинлайнился

GN>Причина: SEH фрейм мешает инлайну, в test2 он не устанавливается, поэтому инлайн возможен.


Да. SEH фрейм стал не нужен благодаря throw(), т.е. throw() это таки оптимизация.

AG>>, и видно, что оптимизатор предположил, что cb2 никогда не бросит.


GN>Откуда видно?

Деструктор после cb2 вызывается как обычная функция.


GN>Что касается неразрушения объекта в test2 — это как-то противоречит Стандарту? Попробуй завернуть внутренности testX, или main в try блок, деструкторы будут добавлены в таблицу раскрутки.


Нет, нифига. Сам попробуй.

Добавляем
int main()
{
  try
  {
    test1();
    test2();
  }
  catch(...)
  {
  }
  puts("I'm fine, thanks");
}

test2 остался без SEH фрейма и заинлайненым.


Изменим cb1 и запустим
void cb1() { throw 1; }
void cb2() throw(){ }

вывод

ctor
dtor
I'm fine, thanks



Вернём cb1 изменим cb2

вывод


void cb1() { }
void cb2() throw(){ throw 1; }


ctor
dtor
ctor
I'm fine, thanks
Русский военный корабль идёт ко дну!
Re[11]: Аргумент - результат компиляции.
От: gear nuke  
Дата: 13.01.10 15:48
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Лишняя сущность struct X как раз нужна для того, чтобы показать оптимизацию для cb2.

AG>У неё деструктор вызывается не в раскрутке стека, а как обычный вызов функции, и если cb2 таки бросит исключение, то dtor не вызовется.

Я могу из этого сделать вывод, что невызов деструктора противоречит 15.5.2. Как сделать выводы об оптимизации не знаю (однако, ближе к концу поста, надеюсь расставить всё по своим местам).

GN>>А как на счёт throw() в её *structors? Это повлияет на листинги

AG>В названных условиях — никак не повлияет, код один в один, даже адреса те же.

Так я и намекаю, что условия тепличные, а взгляд слишком поверхностный.

struct X 
{
  X()  /**/ throw() /**/;
  ~X() /**/ throw() /**/;
};

void cb1(); 
void cb2() throw(); 

void test1()
{
  X x, x2;
  cb1();
}

void test2()
{
  X x, x2;
  cb2();
}

Модель исключений /EHs. Первый вариант — используется спецификатор throw(). В листинге выделена установка exception state.
?test1@@YGXXZ PROC                    ; test1, COMDAT


; 194  : {

    push    -1
    push    __ehhandler$?test1@@YGXXZ
    mov    eax, DWORD PTR fs:0
    push    eax
    mov    DWORD PTR fs:0, esp
    push    ecx

; 195  :   X x, x2;

    lea    ecx, DWORD PTR _x$[esp+16]
    call    ??0X@@QAE@XZ                ; X::X
    lea    ecx, DWORD PTR _x2$[esp+16]
    mov    DWORD PTR __$EHRec$[esp+24], 0 ; 0*
    call    ??0X@@QAE@XZ                ; X::X
    mov    BYTE PTR __$EHRec$[esp+24], 1  ; 1*

; 196  :   cb1();

    call    ?cb1@@YGXXZ                ; cb1

; 197  : }

    lea    ecx, DWORD PTR _x2$[esp+16]
    call    ??1X@@QAE@XZ                ; X::~X
    lea    ecx, DWORD PTR _x$[esp+16]
    call    ??1X@@QAE@XZ                ; X::~X
    mov    ecx, DWORD PTR __$EHRec$[esp+16]
    mov    DWORD PTR fs:0, ecx
    add    esp, 16                    ; 00000010H
    ret    0
_TEXT    ENDS
;    COMDAT text$x
text$x    SEGMENT
__unwindfunclet$?test1@@YGXXZ$0:
    lea    ecx, DWORD PTR _x$[ebp]
    jmp    ??1X@@QAE@XZ                ; X::~X
__unwindfunclet$?test1@@YGXXZ$1:
    lea    ecx, DWORD PTR _x2$[ebp]
    jmp    ??1X@@QAE@XZ                ; X::~X
__ehhandler$?test1@@YGXXZ:
    mov    eax, OFFSET __ehfuncinfo$?test1@@YGXXZ
    jmp    ___CxxFrameHandler3
text$x    ENDS
?test1@@YGXXZ ENDP                    ; test1


?test2@@YGXXZ PROC                    ; test2, COMDAT

; 200  : {

    push    ecx

; 201  :   X x, x2;

    lea    ecx, DWORD PTR _x$[esp+4]
    call    ??0X@@QAE@XZ                ; X::X
    lea    ecx, DWORD PTR _x2$[esp+4]
    call    ??0X@@QAE@XZ                ; X::X

; 202  :   cb2();

    call    ?cb2@@YGXXZ                ; cb2

; 203  : }

    lea    ecx, DWORD PTR _x2$[esp+4]
    call    ??1X@@QAE@XZ                ; X::~X
    lea    ecx, DWORD PTR _x$[esp+4]
    call    ??1X@@QAE@XZ                ; X::~X
    pop    ecx
    ret    0
?test2@@YGXXZ ENDP                    ; test2
В test1() зарегистрировано 2 "раскрутчика стека". При исключении, в зависимости от значения exception state, будет вызван либо __unwindfunclet$?test1@@YGXXZ$0, либо оба обработчика.

0й ehstate устанавливается перед вызовом конструктора X(), а 1й — перед вызовом cb1().
Ключевой момент: упомянутые функции имеют разные спецификаторы исключений

2й вариант без throw() в X. В test1() изменилось лишь разрушение объектов (и кстати видно, что мой компилятор по-старее, не выбрасывает установку -1)
; 197  : }

    lea    ecx, DWORD PTR _x2$[esp+16]
    mov    BYTE PTR __$EHRec$[esp+24], 0
    call    ??1X@@QAE@XZ                ; X::~X
    lea    ecx, DWORD PTR _x$[esp+16]
    mov    DWORD PTR __$EHRec$[esp+24], -1
    call    ??1X@@QAE@XZ                ; X::~X
    mov    ecx, DWORD PTR __$EHRec$[esp+16]
    mov    DWORD PTR fs:0, ecx
    add    esp, 16                    ; 00000010H
    ret    0

?test2@@YGXXZ PROC                    ; test2, COMDAT

; 200  : {

    push    -1
    push    __ehhandler$?test2@@YGXXZ
    mov    eax, DWORD PTR fs:0
    push    eax
    mov    DWORD PTR fs:0, esp
    push    ecx

; 201  :   X x, x2;

    lea    ecx, DWORD PTR _x$[esp+16]
    call    ??0X@@QAE@XZ                ; X::X
    lea    ecx, DWORD PTR _x2$[esp+16]
    mov    DWORD PTR __$EHRec$[esp+24], 0
    call    ??0X@@QAE@XZ                ; X::X

; 202  :   cb2();

    call    ?cb2@@YGXXZ                ; cb2

; 203  : }

    lea    ecx, DWORD PTR _x2$[esp+16]
    call    ??1X@@QAE@XZ                ; X::~X
    lea    ecx, DWORD PTR _x$[esp+16]
    mov    DWORD PTR __$EHRec$[esp+24], -1
    call    ??1X@@QAE@XZ                ; X::~X
    mov    ecx, DWORD PTR __$EHRec$[esp+16]
    mov    DWORD PTR fs:0, ecx
    add    esp, 16                    ; 00000010H
    ret    0
_TEXT    ENDS
;    COMDAT text$x
text$x    SEGMENT
__unwindfunclet$?test2@@YGXXZ$0:
    lea    ecx, DWORD PTR _x$[ebp]
    jmp    ??1X@@QAE@XZ                ; X::~X
__ehhandler$?test2@@YGXXZ:
    mov    eax, OFFSET __ehfuncinfo$?test2@@YGXXZ
    jmp    ___CxxFrameHandler3
text$x    ENDS
?test2@@YGXXZ ENDP                    ; test2
Зато как изменилась test2().
Видно, что зарегистрирован деструктор только одного из объектов — x. Почему так, откуда компилятор "ждёт" исключение? Не надо торопиться с ответом, для него недостаточно исходных данных. Что бы стало достаточно, нужно добавить 3й объект типа X:
; 201  :   X x, x2, x3;

    lea    ecx, DWORD PTR _x$[esp+16]
    call    ??0X@@QAE@XZ                ; X::X
    lea    ecx, DWORD PTR _x2$[esp+16]
    mov    DWORD PTR __$EHRec$[esp+24], 0
    call    ??0X@@QAE@XZ                ; X::X
    lea    ecx, DWORD PTR _x3$[esp+16]
    mov    BYTE PTR __$EHRec$[esp+24], 1
    call    ??0X@@QAE@XZ                ; X::X

; 202  :   cb2();

    call    ?cb2@@YGXXZ                ; cb2
Вот только теперь вопросов не остаётся, похоже, что компилятор "не ждёт" исключения из cb2(). Я не знаю, как можно было сделать такой вывод на основании оригинального кода, разве что угадать!


Так, выходит я сдался? Отнюдь, эта долгая прилюдия лишь что бы показать, что не всё так просто, как хотелось бы:

AG>>>, и видно, что оптимизатор предположил, что cb2 никогда не бросит.


GN>>Откуда видно?

AG>Деструктор после cb2 вызывается как обычная функция.

Ничего не доказывает этот деструктор. Да, читай выше — я даже как бы показал, что компилятор не ждет исключения. Почему? А потому что смотрим внимательно на пример:
void cb2() throw(){}
004010B0  push        0FFFFFFFFh 
004010B2  push        offset __ehhandler$?cb2@@YAXXZ (4018F0h) 
004010B7  mov         eax,dword ptr fs:[00000000h] 
004010BD  push        eax  
004010BE  mov         dword ptr fs:[0],esp 
004010C5  mov         ecx,dword ptr [esp] 
004010C8  mov         dword ptr fs:[0],ecx 
004010CF  add         esp,0Ch 
004010D2  ret

Исключение перехватывается внутри cb2()!

И деструкторы при этом будут вызваны как обычные функции, если enexpected() "вернёт" правильное исключение, никаких нарушений 15.5.2!

Почему же отсутствие throw() так меняет поведение? Потому что в этом случае у функции нет нужды проверять, не выкинула ли она не то исключение. Очевидно, что исключение будет ловиться вызывающим кодом, что и видно на примерах.

GN>>Причина: SEH фрейм мешает инлайну, в test2 он не устанавливается, поэтому инлайн возможен.


AG>Да. SEH фрейм стал не нужен благодаря throw(), т.е. throw() это таки оптимизация.


SEH фрейм никуда не исчез, он переехал из test2() в cb2(). При отсутствии /d1ESrt смог его "оптимизировать" (выкинуть).


На самом деле, разнеся объявления и реализацию по разным единицам трансляции, был поставлен не тот эксперимент.

В конкретном случае с std::exception (о котором и был мой исходный пост) компилятор видит определение. Если бы было указано throw(), ему пришлось бы делать оптимизацию — "поднимать" установку SEH фрейма в вызывающие функции, и после анализа графа вызовов выкидавать его совсем. Часто он справляется, кроме сложных случаев (/d1ESrt). Что бы упростить ему жизнь, разработчики отказались от спецификатора.

GN>>Что касается неразрушения объекта в test2 — это как-то противоречит Стандарту? Попробуй завернуть внутренности testX, или main в try блок, деструкторы будут добавлены в таблицу раскрутки.


AG>Нет, нифига. Сам попробуй.


Спасибо. На ковыряние C++ EH MSVC я и без того потратил не менее 200 часов
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[12]: Аргумент - результат компиляции.
От: Alexander G Украина  
Дата: 13.01.10 16:32
Оценка:
Здравствуйте, gear nuke, Вы писали:


GN>Ничего не доказывает этот деструктор. Да, читай выше — я даже как бы показал, что компилятор не ждет исключения. Почему? А потому что смотрим внимательно на пример:

GN>Исключение перехватывается внутри cb2()!
GN>И деструкторы при этом будут вызваны как обычные функции, если enexpected() "вернёт" правильное исключение, никаких нарушений 15.5.2!
Ну, так это с /d1ESrt , а не в реальной жизни.

GN>Почему же отсутствие throw() так меняет поведение? Потому что в этом случае у функции нет нужды проверять, не выкинула ли она не то исключение. Очевидно, что исключение будет ловиться вызывающим кодом, что и видно на примерах.


GN>>>Причина: SEH фрейм мешает инлайну, в test2 он не устанавливается, поэтому инлайн возможен.


AG>>Да. SEH фрейм стал не нужен благодаря throw(), т.е. throw() это таки оптимизация.


GN>SEH фрейм никуда не исчез, он переехал из test2() в cb2(). При отсутствии /d1ESrt смог его "оптимизировать" (выкинуть).


т.е. здесь
Автор: Alexander G
Дата: 12.01.10

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


GN>На самом деле, разнеся объявления и реализацию по разным единицам трансляции, был поставлен не тот эксперимент.


GN>В конкретном случае с std::exception (о котором и был мой исходный пост) компилятор видит определение.


То, что компилятор видит определение влияет на оптимизацию следующим образом: компилятор может и без throw() понять, что функция никогда не бросит.

GN>Если бы было указано throw(), ему пришлось бы делать оптимизацию — "поднимать" установку SEH фрейма в вызывающие функции, и после анализа графа вызовов выкидавать его совсем.


Что значит пришлось бы поднимать установку SEH фрейма в вызывающие функции? В случае throw() компилятор просто всегда верит, что функция не бросит исключение.

GN>Спасибо. На ковыряние C++ EH MSVC я и без того потратил не менее 200 часов


Я не предлагал ковырять, я просто предлагал проверить, что никакой terminate в описаных условиях не вызывается.
Русский военный корабль идёт ко дну!
Re[13]: Аргумент - результат компиляции.
От: gear nuke  
Дата: 13.01.10 16:53
Оценка:
Здравствуйте, Alexander G, Вы писали:

GN>>И деструкторы при этом будут вызваны как обычные функции, если enexpected() "вернёт" правильное исключение, никаких нарушений 15.5.2!

AG>Ну, так это с /d1ESrt , а не в реальной жизни.

В "реальной жизни" описанная ситуация невозможна, спецификаторы исключений якобы не поддержаны, так что опять нет нарушения 15.5.2.

AG>т.е. здесь
Автор: Alexander G
Дата: 12.01.10

AG>

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


Я наконец-то понял, ты пытаешься рассуждать об общем случае? А я отвечал на вопрос АТ!

GN>>В конкретном случае с std::exception (о котором и был мой исходный пост) компилятор видит определение.


AG>То, что компилятор видит определение влияет на оптимизацию следующим образом: компилятор может и без throw() понять, что функция никогда не бросит.


Разумеется, за исключением нюанса — не должна бросить по контракту. Но у компилятора есть некоторая "глубина анализа", дальше которой он не видит. То есть когда речь идёт о длинных цепочках функций, вызывающих одна другую, оптимизатор может не потянуть.

GN>>Если бы было указано throw(), ему пришлось бы делать оптимизацию — "поднимать" установку SEH фрейма в вызывающие функции, и после анализа графа вызовов выкидавать его совсем.


AG>Что значит пришлось бы поднимать установку SEH фрейма в вызывающие функции?


Я под этим понимаю — смотреть из каких функций вызывается данная, и проверять возможность установить SEH в них, а не в данной. Или заменить бросание исключения безусловным переходом.

AG>Я не предлагал ковырять, я просто предлагал проверить, что никакой terminate в описаных условиях не вызывается.


Не вижу связи между terminate() и оптимизацией std::exception. Я предлагал изучить больше примеров перед попыткой делать выводы.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[14]: Аргумент - результат компиляции.
От: Alexander G Украина  
Дата: 13.01.10 17:08
Оценка:
Здравствуйте, gear nuke, Вы писали:

AG>>т.е. здесь
Автор: Alexander G
Дата: 12.01.10

AG>>

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


GN>Я наконец-то понял, ты пытаешься рассуждать об общем случае? А я отвечал на вопрос АТ!


Ок, всё равно не понял.

здесь
Автор: igna
Дата: 05.01.10

Для оптимизации.


Какой оптимизиции может помочь отсутсвие throw() ?

GN>Разумеется, за исключением нюанса — не должна бросить по контракту. Но у компилятора есть некоторая "глубина анализа", дальше которой он не видит. То есть когда речь идёт о длинных цепочках функций, вызывающих одна другую, оптимизатор может не потянуть.


Может протянуть, может не протянуть, в любом случае, насколько я вижу, хуже от пустого throw() (с /EHs , без /d1... ) не будет.

AG>>Что значит пришлось бы поднимать установку SEH фрейма в вызывающие функции?


GN>Я под этим понимаю — смотреть из каких функций вызывается данная, и проверять возможность установить SEH в них, а не в данной. Или заменить бросание исключения безусловным переходом.


Можно реальный пример, когда SEH фрейм ставится снаружи функции с throw()-спецификацией ?
Русский военный корабль идёт ко дну!
Re[15]: Аргумент - результат компиляции.
От: gear nuke  
Дата: 14.01.10 00:28
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Какой оптимизиции может помочь отсутсвие throw() ?


здесь
Автор: gear nuke
Дата: 12.01.10
же есть пример.

AG>Может протянуть, может не протянуть, в любом случае, насколько я вижу, хуже от пустого throw() (с /EHs , без /d1... ) не будет.


Не-не, погоди Давай переформулируем твое предложение как следует. Вынесем необходимые условия из скобочек:

"С ключем /EHs и без /d1ESrt, в тех случаях, которые ты видел, присутсвие throw() не ухудьшает ситуацию".

А теперь пожалуйста — применяй индукцию для обобщения Я, впрочем, не спорю, что где-то так и есть.

В случае с std::exception это позволяет получить оптимальный код при всех возможных вариантах ключей. С /d1ESrt он лучше, без — по крайней мере, не хуже.

Вот кстати из libcomo, и полагаю везде так:
// This header exists solely for portability.  Normally it just includes
// the header <exception>.

// The header <exception> contains low-level functions that interact
// with a compiler's exception-handling mechanism.  It is assumed to
// be supplied with the compiler, rather than with the library, because
// it is inherently tied very closely to the compiler itself.
и это пишут неплохо разбирающиеся в вопросе люди, они почему-то считают, что если так сделано — значит так надо. Можно поискать объяснения, как это делаю я (сначала -теоретические предпосылки, потом ищем подтверждения на практике). Можно говорить, что это плохо, и лучше по-другому и показывать что в другом случае может быть лучше по-другому, вместо хоть какого-то обоснование в теории.

Может оно надо, что бы результирующий код получался идентичным независимо от ключей компиляции? (libc то собрана с определенными). Вот тут я соглашусь, что с оптимизацией поторопился как с первопричиной. А пока других кандидатов на причины нет — буду придерживаться своей теории.

GN>>Я под этим понимаю — смотреть из каких функций вызывается данная, и проверять возможность установить SEH в них, а не в данной. Или заменить бросание исключения безусловным переходом.


AG>Можно реальный пример, когда SEH фрейм ставится снаружи функции с throw()-спецификацией ?


Я видимо неясно выражаюсь, иначе почему выкидываешь половину контекста? "и после анализа графа вызовов выкидавать его совсем".
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[16]: Аргумент - результат компиляции.
От: Alexander G Украина  
Дата: 14.01.10 08:45
Оценка:
Здравствуйте, gear nuke, Вы писали:


GN>В случае с std::exception это позволяет получить оптимальный код при всех возможных вариантах ключей. С /d1ESrt он лучше, без — по крайней мере, не хуже.


Как с — не важно. Без — не факт, что не хуже, но никогда не лучше.

GN>Вот кстати из libcomo, и полагаю везде так:

GN>
GN>// This header exists solely for portability.  Normally it just includes
GN>// the header <exception>.

GN>// The header <exception> contains low-level functions that interact
GN>// with a compiler's exception-handling mechanism.  It is assumed to
GN>// be supplied with the compiler, rather than with the library, because
GN>// it is inherently tied very closely to the compiler itself.
GN>
и это пишут неплохо разбирающиеся в вопросе люди, они почему-то считают, что если так сделано — значит так надо.


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

GN>Можно поискать объяснения, как это делаю я (сначала -теоретические предпосылки, потом ищем подтверждения на практике).


Теоретические предпосылки для оптимизации при наличии throw() просты: компилятор получает дополнительную информацию, при этом не обеспечивает никаких дополнительных гарантий. Соответственно он может или воспользоваться возмоностью для оптимизации, или нет, но испортить не может. То что throw() это __declspec(nothrow), а __delcpsec(nothrow) служит для оптимизации — это в MSDN сказано.

GN>Может оно надо, что бы результирующий код получался идентичным независимо от ключей компиляции? (libc то собрана с определенными). Вот тут я соглашусь, что с оптимизацией поторопился как с первопричиной. А пока других кандидатов на причины нет — буду придерживаться своей теории.


Оптимизация не только не первопричина, но и не причина вообще.

У меня есть более правдоподобные версии.

1. Т.к. спецификация исключений не поддерживается, решили не требовать от разработчиков их писать в наследниках и не указывать их в стандартной библиотеке и её документации.
2. Так сложилось исторически: в VC8 (я на самом деле приводил листинги VC8) там тоже нет спецификаций исключений. Видимо по какой-либо причние их изначально не указали, в последствии не стали добавлять, чтобы не ломать совместимость со старым кодом.

GN>>>Я под этим понимаю — смотреть из каких функций вызывается данная, и проверять возможность установить SEH в них, а не в данной. Или заменить бросание исключения безусловным переходом.


AG>>Можно реальный пример, когда SEH фрейм ставится снаружи функции с throw()-спецификацией ?


GN>Я видимо неясно выражаюсь, иначе почему выкидываешь половину контекста? "и после анализа графа вызовов выкидавать его совсем".


Т.е. из-за throw() происходит такая штука, как "поднятие" обработчика исключений в вызывающую функцию, при этом поднянтый обработчик всегда выбрасывается? Не верю. Я считаю, что всё проще. Обработчик, который якобы поднимается, никогда не генерируется вообще. Я поверю в наличие "поднятого" ообработчика только кода увижу ситуацию, когда он не выбрасывается.
Русский военный корабль идёт ко дну!
Re[17]: Аргумент - результат компиляции.
От: gear nuke  
Дата: 14.01.10 10:31
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Как с — не важно.


Почему считаешь не важным /d1ESrt? Потому что не используешь сам. Разработчики компилятора не могут руководствоваться такими соображениями. Им необходимо учесть все варианты.

AG>То, что следует использовать стандартную реализацию, это очевидно, но из этого нельзя сджелать никаких выводов, почему в стандартнй реализации сделано так или иначе.


Из этого следует вывод, что разработчикам виднее, я других и не делал.

AG>Теоретические предпосылки для оптимизации при наличии throw() просты: компилятор получает дополнительную информацию, при этом не обеспечивает никаких дополнительных гарантий. Соответственно он может или воспользоваться возмоностью для оптимизации, или нет, но испортить не может. То что throw() это __declspec(nothrow), а __delcpsec(nothrow) служит для оптимизации — это в MSDN сказано.


Да, там сказано что это лучше в одном из случаев (/EHs). При этом там сказано вот что еще:

This attribute tells the compiler that the declared function and the functions it calls never throw an exception.

то есть речь о функциях без определения. std::exception не тот случай, компилятор определение видит, чем ему помогает спецификатор?

AG>У меня есть более правдоподобные версии.


AG>1. Т.к. спецификация исключений не поддерживается, решили не требовать от разработчиков их писать в наследниках и не указывать их в стандартной библиотеке и её документации.


Спецификации поддерживаются.

AG>2. Так сложилось исторически:


Были другие breaking changes, пережили. Я скорее поверю, что /d1ESrt не документируют по историческим причинам. Даже боюсь представить что будет, если он станет широкоизвестен.

AG>Я поверю в наличие "поднятого" ообработчика только кода увижу ситуацию, когда он не выбрасывается.


Она в самом первом моем посте. Просто у тебя зуб на /d1ESrt
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[18]: Аргумент - результат компиляции.
От: Alexander G Украина  
Дата: 14.01.10 10:52
Оценка:
Здравствуйте, gear nuke, Вы писали:

AG>>Как с — не важно.


GN>Почему считаешь не важным /d1ESrt? Потому что не используешь сам. Разработчики компилятора не могут руководствоваться такими соображениями. Им необходимо учесть все варианты.


Учесть все варианты — возможно. Оптимизировать имеет смысл лишь для практически используемых вариантов.

AG>>Теоретические предпосылки для оптимизации при наличии throw() просты: компилятор получает дополнительную информацию, при этом не обеспечивает никаких дополнительных гарантий. Соответственно он может или воспользоваться возмоностью для оптимизации, или нет, но испортить не может. То что throw() это __declspec(nothrow), а __delcpsec(nothrow) служит для оптимизации — это в MSDN сказано.


GN>Да, там сказано что это лучше в одном из случаев (/EHs). При этом там сказано вот что еще:


GN>

This attribute tells the compiler that the declared function and the functions it calls never throw an exception.

GN> то есть речь о функциях без определения. std::exception не тот случай, компилятор определение видит, чем ему помогает спецификатор?

Нет. Из выделенного не следует, что речь исключительно о функциях без определения.

AG>>У меня есть более правдоподобные версии.


AG>>1. Т.к. спецификация исключений не поддерживается, решили не требовать от разработчиков их писать в наследниках и не указывать их в стандартной библиотеке и её документации.


GN>Спецификации поддерживаются.


я про это
Автор: gear nuke
Дата: 13.01.10

В "реальной жизни" описанная ситуация невозможна, спецификаторы исключений якобы не поддержаны,



AG>>2. Так сложилось исторически:


GN>Были другие breaking changes, пережили. Я скорее поверю, что /d1ESrt не документируют по историческим причинам. Даже боюсь представить что будет, если он станет широкоизвестен.


Но было и протягивание нестандартного поведения для совместимости. Хотя бы forScope или передачу временного объекта по неконстантной ссылке.
В данном случае throw() даст мало преимуществ, чтобы его стоило вводить.

AG>>Я поверю в наличие "поднятого" ообработчика только кода увижу ситуацию, когда он не выбрасывается.


GN>Она в самом первом моем посте.


Там он не выбрасывается но и не "поднимается", так что не проходит.

GN>Просто у тебя зуб на /d1ESrt


Да, а также на /d1nonUDToperators
Русский военный корабль идёт ко дну!
Re[18]: Аргумент - результат компиляции.
От: byleas  
Дата: 14.01.10 12:57
Оценка: 7 (1)
Здравствуйте, gear nuke, Вы писали:

GN>Спецификации поддерживаются.

GN>Я скорее поверю, что /d1ESrt не документируют по историческим причинам. Даже боюсь представить что будет, если он станет широкоизвестен.
ESrt в х64 не поддерживаются, только в х86
Re[19]: Аргумент - результат компиляции.
От: gear nuke  
Дата: 14.01.10 14:29
Оценка:
Здравствуйте, byleas, Вы писали:

B>ESrt в х64 не поддерживаются, только в х86


Дык переусложнили EH и не справились наверное но это мелочи


Лучше скажи что-нибудь про std::exception это делалось на заре разбирательств с EH, когда я еще постоянно смотрел генерируемый код, поэтому я уверен что это оптимизация. Но почему-то особо дельше той ревизии не ушло...
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.