2015-12-20 16 views
5

Ho scritto questo breve programma per vedere come avrebbe funzionato la virtualizzazione. Il compilatore dovrebbe essere in grado di dedurre il tipo corretto:Devirtualizzazione del compilatore, non troppo intelligente?

#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; 
} 

compilato con -O2 -std=c++11 utilizzando gcc 5.3 e 3.7 clang via https://gcc.godbolt.org/.

quello che si rivelò è che né il compilatore è stato in grado di ottimizzare tutto - inlines gcc foo() e fa la chiamata virtuale per bar() mentre clang rende chiamata a foo() e devirtualizes e inline chiamata a bar().

Nel frattempo, se invece mi chiamo obj->bar(); e poi obj->foo();, i compilatori non hanno alcun problema ad ottimizzare - clang inlines entrambe le chiamate e gcc fa la chiamata normale bar() invece di uno virtuale e inline foo().

Qualcuno può spiegare questo comportamento?

+0

questa domanda è strana. a cosa dovremmo rispondere? GCC è peggio di Clang? alcuni compilatori possono capire le cose, a volte mancano. Clang è più recente ed è stato creato da zero per supportare questo tipo di ottimizzazioni. –

+0

No, sono curioso di sapere se c'è qualcosa di speciale oltre questa ottimizzazione peggiore nel caso in cui la chiamata non virtuale sia fatta prima. Alcune ottimizzazioni sono già state eseguite che disturbano la deviazione? – cailinscath

+1

http://hubicka.blogspot.de/2014/04/devirtualization-in-c-part-5-feedback.html fornisce interessanti informazioni di base per gcc. È un intero serio degli articoli sulla deviazione dallo sviluppatore gcc che l'ha implementato. Hai provato ad aggiungere "-fwhole-program" o "-fsuggest-final-methods" a gcc? – Jens

risposta

5

Probabilmente il compilatore pensa che l'inlining non sia d'aiuto perché il cout è troppo costoso rispetto al sovraccarico della chiamata di funzione. Se lo sostituisci con qualcosa di più semplice, ad es. un assaggio a un membro, verrà sottolineato. Vedi sotto per l'uscita dell'Assemblea

#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; 
} 

:

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 
Problemi correlati