Re[9]: Вопрос о виртуальном наследовании интерфейсов.
От: Andrew S Россия http://alchemy-lab.com
Дата: 06.10.09 23:04
Оценка:
AS>>Оба раза this собирается в ecx. Разницу видите?
AS>>А все почему — в месте вызова AA2::AA1::foo неизвестно смещение AA1 относительно АА2, именно ввиду того, что АА2 является виртуальной базой, а значит смещение определяется иерархией наследования.

E>1. Посмотри куда передаётся управление, при виртуальном вызове вот в таком вот случае:
struct C1 {
E>    int F1;
E>    virtual int foo() = 0;
E>};

E>struct C2 { int F2; };

E>struct C3 : C2, C1 { 
E>    int F3;

E>    virtual int foo() { return F1 + F2 + F3; }
E>}; 

E>int testFoo( C1* p ) { return p->foo(); }
E>int test()
E>{
E>    C3 c;
E>    return testFoo( &c );
E>}



Посмотрел. К сожалению, в этом примере студия перемещает С1 в начало объекта С3, поэтому пример не работает. Но не беда, изменим тест.

struct C1 {
    int F1;
    virtual int foo() = 0;
};

struct C2 { int F2;
    virtual void foo1()
    {

    }

};

struct C3 : C2, C1 { 
    int F3;

    virtual int foo()
    {
        printf("dd");
        return F1 + F2 + F3;
    }
}; 

int testFoo( C1* p, C3 *p1)
{
    void *p_0 = &p1->F1;
    void *p_1 = &p1->F2;
    void *p_2 = &p1->F3;
    alloca(100);
    printf("cc");
    DebugBreak();
    p->foo();
    return p1->foo();
}

int test()
{
    C3 c;
    return testFoo( &c, & c);
}



Итак смотрим, что же там. А там все тривиально:


;    COMDAT ?test@@YAHXZ
_TEXT    SEGMENT
_c$ = -20
?test@@YAHXZ PROC NEAR                    ; test, COMDAT

; 1472 : {

    sub    esp, 20                    ; 00000014H

; 1473 :     C3 c;
; 1474 :     return testFoo( &c, & c);

    lea    eax, DWORD PTR _c$[esp+20]
    lea    ecx, DWORD PTR _c$[esp+28]
    neg    eax
    sbb    eax, eax
    lea    edx, DWORD PTR _c$[esp+20]
    and    eax, ecx
    push    edx
    push    eax
    mov    DWORD PTR _c$[esp+28], OFFSET FLAT:??_7C3@@6BC2@@@ ; C3::`vftable'
    mov    DWORD PTR _c$[esp+36], OFFSET FLAT:??_7C3@@6BC1@@@ ; C3::`vftable'
    call    ?testFoo@@YAHPAUC1@@PAUC3@@@Z        ; testFoo

; 1475 : }

    add    esp, 28                    ; 0000001cH
    ret    0
?test@@YAHXZ ENDP                    ; test
_TEXT    ENDS


;    COMDAT ?testFoo@@YAHPAUC1@@PAUC3@@@Z
_TEXT    SEGMENT
_p$ = 8
_p1$ = 12
?testFoo@@YAHPAUC1@@PAUC3@@@Z PROC NEAR            ; testFoo, COMDAT

