77

明確にするために、ここでは移植性を求めているわけではないので、特定のボックスに結び付けるソリューションは問題ありません。

基本的に、99% の確率で true と評価される if ステートメントがあり、最後の 1 クロックのパフォーマンスを引き出しようとしています。何らかのコンパイラ コマンドを発行できますか (GCC 4.1.2 と x86 ISA を使用する場合、重要) 分岐予測子に、その分岐をキャッシュする必要があることを伝えますか?

4

7 に答える 7

77

はい、効果はありません。例外は、Netburst より前の古い (廃止された) アーキテクチャであり、それでも測定可能なことは何もしません。

Intel が Netburst アーキテクチャで導入した「分岐ヒント」オペコードと、一部の古いアーキテクチャでのコールド ジャンプ (後方予測は実行され、前方予測は実行されない) のデフォルトの静的分岐予測があります。GCC はこれを で実装し__builtin_expect (x, prediction)ます。ここで、予測は通常 0 または 1 です。コンパイラによって発行されたオペコードは、すべての新しいプロセッサ アーキテクチャ (>= Core 2) では無視されます。これが実際に何かを行う小さなコーナー ケースは、古い Netburst アーキテクチャでのコールド ジャンプのケースです。Intel は現在、静的分岐ヒントを使用しないことを推奨しています。これはおそらく、コード サイズの増加が限界速度向上の可能性よりも有害であると考えているためです。

予測子の役に立たない分岐ヒントに加えて__builtin_expect、コンパイラはコードを並べ替えて、キャッシュの使用を改善したり、メモリを節約したりする場合があります。

期待どおりに動作しない理由は複数あります。

  • プロセッサは小さなループ (n<64) を完全に予測できます。
  • プロセッサは、小さな繰り返しパターン (n~7) を完全に予測できます。
  • プロセッサー自体は、コンパイル時にコンパイラー/プログラマーよりも実行時に分岐の可能性を見積もることができます。
  • 分岐の予測可能性(= 分岐が正しく予測される確率) は、分岐が行われる確率よりもはるかに重要です。残念ながら、これはアーキテクチャに大きく依存しており、分岐の予測可能性を予測することは非常に難しいことで知られています。

Agner Fogs manualsで、分岐予測の内部作業について詳しく読んでください。gccメーリング リストも参照してください。

于 2009-12-05T11:09:44.280 に答える
61

はい。http://kerneltrap.org/node/4705

これ__builtin_expectは、gcc(バージョン> = 2.96)がプログラマーに分岐予測情報をコンパイラーに示すために提供するメソッドです。の戻り値 __builtin_expectは、渡される最初の引数(整数のみである可能性があります)です。

if (__builtin_expect (x, 0))
                foo ();

     [This] would indicate that we do not expect to call `foo', since we
     expect `x' to be zero. 
于 2009-12-05T06:12:25.810 に答える
34

Pentium 4 (別名 Netburst マイクロアーキテクチャ) には、jcc 命令のプレフィックスとして分岐予測ヒントがありましたが、P4 だけがそれらを使って何かを行いました。http://ref.x86asm.net/geek32.htmlを参照してください。また 、 http://www.agner.org/optimize/にある Agner Fog の優れた asm opt ガイド のセクション 3.5 も参照してください。彼は、C++ での最適化のガイドも持っています。

以前とそれ以降の x86 CPU は、これらのプレフィックス バイトを黙って無視します。 可能性が高い/可能性が低いヒントの使用に関するパフォーマンス テストの結果はありますか? PowerPCには、エンコーディングの一部として分岐予測ヒントを持ついくつかのジャンプ命令があると述べています。これは非常にまれな建築上の特徴です。コンパイル時に分岐を静的に予測することは、正確に行うのが非常に難しいため、通常はハードウェアに任せて判断することをお勧めします。

最新の Intel および AMD CPU の分岐予測子と分岐先バッファがどのように動作するかについて、公式に公開されているものはあまり多くありません。最適化マニュアル (AMD および Intel の Web サイトで簡単に見つけることができます) には、いくつかのアドバイスがありますが、特定の動作については文書化されていません。Core2 の BTB エントリの数など、実装を推測するためにテストを実行した人もいます... とにかく、予測子を明示的に示唆するという考えは (今のところ) 放棄されています。

文書化されているのは、たとえば、Core2 には分岐履歴バッファーがあり、ループが常に一定の短い反復回数 (8 または 16 IIRC 未満) を実行する場合に、ループ出口の予測ミスを回避できることです。ただし、64 バイト (Penryn では 19uops) に収まるループは、バッファーから再生されるため、命令フェッチのボトルネックが発生しないため、すぐに展開しないでください。Agner Fog の PDF を読んでください

