分岐予測の最適化に適したコードはどれですか?


10

分岐予測、およびコンパイラー最適化の影響を考えると、どのコードが優れたパフォーマンスを提供する傾向がありますか?

bRareExceptionPresentはまれな状態を表すことに注意してください。ロジックの通常のパスではありません。

/* MOST COMMON path must branch around IF clause */

bool SomeFunction(bool bRareExceptionPresent)
{
  // abort before function
  if(bRareExceptionPresent)
  {
     return false;
  }    
  .. function primary body ..    
  return true;
}

/* MOST COMMON path does NOT branch */

bool SomeFunction(bool bRareExceptionPresent)
{
  if(!bRareExceptionPresent)
  {
    .. function primary body ..
  }
  else
  {
    return false;
  }
  return true;
}

9
ここで外に出て、違いはないと言います。
Robert Harvey

7
パイプラインアーキテクチャが異なるため(遅延スロットと遅延スロットなし)、これはコンパイルする特定のCPUにおそらく依存します。これについて考えるのに費やした時間は、実行時に節約された時間よりもはるかに長い可能性があります-最初にプロファイルし、次に最適化します。

2
ほとんどの場合、時期尚早のマイクロ最適化です。
ロバートハーベイ

2
@MichaelTうん、プロファイリングは確かに、そのコンテキスト内でターゲット、プラットフォーム上のコードのパフォーマンスで実際に何が起こっているかを知る唯一の信頼できる方法です。しかし、私はどちらが一般的に好まれているのか知りたいと思いました。
dyasta 2013

1
@RobertHarvey:両方の条件が満たされた場合を除いて、時期尚早のマイクロ最適化です。(1)ループは数十億回(数百万回ではない)と呼ばれます。(2)皮肉なことに、ループ本体がマシンコードの点で小さい場合。条件#2は、オーバーヘッドに費やされた時間の割合が、有用な作業に費やされた時間と比較して重要でないことを意味します。良いニュースは、通常、両方の条件が満たされるような状況では、本来ブランチレスであるSIMD(ベクトル化)がすべてのパフォーマンスの問題を解決することです。
rwong 2013

回答:


10

今日の世界では、たとえそれがあったとしても、それほど重要ではありません。

動的分岐予測(何十年もの間考えられていたもの(1996年に公開された動的分岐予測Schemesonシステムワークロードの分析を参照))はかなり一般的な場所です。

この例は、ARMプロセッサにあります。分岐予測に関するArm Info Centerから

分岐予測の精度を向上させるために、静的技法と動的技法の組み合わせが採用されています。

問題は、「armプロセッサでの動的分岐予測とは何か」です。動的分岐予測の続きを読むと、2ビット予測スキーム(論文で説明)を使用して、分岐が強くまたは弱く行われるか、行われないかに関する情報が作成されることが示されています。

時間が経つと(時間によっては、そのブロックを数回通過することを意味します)、これにより、コードがどのように進むかに関する情報が蓄積されます。

以下のために静的予測、それはコード自体を見て、どの方法分岐がテストに行われている方法を見て-前の命令またはコードでさらに1に:

ARM1136JF-Sプロセッサで使用されているスキームは、すべての順方向条件分岐が行われず、すべての逆方向分岐が行われることを予測しています。すべての分岐の約65%の前には、完全に予測されるのに十分な非分岐サイクルがあります。

Sparkyが述べたように、これはループがループであることが多いという理解に基づいています。ループは逆方向に分岐します(ループの最後に分岐があり、先頭から再開します)-通常はこれを行います。

コンパイラを推測しようとする危険は、そのコードが実際にコンパイル(および最適化)される方法がわからないことです。そして、ほとんどの場合、それは問題ではありません。動的予測を使用すると、関数全体で2回実行され、早期復帰のためのガードステートメントのスキップを予測します。2つのフラッシュされたパイプラインのパフォーマンスが重要なパフォーマンスである場合、他に注意すべき点があります。

あるスタイルを他のスタイルよりも読み取るのにかかる時間は、おそらく非常に重要です。コードをクリーンにして人間が読み取れるようにするのは、コードがいかに厄介または理想的であるかに関係なく、コンパイラーが問題なく実行できるためです。


7
有名なスタックオーバーフローの質問は、今日でも分岐予測重要であることを示しました。
Florian Margaine 2013

3
@FlorianMargaineは重要ですが、本当に重要な状況に入るには、コンパイル先とその動作方法(arm vs x86 vs mips ...)を理解する必要があるようです。最初にこのマイクロ最適化を実行しようとするコードを記述することは、誤った前提で機能している可能性が高く、望ましい効果を達成できません。

もちろん、DKを引用しないでください。しかし、この質問は、プロファイリングの段階をすでに過ぎたとき、明らかに最適化という意味であったと思います。:-)
Florian Margaine 2013

2
@MichaelTいい答えです。あなたの結論に非常に同意します。この種の事前プロファイリング/抽象的最適化は、明らかに逆効果になります。それは推測ゲームになり、不合理な理由で設計の決定をすることになります。それでも、気になった; o
dyasta 2013


9

私の理解では、CPUが最初に分岐に遭遇したときに、前方の分岐は行われず、後方の分岐は行われることが予測されます(サポートされている場合)。この根拠は、ループ(通常、後方に分岐する)が取得されると想定されていることです。

一部のプロセッサでは、どちらのパスがより可能性が高いかについて、アセンブリ命令にヒントを与えることができます。この詳細は今のところ私から逃れています。

さらに、一部のCコンパイラは静的分岐予測もサポートしているため、コンパイラに分岐の可能性が高くなります。次に、生成されたコードを再編成するか、変更された命令を使用してこの情報を利用します(または単に無視して無視します)。

__builtin_expect((long)!!(x), 1L)  /* GNU C to indicate that <x> will likely be TRUE */
__builtin_expect((long)!!(x), 0L)  /* GNU C to indicate that <x> will likely be FALSE */

お役に立てれば。


3
「私の理解では、CPUが初めて分岐に遭遇したときに、前方の分岐は行われず、後方の分岐は行われることが予測されます(サポートされている場合)。」これは非常に興味深い考えです。これが実際に一般的なアーキテクチャで実装されているという証拠はありますか?
blubb 2013

5
馬の口からまっすぐ:前の枝はデフォルトで取られません。後方分岐はデフォルトで採用されます。また、同じページから:「プレフィックス0x3E –分岐が行われると静的に予測する」。
MSalters 2013

同等のプラットフォームにとらわれないプラグマはあり__builtin_expectますか?
MarcusJ 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.