2015-12-20 18 views
5

Tôi đã viết chương trình ngắn này để xem cách ảo hóa sẽ hoạt động như thế nào. Trình biên dịch sẽ có thể suy ra đúng loại:Trình biên dịch ảo hóa, không quá thông minh?

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

Biên soạn với -O2 -std=c++11 sử dụng gcc 5.3 và kêu vang 3.7 qua https://gcc.godbolt.org/.

gì hóa ra là không phải trình biên dịch đã có thể tối ưu hóa tất cả mọi thứ - inlines gcc foo() và làm cho cuộc gọi ảo để bar() trong khi kêu vang làm cho cuộc gọi đến foo() và devirtualizes và inlines gọi để bar().

Trong khi đó, nếu thay vào đó tôi gọi obj->bar(); và sau đó obj->foo();, các trình biên dịch không có vấn đề trong việc tối ưu hóa - kêu vang inlines cả cuộc gọi và gcc làm cho cuộc gọi bình thường để bar() thay vì một ảo và inlines foo().

Có ai có thể giải thích hành vi này không?

+0

câu hỏi này là số lẻ. chúng ta phải trả lời cái gì? GCC tệ hơn Clang? somtimes trình biên dịch có thể hình dung công cụ, đôi khi họ bỏ lỡ. Clang mới hơn và được xây dựng từ đầu để hỗ trợ các loại tối ưu hóa này. –

+0

Không, tôi chỉ tò mò nếu có điều gì đặc biệt vượt quá tối ưu hóa tồi tệ hơn này trong trường hợp cuộc gọi không thực hiện ảo được thực hiện trước. Một số tối ưu hóa đã được thực hiện mà làm phiền devirtualization? – cailinscath

+1

http://hubicka.blogspot.de/2014/04/devirtualization-in-c-part-5-feedback.html cung cấp thông tin cơ bản thú vị cho gcc. Đó là toàn bộ các bài viết về devirtualization từ nhà phát triển gcc đã triển khai nó. Bạn đã thử thêm "-fwhole-program" hoặc "-fsuggest-final-methods" vào gcc chưa? – Jens

Trả lời

5

Có thể do trình biên dịch nghĩ rằng nội tuyến không giúp được gì bởi vì cout quá đắt so với chi phí của cuộc gọi hàm. Nếu bạn thay thế bằng một cái gì đó đơn giản hơn, ví dụ: một sự đảm bảo cho một thành viên, nó sẽ được inlined. Xem dưới đây để đầu ra của

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

hội:

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 
Các vấn đề liên quan