2013-05-09 39 views
8

stackoverflowers thân mến,Visual C++ ~ Không nội tuyến đơn giản con trỏ hàm const gọi

tôi nhận được một đoạn mã đơn giản mà tôi đang biên soạn trên Microsoft Visual Studio C++ 2012:

int add(int x, int y) 
{ 
    return x + y; 
} 

typedef int (*func_t)(int, int); 

class A 
{ 
public: 
    const static func_t FP; 
}; 

const func_t A::FP = &add; 

int main() 
{ 
int x = 3; 
int y = 2; 
int z = A::FP(x, y); 
return 0; 
} 

Trình biên dịch tạo ra sau mã:

int main() 
{ 
000000013FBA2430 sub   rsp,28h 
int x = 3; 
int y = 2; 
int z = A::FP(x, y); 
000000013FBA2434 mov   edx,2 
000000013FBA2439 lea   ecx,[rdx+1] 
000000013FBA243C call  qword ptr [A::FP (013FBA45C0h)] 
return 0; 
000000013FBA2442 xor   eax,eax 
} 

Tôi đã biên soạn điều này trên 'Tối ưu hóa đầy đủ' (/ Cờ xiên) và 'Bất kỳ phù hợp' để mở rộng chức năng nội tuyến. (/ Ob2 flag)

Tôi đã tự hỏi tại sao trình biên dịch không trực tiếp cuộc gọi này đặc biệt vì nó là const. Có ai trong số các bạn có một ý tưởng tại sao nó không được inline và nếu nó có thể làm cho trình biên dịch nội tuyến nó?

Christian

EDIT: Tôi đang chạy một số xét nghiệm bây giờ và MSVC không inline chức năng gợi ý quá khi:

-Tôi di chuyển con trỏ const ra khỏi lớp và làm cho nó toàn cầu.

-Tôi di chuyển con trỏ const ra khỏi lớp và đặt con trỏ vào vị trí chính.

-Tôi làm cho con trỏ không bị const và di chuyển nó cục bộ.

-Khi tôi làm cho kiểu trả về void và cho nó không có thông số

tôi loại bắt đầu tin Microsoft Visual Studio có thể dùng con trỏ hàm không inline ở tất cả ...

+0

gì sẽ gcc làm gì? –

+0

@ Otávio Décio Tôi không có trình biên dịch GCC nên tôi xin lỗi tôi không biết! –

+0

Vì bạn đang sử dụng trình biên dịch độc quyền, không chắc ai biết câu trả lời sẽ có thể cho bạn biết nếu không có hậu quả pháp lý nghiêm trọng. –

Trả lời

2

Vấn đề không phải là với nội tuyến, mà trình biên dịch thực hiện ở mọi cơ hội. Vấn đề là Visual C++ dường như không nhận ra rằng biến con trỏ thực sự là một hằng số biên dịch.

thử nghiệm hợp cụ thể:

// function_pointer_resolution.cpp : Defines the entry point for the console application. 
// 

extern void show_int(int); 

extern "C" typedef int binary_int_func(int, int); 

extern "C" binary_int_func sum; 
extern "C" binary_int_func* const sum_ptr = sum; 

inline int call(binary_int_func* binary, int a, int b) { return (*binary)(a, b); } 

template< binary_int_func* binary > 
inline int callt(int a, int b) { return (*binary)(a, b); } 

int main(void) 
{ 
    show_int(sum(1, 2)); 
    show_int(call(&sum, 3, 4)); 
    show_int(callt<&sum>(5, 6)); 
    show_int((*sum_ptr)(1, 7)); 
    show_int(call(sum_ptr, 3, 8)); 
// show_int(callt<sum_ptr>(5, 9)); 
    return 0; 
} 

// sum.cpp 
extern "C" int sum(int x, int y) 
{ 
    return x + y; 
} 

// show_int.cpp 
#include <iostream> 

void show_int(int n) 
{ 
    std::cout << n << std::endl; 
} 

