Linuxカーネルのlikely()マクロとunlikely()マクロはどのように動作し、どのような利点がありますか?


Answers

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

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

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

return x;

それから、このコードを以下のように再構成することができます:

if (!x) {
  return x;
}

dosomething();
return x;

この利点は、初めてプロセッサをブランチにするときに、かなり先にロードして実行していた可能性があるため、かなりのオーバーヘッドがあることです。 それがブランチを取ると判断したら、それを無効にして、ブランチターゲットから開始する必要があります。

最新のプロセッサーには分岐予測がありますが、これは以前分岐を通過した時点でのみ役立ち、分岐予測は依然として分岐予測キャッシュにあります。

これらのシナリオでは、コンパイラとプロセッサが使用できる他の多くの戦略があります。 分岐予測子の詳細については、Wikipedia: http : //en.wikipedia.org/wiki/Branch_predictorを参照してください。

Question

私は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)

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




コンパイラは、ハードウェアがサポートする適切な分岐ヒントを発行します。 これは、通常、命令オペコードのビット数を変えることを意味し、コードサイズは変更されません。 CPUは、予測された位置から命令をフェッチし始め、パイプラインをフラッシュし、ブランチに到達したときに間違っていると判明した場合は再始動します。 ヒントが正しい場合には、これはブランチをはるかに高速にします。正確にはハードウェアに依存する速度はどれくらいですか。 これがコードのパフォーマンスにどの程度影響を与えるかは、時間ヒントのどの部分が正しいかによって異なります。

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

移植性は実際問題ではありません。おそらく、定義はプラットフォームごとのヘッダにあります。 静的な分岐のヒントをサポートしていないプラットフォームでは、「可能性が高い」と「そう思わない」を単純に定義できます。




分岐上にヒント接頭辞を生成するためのヒントです。 x86 / x64では、1バイトを占有するため、各ブランチに対して最大で1バイトの増加が得られます。 パフォーマンスに関しては、アプリケーションに完全に依存します。ほとんどの場合、プロセッサ上の分岐予測器はこれらを無視します。

編集:彼らが実際に本当に助けることができる1つの場所を忘れてしまった。 コンパイラは、制御フローグラフを並べ替えることで、 '考えられる'パスの分岐数を減らすことができます。 これにより、複数の終了事例をチェックしているループで大幅な改善が得られます。




これらはGCCの関数であり、与えられた式のなかで最も分岐しやすい条件について、コンパイラにヒントを与えるためのものです。 これにより、最も一般的なケースが実行する命令の数が最も少なくなるように、コンパイラは分岐命令を構築することができます。

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




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

Linuxのこの特定の機能は、ドライバでは誤って使用されています。 osgx はホット属性のセマンティクスを指摘するので、ブロック内で呼び出されたhot関数またはcold関数は、その状態が起こりそうかどうかを自動的に暗示することができます。 たとえば、 dump_stack()coldとマークcoldため、これは冗長ですが、

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

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




Links