411

Linuxカーネルのいくつかの部分を調べてみたところ、次のような呼び出しが見つかりました。

if (unlikely(fd < 0))
{
    /* Do something */
}

また

if (likely(!err))
{
    /* Do something */
}

私はそれらの定義を見つけました:

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

それらが最適化のためのものであることは知っていますが、どのように機能しますか?そして、それらを使用することで、どのくらいのパフォーマンス/サイズの低下が期待できますか?そして、少なくともボトルネックコード(もちろんユーザースペース)では、面倒な(そしておそらく移植性を失う)価値があります。

4

10 に答える 10

381

これらは、分岐予測がジャンプ命令の「可能性の高い」側を優先するようにする命令を発行するようにコンパイラーにヒントを与えます。これは大きな勝利になる可能性があります。予測が正しければ、ジャンプ命令は基本的に無料で、ゼロサイクルしかかかりません。一方、予測が間違っている場合は、プロセッサパイプラインをフラッシュする必要があり、数サイクルかかる可能性があることを意味します。ほとんどの場合、予測が正しい限り、これはパフォーマンスに良い傾向があります。

このようなすべてのパフォーマンスの最適化と同様に、コードが実際にボトルネックにあり、おそらくミクロの性質を考慮して、タイトなループで実行されていることを確認するために、広範なプロファイリングの後にのみ実行する必要があります。一般的に、Linux開発者はかなり経験豊富なので、そうしていたと思います。彼らはgccのみを対象としているため、移植性についてはあまり気にしません。また、生成したいアセンブリについて非常に綿密な考えを持っています。

于 2008-09-20T23:09:26.847 に答える
79

これらは、分岐がどの方向に進むかについてコンパイラーにヒントを与えるマクロです。マクロが利用可能な場合は、GCC固有の拡張機能に拡張されます。

GCCはこれらを使用して、分岐予測を最適化します。たとえば、次のようなものがある場合

if (unlikely(x)) {
  dosomething();
}

return x;

次に、このコードを次のように再構築できます。

if (!x) {
  return x;
}

dosomething();
return x;

これの利点は、プロセッサが最初に分岐するときに、コードを投機的にロードして実行している可能性があるため、かなりのオーバーヘッドが発生することです。分岐を取ると判断した場合は、それを無効にして、分岐ターゲットから開始する必要があります。

最近のほとんどのプロセッサには、ある種の分岐予測がありますが、これは、以前に分岐を通過したことがあり、分岐がまだ分岐予測キャッシュにある場合にのみ役立ちます。

これらのシナリオでコンパイラーとプロセッサーが使用できる戦略は他にもたくさんあります。分岐予測子がどのように機能するかについての詳細は、ウィキペディアで見つけることができます:http: //en.wikipedia.org/wiki/Branch_predictor

于 2008-09-20T23:14:39.580 に答える
11

これらにより、コンパイラは、ハードウェアがサポートする適切なブランチヒントを出力します。これは通常、命令オペコードの数ビットをいじることを意味するため、コードサイズは変更されません。CPUは、予測された場所から命令のフェッチを開始し、パイプラインをフラッシュして、ブランチに到達したときにそれが間違っていることが判明した場合は最初からやり直します。ヒントが正しい場合、これによりブランチがはるかに高速になります。正確には、ハードウェアに応じてどれだけ高速になりますか。これがコードのパフォーマンスにどの程度影響するかは、時間ヒントのどの割合が正しいかによって異なります。

たとえば、PowerPC CPUでは、ヒントのないブランチは16サイクル、正しくヒントされたブランチは8、誤ったヒントは24サイクルかかる場合があります。最も内側のループでは、適切なヒントが大きな違いを生む可能性があります。

移植性は実際には問題ではありません。おそらく、定義はプラットフォームごとのヘッダーにあります。静的ブランチヒントをサポートしていないプラットフォームでは、「可能性が高い」と「可能性が低い」を何も定義しないでください。

