2015-12-20 6 views
5

मैंने यह लघु कार्यक्रम लिखा कि यह देखने के लिए कि कैसे devirtualization काम करेगा। संकलक सही प्रकार अनुमान में सक्षम होना चाहिए:कंपाइलर devirtualization, बहुत स्मार्ट नहीं है?

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

https://gcc.godbolt.org/ के माध्यम से जीसीसी 5.3 और बजना 3.7 का उपयोग कर -O2 -std=c++11 साथ संकलित।

क्या पता चला कि न तो संकलक सब कुछ को अनुकूलित कर सका है - जीसीसी inlines foo() और bar() करने के लिए आभासी कॉल करता है, जबकि बजना foo() करने के लिए कॉल करता है और devirtualizes और inlines bar() को कहते हैं।

इस बीच, अगर बजाय मैं obj->bar(); और फिर obj->foo(); फोन, compilers के अनुकूलन में कोई समस्या नहीं है - बजना दोनों कॉल inlines और जीसीसी के बजाय आभासी एक और inlines foo() की bar() करने के लिए सामान्य कॉल करता है।

क्या कोई इस व्यवहार को समझा सकता है?

+0

इस सवाल का अजीब है। हमें क्या जवाब देना चाहिए? जीसीसी क्लेंग से भी बदतर है? कुछ समय कंपाइलर सामान को चित्रित कर सकते हैं, कभी-कभी वे याद करते हैं। क्लैंग नया है और इस तरह के अनुकूलन का समर्थन करने के लिए स्क्रैच से बनाया गया था। –

+0

नहीं, अगर मैं गैर-आभासी कॉल पहले किया जाता है, तो इस मामले में इस खराब अनुकूलन से परे कुछ खास है, तो मैं सिर्फ उत्सुक हूं। कुछ अनुकूलन पहले से ही प्रदर्शन कर रहे हैं जो devirtualization परेशान? – cailinscath

+1

http://hubicka.blogspot.de/2014/04/devirtualization-in-c-part-5-feedback.html जीसीसी के लिए दिलचस्प पृष्ठभूमि जानकारी प्रदान करता है। यह जीसीसी डेवलपर से devirtualization के बारे में लेखों की एक पूरी गंभीर है जो इसे लागू किया। क्या आपने gcc में "-fwhole-program" या "-fsuggest-final-विधियां" जोड़ने का प्रयास किया है? – Jens

उत्तर

5

ऐसा शायद इसलिए है क्योंकि संकलक सोचता है कि इनलाइनिंग मदद नहीं करती है क्योंकि cout फ़ंक्शन कॉल के ऊपरी हिस्से की तुलना में बहुत महंगा है। यदि आप इसे कुछ आसान से बदलते हैं, उदा। एक सदस्य के लिए एक समझौता, यह रेखांकित हो जाएगा।

#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 
संबंधित मुद्दे