2012-12-25 18 views
13

は、次のことを考えてみましょう:C++でのブール乗算?

inline unsigned int f1(const unsigned int i, const bool b) {return b ? i : 0;} 
inline unsigned int f2(const unsigned int i, const bool b) {return b*i;} 

f2の構文はf1f2は厳密に等価であることがよりコンパクトですが、標準的な保証を行いますか?

また、biがコンパイル時にわかっている場合、コンパイラにこの式を最適化したい場合、どのバージョンを好むべきですか?

+2

コンパイラを指定せずに(コンパイル時の既知の引数の場合)、コンパイラが何を行うのか分かりませんが、両方を最適化する可能性があります。コンパイル時に既知の引数をそれぞれコンパイルし、出力を検査する方法の1つです。 –

+1

生成されたアセンブリコードを見てください – James

+1

FWIW、MSVCは、ブール値そのものの評価でトリックをしないようにした場合、マスクを生成して最初のケースに追加し、2番目にケースを追加しますさらに最適化するためにboolを生成するテストが変更されます)。本質的に、MSVCはいずれの場合もブランチレスであり、素朴なケースではより最適なようです。 – JasonD

答えて

11

まあ、はい、どちらも同等です。 boolは整数型であり、trueは整数コンテキストでは1に変換され、false0に変換されることが保証されています。

(ゼロの整数値をブール値のコンテキストでfalseに変換することが保証されている間、逆もまた真である、すなわち、非ゼロの整数値は、ブール値のコンテキストでtrueに変換することが保証されている。)

あなたが作業しているので、符号なしの型で、1は、簡単に他を考え出すことができるまともなコンパイラがために、それ自体で最高の実装を選択することができるはずですが、おそらく

i & -(unsigned) b 

のように、同じものの、まだ完全にポータブルな実装をビット・ハック・ベースいずれかのバージョン。

P.S.私の驚くべきことではあるが、GCC 4.1.2は3つの変種を事実上文字通りにコンパイルした。すなわち、乗算に基づく変種でマシン乗算命令を使用した。 ?:のバリアントにcmovne命令を使用すると、ブランチレスにすることができるほどスマートだったため、最も効率的な実装になりました。

1

コンパイラは暗黙の変換を使用してunsigned intbにするので、これはうまくいくはずです。単純な乗算で条件チェックをスキップしています。どれがより効果的か速いのですか?わからない。良いコンパイラは、おそらく私が想定していた両方のバージョンを最適化するでしょう。

5

はい。これは、式の中で使用されたときあなたが保証されるよう0あるtrue1falseであると仮定しても安全です:

C++ 11、インテグラルプロモーション、4.5

bool型缶の右辺値falseが0になり、真が1になるように、int型の右辺値に変換されます。

0

FWIW、次のコード

のgcc -O2でコンパイルさ
inline unsigned int f1(const unsigned int i, const bool b) {return b ? i : 0;} 
inline unsigned int f2(const unsigned int i, const bool b) {return b*i;} 

int main() 
{ 
    volatile unsigned int i = f1(42, true); 
    volatile unsigned int j = f2(42, true); 
} 

は、このアセンブリを生成します:あなたが見ることができるよう

.file "test.cpp" 
    .def ___main; .scl 2; .type 32; .endef 
    .section .text.startup,"x" 
    .p2align 2,,3 
    .globl _main 
    .def _main; .scl 2; .type 32; .endef 
_main: 
LFB2: 
    .cfi_startproc 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    .cfi_offset 5, -8 
    movl %esp, %ebp 
    .cfi_def_cfa_register 5 
    andl $-16, %esp 
    subl $16, %esp 
    call ___main 
    movl $42, 8(%esp) // i 
    movl $42, 12(%esp) // j 
    xorl %eax, %eax 
    leave 
    .cfi_restore 5 
    .cfi_def_cfa 4, 4 
    ret 
    .cfi_endproc 
LFE2: 

f1またはf2のいずれかの多くの左は、ありません。

C++標準に関する限り、コンパイラは、観察可能な振る舞い(のようにの規則)を変更しない限り、最適化に関して何かを行うことができます。

+2

代表的な結果を得るためには、 'f1(rand()、rand())関数への予測不可能な(コンパイラによる)引数を使用するほうが良いでしょう。 – AnT