2009-04-01 33 views
5

Lập cấu hình mã C++ của tôi với gprof, tôi phát hiện ra rằng một phần đáng kể thời gian của tôi được sử dụng để gọi một phương thức ảo liên tục. Bản thân phương thức này ngắn và có thể được gạch chân nếu nó không phải là ảo.Tăng tốc các cuộc gọi hàm ảo trong gcc

Một số cách để tôi có thể tăng tốc độ này lên quá ngắn để viết lại tất cả để không phải là ảo?

+0

Có thể đăng một đoạn mã đại diện cho funciton ảo và cách nó được gọi. –

+0

Tôi đã không sử dụng gprof, nhưng nó không đo thời gian * bên trong * chức năng hơn là thời gian gọi? – Uri

+0

Ảo không ngụ ý không được gạch chân. Vui lòng xem câu trả lời đầy đủ của tôi để biết chi tiết. –

Trả lời

1

Đôi khi, bạn nên xem xét cách viết mã trong 'C' cũ nếu bạn không có sẵn đường cú pháp của C++. Đôi khi câu trả lời không sử dụng một cuộc gọi gián tiếp. Xem this answer để biết ví dụ.

+0

+1. Giải pháp này là hợp lý vì trong trường hợp này người hỏi đã thực sự xác định nguyên nhân của sự chậm chạp là cuộc gọi hàm này - nhưng nói chung, không "tối ưu hóa" bằng công tắc cho đến khi bạn chắc chắn mã của bạn đang dành thời gian . –

9

Bạn có chắc thời gian liên quan đến cuộc gọi không? Nó có thể là chính chức năng mà chi phí là? Nếu đây là trường hợp chỉ đơn giản là nội tuyến có thể làm cho chức năng biến mất từ ​​hồ sơ của bạn nhưng bạn sẽ không thấy nhiều tốc độ.

Giả sử nó thực sự là phí tổn của việc thực hiện quá nhiều cuộc gọi ảo có giới hạn cho những gì bạn có thể làm mà không làm cho mọi thứ không phải ảo.

Nếu cuộc gọi có đầu cho những thứ như thời gian/cờ thì tôi thường sử dụng phương pháp tiếp cận hai cấp. Việc kiểm tra được gạch chân với một cuộc gọi không phải ảo, với hành vi của lớp cụ thể chỉ được gọi nếu cần thiết.

Ví dụ:

class Foo 
{ 
public: 

inline void update(void) 
{ 
    if (can_early_out) 
    return; 

    updateImpl(); 
} 

protected: 

virtual void updateImpl(void) = 0;  
}; 
6

Thời gian đó có được thực hiện trong cuộc gọi chức năng thực tế hoặc trong chính chức năng không?

Cuộc gọi chức năng ảo chậm hơn đáng kể so với cuộc gọi không phải ảo, bởi vì cuộc gọi ảo yêu cầu thêm một tham số. (Google cho 'vtable' nếu bạn muốn đọc tất cả các chi tiết lông.)) Cập nhật: Nó chỉ ra Wikipedia article không phải là xấu về điều này. Tuy nhiên,

"Đáng chú ý" ở đây có nghĩa là một vài hướng dẫn Nếu nó chiếm một phần đáng kể trong tổng số tính toán bao gồm thời gian trong hàm được gọi, có vẻ như một nơi tuyệt vời để xem xét unvirtualizing và nội tuyến.

Nhưng trong khoảng gần 20 năm của C++, tôi không nghĩ mình từng thấy điều đó thực sự xảy ra. Tôi rất muốn xem mã.

+0

+1 để nói về toàn bộ vấn đề ở đây, bao gồm trải nghiệm của bạn. Tôi cũng gặp khó khăn khi tin rằng phí gọi là vấn đề thực sự. – dwc

6

Nếu cuộc gọi ảo thực sự là nút cổ chai, hãy thử dùng thử CRTP.

5

Xin lưu ý rằng "ảo" và "nội tuyến" không phải là đối lập - một phương pháp có thể là cả hai. Trình biên dịch sẽ hạnh phúc inline một hàm ảo nếu nó có thể xác định loại đối tượng tại thời gian biên dịch:

struct B { 
    virtual int f() { return 42; } 
}; 

struct D : public B { 
    virtual int f() { return 43; } 
}; 

int main(int argc, char **argv) { 
    B b; 
    cout << b.f() << endl; // This call will be inlined 

    D d; 
    cout << d.f() << endl; // This call will be inlined 

    B& rb = rand() ? b : d; 
    cout << rb.f() << endl; // Must use virtual dispatch (i.e. NOT inlined) 
    return 0; 
} 

[UPDATE: Made nhất định rb 's thật năng động loại đối tượng không thể được biết đến tại thời gian biên dịch - nhờ cho MSalters]

Nếu loại đối tượng có thể được xác định tại thời gian biên dịch nhưng hàm không thể inlineable (ví dụ: lớn hoặc được định nghĩa bên ngoài định nghĩa lớp), nó sẽ được gọi là không thực sự.

+0

Trong khi chính xác điều này là một chút của một tình huống contrived mà trong đời thực hầu như (haha!) Không bao giờ xảy ra - nếu nó đã làm 'f' thậm chí sẽ không cần phải được ảo trong mã trên. –

+1

B & rb = rand()? b: d; // và rõ ràng tại sao f cần phải là ảo. – MSalters

+0

@Andrew: Tôi không đồng ý - quan điểm của tôi là có thể tạo một phương thức ảo, cho phép sự linh hoạt đi cùng với điều đó, mà không phải hy sinh tốc độ có sẵn từ nội tuyến bất cứ khi nào có thể. –

1

Bạn có thể nhận được hiệu suất tốt hơn một chút từ cuộc gọi ảo bằng cách thay đổi quy ước gọi điện. Trình biên dịch Borland cũ có một quy ước __fastcall đã chuyển các đối số trong các thanh ghi cpu thay vì trên stack.

Nếu bạn bị kẹt với cuộc gọi ảo và vài thao tác đó thực sự đếm, hãy kiểm tra tài liệu biên dịch của bạn để biết các quy ước gọi điện được hỗ trợ.

Các vấn đề liên quan