2017-01-29 5 views
10

私はの一部のみが極めて低い発現を有するが、他のは、統計学的に中性であると仮定)(/)(おそらくそうとして表現の一部のみをマークすることです:は、それが合理的な

if (could_be || very_improbable) { 
    DoSomething(); 
} 

う私はunlikely()マクロに非常に不可能なビットを置くなら、それはどのような方法でコンパイラを助ける?

if (could_be || unlikely(very_improbable)) { 
    DoSomething(); 
} 

注:私はマルコスの仕組みを尋ねません - 私はそれを理解しています。ここでの質問はGCCに関するものであり、その一部についてしかわからないのであれば、式を最適化することができます。私はそれが問題の式に大きく依存していることを認識しています。これらのマクロを経験した人には魅力的です。

+0

GCC-5.4には、「-Wall -O2 -march = x86-64 -mtune = generic -S'、一般的なAMD64/x86-64アーキテクチャのアセンブリを生成するには) ';生成されるコードに違いはありません。 –

+1

Linuxカーネルの[likely()/ unlikely()マクロと重複している可能性があります。何が彼らの利益ですか?](http://stackoverflow.com/questions/109710/likely-unlikely-macros-in-the-linux-kernel-how-do-the-work-whats-their) –

+0

@NominalAnimalあなたはどういう意味ですか?コードスニペットを作成してコンパイルしたことはありますか? – immortal

答えて

6

はい、それは合理的であり、コンパイラ適切なシナリオでそれを活用することができます。あなたの実際の例では

could_bevery_improbableが実際に不可欠な変数である場合、コンパイラは本当にについて何を行うことができますので、述語の部分式にlikelyまたはunlikelyマクロを挿入する任意のポイントであることをそこに行くされていませんこれをより速くする?コンパイラは、ブランチの結果に応じてifブロックを別々に整理できますが、very_improbablyが役に立たないと思われるだけで、テストするコードを生成する必要があります。

のは、コンパイラが、より多くの作業を行うことができます例を見てみましょう:ここ

extern int fn1(); 
extern int fn2(); 
extern int f(int x); 

int test_likely(int a, int b) { 
    if (likely(f(a)) && unlikely(f(b))) 
    return fn1(); 
    return fn2(); 
} 

述語は引数付きf()への2回の呼び出しで構成され、icclikelyの4つの組み合わせの3のために異なるコードが生成されますそしてunlikelylikely(f(a)) && likely(f(b))ため

コードproduced

test_likely(int, int): 
     push  r15           #8.31 
     mov  r15d, esi          #8.31 
     call  f(int)           #9.7 
     test  eax, eax          #9.7 
     je  ..B1.7  # Prob 5%      #9.7 
     mov  edi, r15d          #9.23 
     call  f(int)           #9.23 
     test  eax, eax          #9.23 
     je  ..B1.7  # Prob 5%      #9.23 
     pop  r15           #10.12 
     jmp  fn1()          #10.12 
..B1.7:       # Preds ..B1.4 ..B1.2 
     pop  r15           #11.10 
     jmp  fn2()          #11.10 

ここでは、両方の述部が真である可能性が高いため、iccは、両方が真である場合に直線コードを生成し、いずれかが偽であると判断された場合は飛び越します。 unlikely(f(a)) && likely(f(b))ため

コードproduced

test_likely(int, int): 
     push  r15           #8.31 
     mov  r15d, esi          #8.31 
     call  f(int)           #9.7 
     test  eax, eax          #9.7 
     jne  ..B1.5  # Prob 5%      #9.7 
..B1.3:       # Preds ..B1.6 ..B1.2 
     pop  r15           #11.10 
     jmp  fn2()          #11.10 
..B1.5:       # Preds ..B1.2 
     mov  edi, r15d          #9.25 
     call  f(int)           #9.25 
     test  eax, eax          #9.25 
     je  ..B1.3  # Prob 5%      #9.25 
     pop  r15           #10.12 
     jmp  fn1()          #10.12 

さて、述語はおそらく偽であるので、iccは、その場合のリターンに直結する定額コードを生成し、へB1.5にラインの外にジャンプします述部を続行します。この場合、2番目のコール(f(b))がtrueになると予想され、tail-callfn1()で終了するフォールスルーコードが生成されます。 2番目の呼び出しがfalseになると、最初のジャンプ(ラベルB1.3)でfall-thoughの場合に既にアセンブルされている同じシーケンスにジャンプします。

これはunlikely(f(a)) && unlikely(f(b))ために生成されたコードであることもが判明しました。この場合、コンパイラがコードの末尾を変更してjmp fn2()をフォールスルーケースにすると想像できますが、そうではありません。これにより、以前のシーケンスをB1.3で再利用することができなくなることがわかりました。でもこのコードを実行している可能性は低いので、すでにあるとは限りません。 likely(f(a)) && unlikely(f(b))ため

コードproducedは:

test_likely(int, int): 
     push  r15           #8.31 
     mov  r15d, esi          #8.31 
     call  f(int)           #9.7 
     test  eax, eax          #9.7 
     je  ..B1.5  # Prob 5%      #9.7 
     mov  edi, r15d          #9.23 
     call  f(int)           #9.23 
     test  eax, eax          #9.23 
     jne  ..B1.7  # Prob 5%      #9.23 
..B1.5:       # Preds ..B1.4 ..B1.2 
     pop  r15           #11.10 
     jmp  fn2()          #11.10 
..B1.7:       # Preds ..B1.4 
     pop  r15           #10.12 
     jmp  fn1()          #10.12 

これは、そのようにブロックを、注文し直すように、第二述語に対する期待が現在偽であることを除いて、最初のケース(likely && likely)に類似していますreturn fn2()のケースは、フォールスルーケースです。

ので、コンパイラは間違いなく正確likelyunlikely情報を使用することができ、そして実際にそれが理にかなって:あなたは二つに上記のテストを解散if文を連鎖している場合、それは驚くべきことではないので、それは、別々の分岐ヒントがうまくいくことはかなり明白です意味的に同等の&&の使用には依然としてヒントがあります。ここで

は、あなたがここまでなった場合には、「全文」の治療を取得していないいくつかの他の注意事項です:私は例を説明するために iccを使用

  • を、このテストのために、少なくとも両方clanggccは同じ基本的な最適化を行います(4つのケースのうち3つを別々にコンパイルします)。
  • コンパイラがサブ述部の確率を知ることによって作ることができる1つの「明白な」最適化は、述語の順序を逆にすることです。たとえば、likely(X) && unlikely(Y)がある場合は、Yの状態を最初に確認できます。これは、ショート・カット・チェックY を許可する可能性が高いためです。どうやら、単純な述語ではgccはmake this optimizationになりますが、これを行うにはiccまたはclangを同軸化できませんでした。 gccの最適化は明らかに非常に脆弱です。つまり、の方がはるかに良いとなるにもかかわらず、述語を少し変更するとdisappearsになります。
  • コンパイラは、変換されたコードが、言語の意味に従って直接コンパイルされたかのように動作することを保証できない場合、最適化を行うことが制限されています。特に、操作に副作用がないことを証明できない限り、操作の順序を変更する機能は制限されています。述語を構造化するときは、そのことを覚えておいてください。もちろん

、コンパイラはXYは副作用がないことが確認できたときに、これはを許可されているだけ、およびYがはるかに高価にある場合には、効果ではないかもしれません(Xの評価の追加費用が高いため、Yへのチェックを避けることのいずれかの利点が圧倒されるため)。

+0

包括的な回答ありがとうございます! すべてを丸ごと丸める - 最適化フラグはどのように使用しましたか?コンパイラを得るためにどのような最適化フラグを使用しましたか(私はGCCで作業します) O2が明らかになると思いますが、O3はまだそれを行うと思いますか? – immortal

-1

はい、Cはインテリジェントまたはガーディングを使用します。したがって、我々が書くならば、

if(a() || b()) 

が評価されます。 trueの場合、bは評価されません。 falseの場合は、最終的な決定を行うために評価される必要があります。

したがって、a()が安価であると思われる場合、b()は高価であるか不可能である場合は、a()を先に入力します。しかし、コードは、制御フローたら

if(a()) 
    { 
     do_it(); 
    } 
    /* action point */ 
    else if(b()) 
    { 
    do_it(); 
    } 

にequaivalentであるあなたはジャンプが可能性があるかどうかをコンパイラに伝えるかが助けることを見る「action_point」に達します。

+2

「短絡」は、「インテリジェントまたはガーディング」の適切な用語です。 – Downvoter

+0

私は 'a()'と 'b()'は副作用を持たないことにも言及したいと思います。現在は、 '__builtin_expect'アノテーションに関係なく、一般的に並べ替えることのできない関数呼び出しのように見えます。 – yugr

+2

これは別の質問に答えるものです。短絡オペレータの条件を最適にするにはどうすればよいのでしょうか?また、私が理解しているように、「可能」または「そう思わない」とは直接関係しません。 – BeeOnRope

2

はい、このです。たとえば、次の例では、XXXがオフの場合、GCCは の前にの予期しないの前にテストするので、実行時にコードが実行されることはありません(Cygwin、gcc 5.4)。もちろん、この特定のケースではyのチェックはxの前に書かれていますが、これはcodegenの間にコードがGCCによって予期しない方法で再シャッフルされる可能性があるため重要ではありません。

#ifdef XXX 
#define __builtin_expect(x, y) (x) 
#endif 

void bar(); 

void foo(int x, int y, int z) { 
    if(__builtin_expect(y > 0, 0) || x == 0) 
    bar(); 
} 

XXXがオフになっている(すなわち、__builtin_expectがアクティブである):

foo: 
    testl %ecx, %ecx 
    je  .L4 
    testl %edx, %edx 
    jg  .L4 
    rep ret 

XXX(すなわち__builtin_expectは無視)に定義されている場合:

foo: 
    testl %edx, %edx 
    jg  .L4 
    testl %ecx, %ecx 
    je  .L4 
    rep ret 
+0

良いキャッチ。 gccが実際に小切手を並べ替えるケースは見つけられませんでしたが、明らかにこれが1つです。 – BeeOnRope

関連する問題