; 1460 : {

    push    ebp
    mov    ebp, esp

; 1461 :     void *p_0 = &p1->F1;
; 1462 :     void *p_1 = &p1->F2;
; 1463 :     void *p_2 = &p1->F3;
; 1464 :     alloca(100);

    mov    eax, 100                ; 00000064H
    call    __alloca_probe

; 1465 :     printf("cc");

    push    OFFSET FLAT:??_C@_02EMJB@cc?$AA@    ; `string'
    call    DWORD PTR __imp__printf
    add    esp, 4

; 1466 :     DebugBreak();

    call    DWORD PTR __imp__DebugBreak@0

; 1467 :     p->foo();

    mov    ecx, DWORD PTR _p$[ebp]
    mov    eax, DWORD PTR [ecx]
    call    DWORD PTR [eax]

; 1468 :     return p1->foo();

    mov    ecx, DWORD PTR _p1$[ebp]
    add    ecx, 8
    mov    edx, DWORD PTR [ecx]
    call    DWORD PTR [edx]

; 1469 : }

    lea    esp, DWORD PTR [ebp]
    pop    ebp
    ret    0
?testFoo@@YAHPAUC1@@PAUC3@@@Z ENDP            ; testFoo
_TEXT    ENDS


Первая кооректировка this происходит при касте C3 к C1. Что и понятно. Далее в testFoo (я позволил себе вставить маркерные функции в код, чтобы было видно, где мы находимся и не позволить оптимизировать компилятору "слишком просто") происходит корректировка C3 к C1 во втором вызове.

Теперь посмотрим, что из себя представляет виртуальные таблицы С3. Из кода test видно, что их две. foo1 нас не интересует, а foo только в одном экземпляре. Посмотрим, что в ней.


CONST    SEGMENT
??_7C3@@6BC2@@@ DD FLAT:?foo1@C2@@UAEXXZ        ; C3::`vftable'
CONST    ENDS
;    COMDAT ??_7C3@@6BC1@@@
CONST    SEGMENT
??_7C3@@6BC1@@@ DD FLAT:?foo@C3@@UAEHXZ            ; C3::`vftable'
CONST    ENDS


_TEXT    SEGMENT
?foo@C3@@UAEHXZ PROC NEAR                ; C3::foo, COMDAT

; 1453 :     {

    push    esi
    mov    esi, ecx

; 1454 :         printf("dd");

    push    OFFSET FLAT:??_C@_02HICB@dd?$AA@    ; `string'
    call    DWORD PTR __imp__printf

; 1455 :         return F1 + F2 + F3;

    mov    eax, DWORD PTR [esi+8]
    mov    edx, DWORD PTR [esi-4]
    mov    ecx, DWORD PTR [esi+4]
    add    esp, 4
    add    eax, edx
    add    eax, ecx
    pop    esi

; 1456 :     }

    ret    0
?foo@C3@@UAEHXZ ENDP                    ; C3::foo
_TEXT    ENDS


Таким образом foo, изначально появившееся в С1, _всегда_ вызывается с this подобъекта С1, где бы он не находился. А уже конкретная реализация foo корректирует this так, чтобы он соответствовал реальному типу, в данном случае, С3. Она то его всегда знает. А поскольку в месте вызова foo компилятор всегда тем или иным способом может вычислить this для подобъекта, то никаких переходников и не нужно.

Итого. Никаких переходников — тут они не нужны. С другой стороны, я допускаю, что могут быть реализации, имеющие переходники, корректирующие this. Но даже в этом случае для виртуального наследования еще один уровень косвенности для корректировки this необходим ввиду причин, которые я описал ранее.

Т.о., вернувшись к изначальному тезису что виртуальное наследование несет перфоманс пенальти, можно отметить:
1. По вызовам.
2. По кастам.
3. По памяти.

Не так уж и мало, чтобы несколько раз подумать, прежде чем это использовать. Даже без учета "странностей" поведения.

PS Потрясающий способ ведения дискуссий. Пожалуй, на этом надо закругляться.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[11]: Первое и последнее предупреждение.
От: Andrew S Россия http://alchemy-lab.com
Дата: 06.10.09 23:11
Оценка:
AS>>Разница в том, что параметры по умолчанию в этом случае имеют значение. Для реализации.
E>Зачем считалке ссылок параметры конструктора?

Кто сказал что виртуально наследоваться будут только считалки? Я такого не говорил.

E>>>Как так, статически? Вот есть у меня IInterface2* p. И зову я у ного p->f(). Как происходит коррекция this статически? Откуда вызывающая сторона знает, что IInterface2 не первая база объекта, в котором реализована f?


AS>>Пример иерархии приведите.


E>Иерархия всё та же. IBase, из него выведены IInterface1 и IInterface2, а из IInterface1 и IInterface2 выведен CObj.

E>IInterfaceN могут быть выведены как виртуально, так и нет. Я так понимаю, обсуждается вопрос о накладных расходах, связанных с виртуальным наследованием?

Виртуальной является база + (возможно) другие промежуточные классы. Термина "виртуально вывести", к своему стыду, я не знаю.

AS>>Еще как. Причем, в отличие от вас, я привел конкретный пример с кодом. Где явно видна разница между виртуальным наследованием и отсутствием.


E>Там, в случае отсутствия, за одно ещё и множественного нет...


Оно там погоды не делает. Что вы доказали своим следующим примером.

AS>>Смотрите код. Читайте статьи. Лучше чем там, я точно не объясню.