Các chức năng được tách ra thành nhiều đơn vị biên soạn để cung cấp cho kiểm soát tốt hơn nội tuyến. Cụ thể, tôi không muốn show_int được gạch chân, vì nó làm cho mã lắp ráp lộn xộn.

Lỗi đầu tiên của sự cố là mã hợp lệ (dòng nhận xét) bị từ chối bởi Visual C++. G++ has no problem with it, nhưng Visual C++ than phiền "biểu thức hằng số biên dịch mong đợi". Đây thực sự là một yếu tố dự báo tốt cho mọi hành vi trong tương lai.

Với tối ưu hóa kích hoạt và ngữ nghĩa tổng hợp bình thường (không cross-mô-đun nội tuyến), trình biên dịch tạo ra:

_main PROC      ; COMDAT 

; 18 : show_int(sum(1, 2)); 

    push 2 
    push 1 
    call _sum 
    push eax 
    call [email protected]@[email protected]   ; show_int 

; 19 : show_int(call(&sum, 3, 4)); 

    push 4 
    push 3 
    call _sum 
    push eax 
    call [email protected]@[email protected]   ; show_int 

; 20 : show_int(callt<&sum>(5, 6)); 

    push 6 
    push 5 
    call _sum 
    push eax 
    call [email protected]@[email protected]   ; show_int 

; 21 : show_int((*sum_ptr)(1, 7)); 

    push 7 
    push 1 
    call DWORD PTR _sum_ptr 
    push eax 
    call [email protected]@[email protected]   ; show_int 

; 22 : show_int(call(sum_ptr, 3, 8)); 

    push 8 
    push 3 
    call DWORD PTR _sum_ptr 
    push eax 
    call [email protected]@[email protected]   ; show_int 
    add esp, 60     ; 0000003cH 

; 23 : //show_int(callt<sum_ptr>(5, 9)); 
; 24 : return 0; 

    xor eax, eax 

; 25 : } 

    ret 0 
_main ENDP 

Có đã là một sự khác biệt lớn giữa việc sử dụng sum_ptr và không sử dụng sum_ptr. Các câu lệnh sử dụng sum_ptr tạo ra một cuộc gọi hàm gián tiếp call DWORD PTR _sum_ptr trong khi tất cả các câu lệnh khác tạo ra một lệnh gọi hàm trực tiếp call _sum, ngay cả khi mã nguồn đã sử dụng một con trỏ hàm.

Nếu bây giờ chúng tôi cho phép nội tuyến bằng cách biên dịch function_pointer_resolution.cpp và sum.cpp với /GL và liên kết với /LTCG, chúng tôi thấy rằng trình biên dịch inlines tất cả các cuộc gọi trực tiếp. Cuộc gọi gián tiếp giữ nguyên trạng thái.

_main PROC      ; COMDAT 

; 18 : show_int(sum(1, 2)); 

    push 3 
    call [email protected]@[email protected]   ; show_int 

; 19 : show_int(call(&sum, 3, 4)); 

    push 7 
    call [email protected]@[email protected]   ; show_int 

; 20 : show_int(callt<&sum>(5, 6)); 

    push 11     ; 0000000bH 
    call [email protected]@[email protected]   ; show_int 

; 21 : show_int((*sum_ptr)(1, 7)); 

    push 7 
    push 1 
    call DWORD PTR _sum_ptr 
    push eax 
    call [email protected]@[email protected]   ; show_int 

; 22 : show_int(call(sum_ptr, 3, 8)); 

    push 8 
    push 3 
    call DWORD PTR _sum_ptr 
    push eax 
    call [email protected]@[email protected]   ; show_int 
    add esp, 36     ; 00000024H 

; 23 : //show_int(callt<sum_ptr>(5, 9)); 
; 24 : return 0; 

    xor eax, eax 

; 25 : } 

    ret 0 
_main ENDP 

