2009-03-01 25 views
3

私はこのようなコードを持っている、と私は読んで、それは少し難しい見つける:C/C++コンパイラはこのif文を最適化しますか?

// code1 
if((expensiveOperation1() && otherOperation() && foo()) 
    || (expensiveOperation2() && bar() && baz()) { 
    // do something 
} 

私はちょうどそれを読みやすくするために、次のようにそれを変更:

// code2 
const bool expr1 = expensiveOperation1() && otherOperation() && foo(); 
const bool expr2 = expensiveOperation2() && bar() && baz(); 
if(expr1 || expr2){ 
    // one of the conditions met 
} 

しかし、私が今すべき効率を気にする?

つまり、最初の結言句が満たされている場合は、文が真となることが既に明らかになっているので、2番目の結び付き句が満たされていれば、2番目の結び付き句を調べることさえ気にしません。

私のより読みやすい例では、cond1cond2の両方を計算する必要があります。または、コンパイラは、expr2が他の場所で使用されていない場合、code2code1に変更するほどスマートになるでしょうか?

+0

私はgcc/g ++(正確にはg ++ v3.4)を使用しています。 – Frank

答えて

24

いずれかの関数に副作用があると論理的には同等ではないので、私はそうしてはいけないと言います。

次は、しかし同等になり、それはコードより自己文書を作り、あなたがテスト関数に説明的な名前を与えることを許可する利点を持っていると思います:

// code3 
inline bool combinedOp1() 
{ 
    return expensiveOperation1() && otherOperation() && foo(); 
} 

inline bool combinedOp2() 
{ 
    return expensiveOperation2() && bar() && baz(); 
} 

そして、次のようにそれを呼び出します:

if (combinedOp1() || combinedOp2()) 
{ 
    // do something 
} 
+0

遅延評価のため、副作用がない方がよいでしょう。 – tvanfosson

+0

+1私から。私はそれを考えていたはずです! ;-) – Frank

+0

合意して、これを最適化すれば驚くだろう。もちろん、*副作用はないはずですが、コンパイラはそのことを知らないでしょう。それは最悪を想定しなければならない。もしあなたのexpensiveOperationの定義が見えるなら、それはoptmizeするかもしれませんが、安全な賭けではありません。私はこの解決策が好きです。 – jalf

19

おそらく、2番目の小切手に最初の小切手を組み込むのではないでしょうか?

// code3 
bool expr = expensiveOperation1() && otherOperation() && foo(); 
expr = expr || (expensiveOperation2() && bar() && baz()); 
if(expr){ 
    // one of the conditions met 
} 

さらに、高価な操作を完全にスキップする遅延評価を利用して、最も費用のかからないチェックが各リスト内で最初に行われるようにします。

+0

ニース - 何百万年もの間、私のためにおそらく思いつかなかった単純なものの1つ。 –

+0

2番目の行がコンパイルされないように、exprがconstと宣言されていることを除いて、ニース。 "const bool expr1 = ...; const bool expr2 = expr1 ||(...)"を意味すると思います。 – SCFrench

+0

Cut /貼り付けエラー。私はそれをconstにして再利用するのではないでしょう。 – tvanfosson

4

まあ、一般的に、コンパイラは、条件が副作用を持っていることをオフのチャンスに& &年代と|| 'sの順序を変更しません。いくつかの非常にスマートなコンパイラは静的に独立性を検証できるかもしれませんが、これはまれです。

可能であれば、安価な操作の条件を並べ替えることで、高価なものを短絡させることができます。

1

この質問に対する答えはもちろんコンパイラによって異なります。決定的な方法は、コンパイラによって生成されたアセンブリをこの関数用に調べることです。ほとんどの(すべて?)コンパイラはこれを行う方法を持っています。たとえばgccには-Sというオプションがあります。いくつかの奇妙な理由で、ほとんどのデバッガが関数の逆アセンブリを表示できない場合や、これを行うための他のツールがある場合。

0

私は、コードを並べ替えるために最適化を非常に積極的にするのはコンパイラが気に入らないと付け加えるだけです。

私はちょうどコンパイラがそれが何を言ったかをしたい。

私の能力を上回ることができれば、それはまた自分自身よりも優れている可能性があります。

2

ここでのトップの回答は、「はず」と「たぶん」という質問に答えています。それは決まった答えではありません!

コンパイラがこの小さなコードを最適化しているかどうかを知りたい場合は、 "show assembly output"フラグを使用してコードをコンパイルします。そのフラグは "-S"です。出力アセンブリを見ると、コンパイルされているかどうかを正確に100%表示します。

次に、コードスニペットにスナップされた最初のコードを "therefromhere"から比較して、コンパイラが最適な(つまり最小サイクル)最適化を見つけるまで、多数のコード変更をすばやく試してみることができます。

asm出力を見るのは複雑で怖いですが、実際には5分ほどかかります。 What is the fastest way to swap values in C?

+0

-1これは、次のコンパイラの更新で壊れてしまう移植性のないコードを生成するための素晴らしい方法です。言語が保証するものを理解しようと常にしてください。コンパイラの実装の詳細を分析し始めると、その動作が保証のうちの1つを破るように見える場合に限ります。 –

+1

コンパイラの出力asmを見ている間、あなたのCコードを確実に移植できないようにしておいたことは、いつかお勧めしました。 私の文章を「あなたのコードを移植性のないものにする」ことを誤解するのは本当に難しいことです。 愚か者だけが故意にCコードを移植できないようにします。大きな愚か者は意図的にCコードを考えずに移植することはありません。 –

0

cond2(expensiveOperation2()、bar()およびbaz()の関数が純粋な(つまり副作用がない)ことを知っている場合、コンパイラは最適化できます。それらが純粋であれば、コンパイラがインライン関数にするのが最も簡単な方法です。

可能ですコンパイラはあなたがいなくてもそれを伝えることができますが、expensiveOperation2()はおそらく非常に多くの作業をします。

FWIWこれらの関数が純粋な場合は、bar()およびbaz()がexpensiveOperation2()より先に実行されるように並べ替える必要があります(cond1の順序も同じです)。

関連する問題