E>Ты понимаешь, что нужда вычислять указатель на IBase возникнет только при вызове его методов? При вызове методов IInterface1 или IInterface2 всё будет как обычно?

Ну вот к примеру базовые методы а-ля AddRef/Release должны быть максимально производительными. А для "абстрактных" примитивов синхронизации не то что виртуальное наследование — даже виртуальный вызов бывает виден.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[12]: Первое и последнее предупреждение.
От: Erop Россия  
Дата: 07.10.09 03:08
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>>>Разница в том, что параметры по умолчанию в этом случае имеют значение. Для реализации.

E>>Зачем считалке ссылок параметры конструктора?

AS>Кто сказал что виртуально наследоваться будут только считалки? Я такого не говорил.

А разве не такую альтернативу решения из ATL мы обсуждаем примерно отсюда
Автор: Erop
Дата: 05.10.09
?..

AS>Ну вот к примеру базовые методы а-ля AddRef/Release должны быть максимально производительными.

Ну так поделишься данными по реальной стоимости виртуальной базы? Насколько дольше-то? По сравнению с виртуальным вызовом и прологом функции, например?

AS>А для "абстрактных" примитивов синхронизации не то что виртуальное наследование — даже виртуальный вызов бывает виден.

А при чём тут тогда интерфейсы и альтернативы ATL?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: Первое и последнее предупреждение.
От: Andrew S Россия http://alchemy-lab.com
Дата: 07.10.09 18:15
Оценка:
AS>>>>Разница в том, что параметры по умолчанию в этом случае имеют значение. Для реализации.
E>>>Зачем считалке ссылок параметры конструктора?

AS>>Кто сказал что виртуально наследоваться будут только считалки? Я такого не говорил.

E>А разве не такую альтернативу решения из ATL мы обсуждаем примерно отсюда
Автор: Erop
Дата: 05.10.09
?..


Я обсуждаю проблемы виртуального наследования. А не частные случаи.

AS>>Ну вот к примеру базовые методы а-ля AddRef/Release должны быть максимально производительными.

E>Ну так поделишься данными по реальной стоимости виртуальной базы? Насколько дольше-то? По сравнению с виртуальным вызовом и прологом функции, например?

Егор, право, неужели поиск отменили?
http://www.rsdn.ru/article/devtools/CppPerformance.xml
Автор(ы): Сергей Сацкий, Роман Плеханов
Дата: 31.07.2007
Сравнение производительности кода, сгенерированного различными компиляторами С++ на различных аппаратных платформах. За основу статьи взят материал отчета Technical Report on C++ Performance комитета WG21. Набор тестов расширен, в некоторых случаях предлагаемый код модифицирован. Приведен более подробный анализ возникающих накладных расходов.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[14]: Первое и последнее предупреждение.
От: Erop Россия  
Дата: 07.10.09 22:59
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>Я обсуждаю проблемы виртуального наследования. А не частные случаи.

А я твой тезис о безальтернативности решения из ATL...

E>>Ну так поделишься данными по реальной стоимости виртуальной базы? Насколько дольше-то? По сравнению с виртуальным вызовом и прологом функции, например?


AS>Егор, право, неужели поиск отменили?

AS>http://www.rsdn.ru/article/devtools/CppPerformance.xml
Автор(ы): Сергей Сацкий, Роман Плеханов
Дата: 31.07.2007
Сравнение производительности кода, сгенерированного различными компиляторами С++ на различных аппаратных платформах. За основу статьи взят материал отчета Technical Report on C++ Performance комитета WG21. Набор тестов расширен, в некоторых случаях предлагаемый код модифицирован. Приведен более подробный анализ возникающих накладных расходов.


Что-то как-то я там не нашёл
1) данных по MSVC
2) вообще случаев, когда вызов метода виртуальной базы значительно бы проигрывал бы вызову метода обычной базы...
Тем более, что если бы мы хотели бы сравнить решение с невиртуальным методом виртуальной базы и виртуальным методом невиртуальной базы, то именно такого сравнения вообще вроде бы нет. Но в любом случае мы имеем отличие в 20-25% от времени просто пустого виртуального вызова. Обычно сами по себе вызовы в нормальной программе занимают очень незначительную часть времени работы (нормальные программы либо что-то полезное делают, либо чего-то ждут, а не "хлопают дверками"), так что 20% экономии от незначительной доли времени работы программы -- это, как-то не впечатляет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[15]: Первое и последнее предупреждение.
От: Andrew S Россия http://alchemy-lab.com
Дата: 08.10.09 17:22
Оценка: :)
AS>>Я обсуждаю проблемы виртуального наследования. А не частные случаи.
E>А я твой тезис о безальтернативности решения из ATL...

