2015-12-20 9 views
5

Napisałem ten krótki program, aby sprawdzić, jak działa devirtualization. Kompilator powinien być w stanie wywnioskować, o typieDevirtualization kompilatora, niezbyt inteligentny?

#include <iostream> 
using std::cout; 
using std::endl; 

class Base 
{ 
public: 
    void foo() { cout << "Base::foo" << endl; } 
    virtual void bar() { cout << "Base::bar" << endl; } 
    virtual ~Base() = default; 
}; 

class Child : public Base 
{ 
public: 
    void foo() { cout << "Child::foo" << endl; } 
    void bar() { cout << "Child::bar" << endl; } 
}; 

int main() 
{ 
    Base* obj = new Child; 
    obj->foo(); 
    obj->bar(); 
    delete obj; 
} 

skompilowany z -O2 -std=c++11 gcc 5.3, 3.7 za pomocą szczęk https://gcc.godbolt.org/.

Co okazało się, że ani kompilator był w stanie zoptymalizować wszystko - gcc inlines foo() i sprawia, że ​​wirtualny wezwanie do bar() podczas dzyń sprawia wezwanie do foo() i devirtualizes i inlines zadzwonić do bar().

Tymczasem, jeśli zamiast zadzwonić obj->bar(); a następnie obj->foo();, kompilatory nie mają problemu w optymalizacji - dzyń inlines oba połączenia i gcc sprawia zwykłe połączenie do bar() zamiast wirtualnego jednego i inlines foo().

Czy ktoś może wyjaśnić to zachowanie?

+0

to pytanie jest dziwne. na co mamy odpowiedzieć? GCC jest gorszy niż Clang? czasami kompilatorzy mogą wymyślać rzeczy, czasami tęsknią. Clang jest nowszy i został zbudowany od podstaw, aby obsługiwać tego rodzaju optymalizacje. –

+0

Nie, jestem ciekawy, czy jest coś wyjątkowego poza tą gorszą optymalizacją w przypadku, gdy wywołanie inne niż wirtualne jest wykonywane jako pierwsze. Niektóre optymalizacje są już wykonywane, które zakłócają devirtualization? – cailinscath

+1

http://hubicka.blogspot.de/2014/04/devirtualization-in-c-part-5-feedback.html dostarcza interesujących informacji podstawowych dotyczących gcc. Jest to cały poważny artykuł o devirtualization od dewelopera gcc, który zaimplementował go. Czy próbowałeś dodać "-fwhole-program" lub "-fsuggest-final-methods" do gcc? – Jens

Odpowiedz

5

Jest tak prawdopodobnie dlatego, że kompilator uważa, że ​​wstawianie nie pomaga, ponieważ cout jest zbyt kosztowne w porównaniu do narzutu wywołania funkcji. Jeśli zastąpisz go czymś prostszym, np. przydział do członka, zostanie on przedstawiony. Zobacz poniżej wyjściu

#include <iostream> 
using std::cout; 
using std::endl; 

class Base 
{ 
public: 
    void foo() { i = 1; } 
    virtual void bar() { i = 2; } 
    virtual ~Base() = default; 

    int i = 0; 
}; 

class Child : public Base 
{ 
public: 
    void foo() { i = 3; } 
    void bar() { i = 4; } 
}; 

int main() 
{ 
    Base* obj = new Child; 
    obj->foo(); 
    obj->bar(); 
    std::cout << obj->i << std::endl; 
    //delete obj; 
} 

Zgromadzenia:

Base::bar(): 
     movl $2, 8(%rdi) 
     ret 
Child::bar(): 
     movl $4, 8(%rdi) 
     ret 
Base::~Base(): 
     ret 
Child::~Child(): 
     ret 
Child::~Child(): 
     jmp  operator delete(void*) 
Base::~Base(): 
     jmp  operator delete(void*) 
main: 
     subq $8, %rsp 
     movl $16, %edi 
     call operator new(unsigned long) 
     movl $4, %esi 
     movl std::cout, %edi 
     call std::basic_ostream<char, std::char_traits<char> >::operator<<(int) 
     movq %rax, %rdi 
     call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&) 
     xorl %eax, %eax 
     addq $8, %rsp 
     ret 
     subq $8, %rsp 
     movl std::__ioinit, %edi 
     call std::ios_base::Init::Init() 
     movl $__dso_handle, %edx 
     movl std::__ioinit, %esi 
     movl std::ios_base::Init::~Init(), %edi 
     addq $8, %rsp 
     jmp  __cxa_atexit 
Powiązane problemy