mấu chốt: Vâng, trình biên dịch thực hiện các cuộc gọi nội tuyến thực hiện thông qua một thời gian biên dịch con trỏ hàm liên tục, chừng nào mà con trỏ hàm không được đọc từ một biến. Việc sử dụng một con trỏ hàm đã được tối ưu hóa:

call(&sum, 3, 4); 

nhưng điều này không:

(*sum_ptr)(1, 7); 

Tất cả các bài kiểm tra chạy với Visual C++ 2010 Service Pack 1, biên soạn cho x86, lưu trữ trên x64.

Microsoft (R) 32-bit C/C++ Compiler Tối ưu hóa Version 16.00.40219.01 cho 80x86

+0

Nhưng không phải là một con trỏ chức năng về mặt kỹ thuật một biến với một địa chỉ đến một hàm? Và tôi có thể kết luận rằng MSVC không thể thấy được những hằng số biên dịch được không? –

+0

@ChristianVeenman: Chắc chắn MSVC không thể thấy rằng 'sum_ptr' là hằng số biên dịch, vì nó nói nhiều nếu bạn bỏ ghi chú dòng 'callt (5, 9)' –

+0

Cảm ơn lời giải thích! Cũng cho các testruns! –

1

Bạn có thể thử __forceinline . Không ai có thể cho bạn biết chính xác lý do tại sao nó không được gạch chân. Tuy nhiên, cảm giác thông thường nói với tôi rằng nó nên được, tuy nhiên./O2 nên ưu tiên tốc độ mã trên kích thước mã (nội tuyến) ... Lạ.

+0

Tôi đã thử nghiệm nó bằng cách thêm __forceinline vào hàm add nhưng tôi vẫn nhận được lệnh gọi tương tự ở đó! Tôi cũng đã thử cả hai tùy chọn "Ủng hộ cho tốc độ" và "Ủng hộ cho kích thước nhỏ" nhưng không loại bỏ chức năng gọi! Và cảm ơn! –

+0

Nếu tồi tệ nhất đến tồi tệ nhất bạn có thể sử dụng một macro thay vì một chức năng tĩnh :) – RandyGaul

+0

heheh cảm ơn, nhưng tôi có lẽ sẽ chuyển sang GCC! (Hoặc sử dụng makefiles trong Visual Studio) –

1

Tôi nghĩ rằng bạn đã đúng trong kết luận này: "... không thể có con trỏ hàm nội tuyến chút nào".

này ví dụ rất đơn giản cũng phá vỡ tối ưu hóa:

static inline 
int add(int x, int y) 
{ 
    return x + y; 
} 

int main() 
{ 
    int x = 3; 
    int y = 2; 
    auto q = add; 
    int z = q(x, y); 
    return z; 
} 

mẫu của bạn thậm chí còn phức tạp hơn cho các trình biên dịch, vì vậy nó không phải là đáng ngạc nhiên.

+0

hehehe Fair hay không! Câu trả lời của bạn đã được chấp nhận! –

+0

Không, có một số trường hợp con trỏ hàm được tối ưu hóa. –

+0

@BenVoigt Tôi đồng ý rằng đôi khi chúng được tối ưu hóa, nhưng theo các thử nghiệm của tôi (và Camerons) chúng không bao giờ được inlined. Hay tôi bỏ lỡ một bài kiểm tra cụ thể? –

1

Đây không phải là một câu trả lời thật, nhưng một "có thể workaround" một: STL từ Microsoft một lần đã đề cập rằng lambdas dễ dàng inlineable hơn f ptrs để bạn có thể thử điều đó.

Là một đố Bjarne thường đề cập đến loại đó là nhanh hơn thatn qsort vì qsort mất chức năng ptr, nhưng gcc như những người khác đã ghi nhận không có vấn đề nội tuyến họ ... như vậy có lẽ nên thử Bjarne gcc: P

+0

Cảm ơn bạn: P Bjarne Chắc chắn nên.:P Hehe quá xấu mà Visual Studio IDE chỉ là cách tốt hơn sau đó bất kỳ một GCC. (theo ý kiến ​​của tôi: P) –

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