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)
これらは最適化のためであることは知っていますが、どのように機能するのでしょうか。また、これらを使用することで、どの程度のパフォーマンス/サイズの削減が期待できるのでしょうか。また、少なくともボトルネック コード (もちろん、ユーザー空間) では、面倒な作業 (およびおそらく移植性の低下) をする価値があるのでしょうか。
ベストアンサー1
これらは、分岐予測がジャンプ命令の「可能性の高い」側を優先するようにする命令を発行するためのコンパイラへのヒントです。これは大きな利点になります。予測が正しければ、ジャンプ命令は基本的に無料であり、サイクルは 0 になります。一方、予測が間違っている場合は、プロセッサ パイプラインをフラッシュする必要があり、数サイクルかかる可能性があります。予測がほとんどの場合正しい限り、これはパフォーマンスにとって良い傾向にあります。
このようなパフォーマンス最適化はすべて、コードが本当にボトルネックになっているか、またマイクロの性質上、タイトなループで実行されているかを確認するために、徹底的なプロファイリングを行った後でのみ実行する必要があります。一般的に、Linux 開発者はかなり経験豊富であるため、そうしたことは行っていると思います。彼らは gcc のみを対象としているため、移植性についてはあまり気にしておらず、生成するアセンブリについて非常に正確な考えを持っています。
ほとんどの ISA には、静的予測 (後方分岐実行 / 前方分岐不実行) 以外に、マシン コードがハードウェア分岐予測器に実際にヒントを与える方法がないことに注意してください。また、2013 年頃からの x86 などの最新の実装では、それさえももう存在しません。
- なぜ Intel はここ数年で静的分岐予測メカニズムを変更したのでしょうか?
- 分岐予測器に、分岐が続く可能性を知らせることは可能ですか?(ほとんどの ISA ではそうではありません)
およびマクロまたは C++ /アノテーションはlikely
、コンパイラの分岐レイアウトをヒントにして、高速パスの I-cache の局所性を優先し、高速パスで実行される分岐を最小限に抑えることができます。また、可能な場合は、分岐のあるアセンブリと分岐のないアセンブリの決定をヒントにすることもできます。unlikely
[[likely]]
[[unlikely]]