Intel がここ数年で静的分岐予測メカニズムを変更した理由も参照してください。: Intel 以来、Sandybridge は静的予測をまったく使用していません。これは、CPU の動作をリバース エンジニアリングしようとするパフォーマンス実験からわかる限りです。(多くの古い CPU では、動的予測が失敗した場合のフォールバックとして静的予測があります。通常の静的予測では、前方分岐は行われず、後方分岐が行われます (後方分岐はループ分岐であることが多いため)。


likely()GNU C を使用した/unlikely()マクロの効果__builtin_expect(Drakosha の回答の言及など) は、BP ヒントを asm に直接挿入しませ。(それはおそらくそうするかもしれませんがgcc -march=pentium4、他の何かのためにコンパイルするときはそうではありません)。

実際の効果は、高速パスで分岐が少なくなり、おそらく合計命令数が少なくなるようにコードをレイアウトすることです。これは、静的予測が機能する場合の分岐予測に役立ちます (たとえば、静的予測にフォールバックする CPU では動的予測はコールドです)。

if else ステートメントでの GCC の __builtin_expect の利点は何ですか? を参照してください。code-gen の特定の例については。

完全に予測された場合でも、採用されたブランチは、採用されていないブランチよりもわずかに多くのコストがかかります。CPU が 16 バイトのチャンクでコードをフェッチして並列にデコードする場合、分岐が行われるということは、そのフェッチ ブロック内の後続の命令が実行される命令ストリームの一部ではないことを意味します。フロントエンドでバブルが発生し、これが高スループット コードのボトルネックになる可能性があります (キャッシュ ミス時にバックエンドで失速せず、命令レベルの並列性が高い)。

異なるブロック間をジャンプすると、より多くのキャッシュラインのコードに触れる可能性があり、L1i キャッシュのフットプリントが増加し、低温の場合は命令キャッシュ ミスが増える可能性があります。(そして潜在的に uop-cache フットプリント)。これは、ファスト パスを短く直線的にすることのもう 1 つの利点です。


GCC のプロファイルに基づく最適化により、通常、可能性が高い/可能性が低いマクロは不要になります。コンパイラは、コード レイアウトの決定のために各分岐がどのように移動したかに関するランタイム データを収集し、ホット ブロック/関数とコールド ブロック/関数を識別します。(たとえば、ホット関数ではループを展開しますが、コールド関数では展開しません。) GCC マニュアルの-fprofile-generateおよびを参照してください。 g ++でプロファイルガイド付き最適化を使用するには?-fprofile-use

それ以外の場合、可能性が高い/可能性が低いマクロを使用せず、PGO を使用しなかった場合、GCC はさまざまなヒューリスティックを使用して推測する必要があります。 -fguess-branch-probability以上でデフォルトで有効になります-O1

https://www.phoronix.com/scan.php?page=article&item=gcc-82-pgo&num=1には、Xeon Scalable Server CPU で gcc8.2 を使用した PGO とレギュラーのベンチマーク結果があります。(Skylake-AVX512)。すべてのベンチマークで少なくともわずかな速度向上が得られ、一部のベンチマークでは最大 10% の速度向上が見られました。(そのほとんどは、おそらくホット ループでのループ展開によるものですが、その一部は、おそらくブランチ レイアウトの改善やその他の効果によるものです。)

于 2009-12-05T07:21:09.587 に答える
1

SUN C Studioには、この場合に定義されたいくつかのプラグマがあります。

#pragma rarely_called()

これは、条件式の一部が関数呼び出しであるか、関数呼び出しで始まる場合に機能します。

しかし、一般的なif/whileステートメントにタグを付ける方法はありません

于 2009-12-05T06:18:10.650 に答える
-10

いいえ、分岐予測子に通知するアセンブリコマンドがないためです。心配しないでください。分岐予測はかなり賢いです。

また、時期尚早の最適化とそれがどのように悪であるかについての義務的なコメント。

編集:ドラコシャはGCCのいくつかのマクロについて言及しました。ただし、これはコードの最適化であり、実際には分岐予測とは関係がないと思います。

于 2009-12-05T06:12:35.170 に答える
-11

これは私にはやり過ぎのように聞こえます-このタイプの最適化はわずかな時間を節約します。たとえば、より新しいバージョンのgccを使用すると、最適化にはるかに大きな影響を及ぼします。また、さまざまな最適化フラグをすべて有効または無効にしてみてください。すべてがパフォーマンスを向上させるわけではありません。

基本的に、これが他の多くの実りある道と比較して大きな違いを生む可能性は非常に低いようです。

編集:コメントをありがとう。私はこのコミュニティウィキを作成しましたが、他の人がコメントを見ることができるように残しました。

于 2009-12-05T06:13:38.833 に答える