E>>>Ну так поделишься данными по реальной стоимости виртуальной базы? Насколько дольше-то? По сравнению с виртуальным вызовом и прологом функции, например?


AS>>Егор, право, неужели поиск отменили?

AS>>http://www.rsdn.ru/article/devtools/CppPerformance.xml
Автор(ы): Сергей Сацкий, Роман Плеханов
Дата: 31.07.2007
Сравнение производительности кода, сгенерированного различными компиляторами С++ на различных аппаратных платформах. За основу статьи взят материал отчета Technical Report on C++ Performance комитета WG21. Набор тестов расширен, в некоторых случаях предлагаемый код модифицирован. Приведен более подробный анализ возникающих накладных расходов.


E>Что-то как-то я там не нашёл

E>1) данных по MSVC

И что? Напомню, кто-то утверждал, что виртуальное наследование не несет накладных расходов на вызов метода.

E>2) вообще случаев, когда вызов метода виртуальной базы значительно бы проигрывал бы вызову метода обычной базы...


http://rsdn.ru/article/devtools/CppPerformance.xml#EGOBG
Автор(ы): Сергей Сацкий, Роман Плеханов
Дата: 31.07.2007
Сравнение производительности кода, сгенерированного различными компиляторами С++ на различных аппаратных платформах. За основу статьи взят материал отчета Technical Report on C++ Performance комитета WG21. Набор тестов расширен, в некоторых случаях предлагаемый код модифицирован. Приведен более подробный анализ возникающих накладных расходов.


E>Тем более, что если бы мы хотели бы сравнить решение с невиртуальным методом виртуальной базы и виртуальным методом невиртуальной базы, то именно такого сравнения вообще вроде бы нет. Но в любом случае мы имеем отличие в 20-25% от времени просто пустого виртуального вызова. Обычно сами по себе вызовы в нормальной программе занимают очень незначительную часть времени работы (нормальные программы либо что-то полезное делают, либо чего-то ждут, а не "хлопают дверками"), так что 20% экономии от незначительной доли времени работы программы -- это, как-то не впечатляет...


Мы хотим сравнить виртуальный метод, разговор шел про абстрактные интерфейсы. Иное сравнивать по мне нет смысла (кстати, есть сомнения, что будет разница для обычных методов?). И не 20% экономии. Отношение от 30 до 80 процентов. Т.е. в плохом случае в ТРИ раза медленнее. На самом деле — примерно в 2 раза, поскольку есть фактически еще один vtbl. Спрашивается, нафига это для абстрактных интерфейсов, а тем более для их реализации?

Итого. Егор, надо уметь проигрывать. А лучше просто не начинать, не будучи уверенным в своих словах. Предлагаю на этом закруглиться, ничего нового я не узнал, и в таком ключе продолжать беседу мне скучно — для меня данная ветка закрыта. Удачи.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[16]: Первое и последнее предупреждение.
От: Erop Россия  
Дата: 08.10.09 18:20
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>И что? Напомню, кто-то утверждал, что виртуальное наследование не несет накладных расходов на вызов метода.

Зависит от реализации...


AS>Мы хотим сравнить виртуальный метод, разговор шел про абстрактные интерфейсы. Иное сравнивать по мне нет смысла (кстати, есть сомнения, что будет разница для обычных методов?). И не 20% экономии. Отношение от 30 до 80 процентов. Т.е. в плохом случае в ТРИ раза медленнее. На самом деле — примерно в 2 раза, поскольку есть фактически еще один vtbl. Спрашивается, нафига это для абстрактных интерфейсов, а тем более для их реализации?


80 процентов не в том сценарии, который предлагалось использовать вообще-то. Кроме того даже эти 80% от очень маленького времени...

AS>Итого. Егор, надо уметь проигрывать. А лучше просто не начинать, не будучи уверенным в своих словах. Предлагаю на этом закруглиться, ничего нового я не узнал, и в таком ключе продолжать беседу мне скучно — для меня данная ветка закрыта. Удачи.


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