2013-05-09 6 views
8

親愛なるStackOverflowersを呼び出していない、のVisual C++〜簡単なのconst関数ポインタをインライン化することは

私はマイクロソフトの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; 
} 

をコード:

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 
} 

「完全最適化」(/ Obxフラグ)と「インライン関数展開のための任意の適切」でコンパイルしました。 (/ Ob2フラグ)

私は、コンパイラがなぜこのコールをインラインにしないのかと疑問に思っていました。あなたがインライン化されていない理由と、インラインでコンパイラを作成することが可能なのかどうかはあなたの誰もが知っていますか?

クリスチャン

編集:私は今、いくつかのテストを実行していますし、MSVCはあまりにも時に関数ポインタをインライン化に失敗:

は、クラスの外のconstポインタを移動し、それがグローバルに-I。

-I constポインタをクラスから移動し、メインでローカルにします。

-Iポインタを非constにしてローカルに移動します。

-When私は戻り値の型がvoidにし、それにはパラメータ

を与えていない私は親切で、すべてのMicrosoftのVisual Studioできませんインライン関数ポインタを信じ始める...

+0

何だろうgccはどうですか? –

+0

@OtávioDécio私はGCCコンパイラを持っていないので、わかりません。 –

+0

あなたは独自のコンパイラを使用しているので、答えを知っている人なら誰でも致命的な法的結果なしにあなたに伝えることはできません。 –

答えて

2

コンパイラがあらゆる機会に行うインライン化では問題はありません。問題は、Visual C++ではポインタ変数が実際にコンパイル時定数であることを認識していないように見えることです。

テストケース:

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

機能がインラインより良好な制御を与えるために複数のコンパイル単位に分離されます。具体的には、show_intをインライン化したくないのは、アセンブリコードが乱雑になるからです。

問題の最初の悩みは、有効なコード(コメント行)がVisual C++によって拒否されることです。 G++ has no problem with itですが、Visual C++では「期待されるコンパイル時定数式」が返されます。これは、実際にはすべての将来の行動の良い予測子です。最適化が有効と通常のコンパイルセマンティクス(ノークロスモジュールのインライン化)で

、コンパイラが生成します。

_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 

sum_ptrを使用してsum_ptrしていないを使用しての間には大きな違いがすでにあります。 sum_ptrを使用しているステートメントは、間接的な関数呼び出しcall DWORD PTR _sum_ptrを生成しますが、他のすべてのステートメントは、ソースコードが関数ポインタを使用している場合でも、直接関数呼び出しcall _sumを生成します。

function_pointer_resolution.cppとsum.cppを/GLでコンパイルし、/LTCGでリンクしてインライン化を有効にすると、コンパイラはすべての直接呼び出しをインライン化することがわかります。間接通話は現状のままです。

_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 

ボトムライン:はい、コンパイラは限りその関数ポインタを変数から読まれていないとして、コンパイル時定数関数ポインタを介して行わインライン呼び出しを行います。

call(&sum, 3, 4); 

が、これはしなかった:関数ポインタのこの使用が最適化されてしまった

(*sum_ptr)(1, 7); 

すべてのテストは、x64上でホストされているx86の、用にコンパイルは、Visual C++ 2010のService Pack 1で実行されます。

のMicrosoft(R)32ビットのC/C++最適化コンパイラバージョン16.00.40219.01 80×86

+0

しかし、関数ポインタは技術的に関数へのアドレスを持つ変数ではありませんか?そして、私はMSVCがそれらがコンパイル時定数であることを見ることができないと結論付けることができますか? –

+0

@ChristianVeenman:間違いなく、 'call_(5,9) '行のコメントを外すと、MSVCは' sum_ptr'がコンパイル時定数であることを知ることができません。 –

+0

説明をありがとう!また、テストランのために! –

1

あなたは__forceinlineを試すことができます。なぜそれがインライン化されていないのかを誰にも正確に伝えることはできません。しかし、常識によれば、そうすべきだと私は言います。/O2は、コードサイズ(インライン展開)よりもコードスピードを優先する必要があります... Strange。

+0

add関数で__forceinlineを追加してテストしましたが、まだ同じ呼び出し命令があります!私も両方のオプション "速度のための賛成"と "小さなサイズのための賛成"を試みたが、どちらも呼び出し関数を削除していない!ありがとう! –

+0

最悪の場合は、静的関数の代わりにマクロを使うことができます:) – RandyGaul

+0

hehehありがとうございます。しかし、私はおそらくGCCに移動します! (または、Visual Studio内でmakefileを使用してください) –

1

あなたはこの結論で正しいと思います。「...関数ポインタをまったくインラインできません」

この非常に簡単な例では、最適化を破る:

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

あなたのサンプルは、より複雑なコンパイラのためであるので、それは驚くべきことではありません。

+0

heheheフェアかどうか!あなたの答えは受け入れられました! –

+0

いいえ、関数ポインタが最適化されるケースがいくつかあります。 –

+0

@BenVoigt私は時には最適化されることに同意しますが、私のテスト(およびキャメロンズ)によれば、彼らはインラインになることはありません。または、私は特定のテストを欠場するのですか? –

1

のためにこれが本当の答えが、 "多分回避策" ものではありません。マイクロソフトから STL一度lambdaはf ptrsよりも簡単にインライン化できるので、これを試すことができます。 qsort関数のPTRがかかりますが、他の人が指摘しているようにgccがそれらをインライン化問題がないので、Bjarneは、多くの場合、その種に言及しているトリビアとして

は...ので、多分Bjarne gccを試してみてくださいqsortはthatn速いです:P

+0

ありがとう!:P Bjarne確かにshould ::P Visual Studio IDEがGCCよりも優れていることは間違っています。 (私の意見では:P) –

関連する問題