于 2008-09-20T23:11:53.267 に答える
7
long __builtin_expect(long EXP, long C);

この構文は、式 EXP が値 C を持つ可能性が最も高いことをコンパイラーに伝えます。戻り値は EXP です。 __builtin_expectは、条件式で使用するためのものです。ほとんどの場合、ブール式のコンテキストで使用されます。この場合、2 つのヘルパー マクロを定義する方がはるかに便利です。

#define unlikely(expr) __builtin_expect(!!(expr), 0)
#define likely(expr) __builtin_expect(!!(expr), 1)

これらのマクロは、次のように使用できます。

if (likely(a > 1))

参考:https ://www.akkadia.org/drepper/cpumemory.pdf

于 2016-11-23T13:22:05.497 に答える
4

(一般的なコメント - 他の回答が詳細をカバーしています)

それらを使用することで移植性を失う必要はありません。

他のコンパイラを使用して他のプラットフォームでコンパイルできるようにする単純な nil-effect "inline" またはマクロを作成するオプションが常にあります。

他のプラットフォームを使用している場合、最適化のメリットは得られません。

于 2008-09-20T23:19:44.153 に答える
3

Codyのコメントによると、これは Linux とは関係ありませんが、コンパイラへのヒントです。何が起こるかは、アーキテクチャとコンパイラのバージョンによって異なります。

Linux のこの特定の機能は、ドライバーでは多少誤用されています。osgxがホット属性のセマンティクスで指摘しているように、ブロック内で呼び出された関数hotまたはcold関数は、条件が可能性が高いかどうかを自動的に示唆できます。たとえば、これは冗長であるようにdump_stack()マークされています。cold

 if(unlikely(err)) {
     printk("Driver error found. %d\n", err);
     dump_stack();
 }

の将来のバージョンでgccは、これらのヒントに基づいて関数を選択的にインライン化する可能性があります。booleanではなく、スコアである可能性が高いなどの提案もありました。一般に、 などの代替メカニズムを使用することをお勧めしcoldます。ホットパス以外の場所で使用する理由はありません。あるアーキテクチャでコンパイラが行うことは、別のアーキテクチャではまったく異なる場合があります。

于 2014-01-27T20:59:36.603 に答える
2

多くの Linux リリースでは、 complier.h は /usr/linux/ にあり、簡単に使用できるように含めることができます。そして別の意見として、likely() よりもlikely() の方が便利です。

if ( likely( ... ) ) {
     doSomething();
}

多くのコンパイラでも最適化できます。

ところで、コードの詳細な動作を観察したい場合は、次のように簡単に実行できます。

gcc -c test.c objdump -d test.o > obj.s

次に、obj.s を開くと、答えが見つかります。

于 2012-03-07T02:27:04.490 に答える
1

これらは、ブランチにヒントプレフィックスを生成するためのコンパイラへのヒントです。x86 / x64では、1バイトを使用するため、ブランチごとに最大で1バイトの増加が得られます。パフォーマンスに関しては、それは完全にアプリケーションに依存します-ほとんどの場合、プロセッサの分岐予測は最近それらを無視します。

編集:彼らが実際に助けることができる1つの場所を忘れました。これにより、コンパイラーは制御フローグラフを並べ替えて、「可能性の高い」パスに対して実行される分岐の数を減らすことができます。これにより、複数の終了ケースをチェックしているループが大幅に改善される可能性があります。

于 2008-09-20T23:07:35.493 に答える
1

これらは、プログラマーが特定の式で最も可能性の高い分岐条件についてコンパイラーにヒントを与えるためのGCC関数です。これにより、コンパイラは分岐命令を作成できるため、最も一般的なケースでは、実行する命令の数が最も少なくなります。

分岐命令がどのように構築されるかは、プロセッサアーキテクチャによって異なります。

于 2008-09-20T23:08:15.243 に答える