2

変化せず、常にゼロに等しい変数 A、関数 F、関数 G、および関数 H があり、最新バージョンの GCC で -O3 最適化を使用して最新の Intel デスクトップ プロセッサで次のコードを呼び出すとします。 :

for(i = 0; i < a_big_number; i++)
{
if(A != 0) F();
else G();
}

実行には2秒かかります。A は常に 0 であるため、F は決して呼び出されないことに注意してください。

for(i = 0; i < a_big_number; i++)
{
if(A != 0) H();
else G();
}

実行に1秒しかかかりません。ここでも、A は常に 0 であり、H は呼び出されません。最後に、

for(i = 0; i < a_big_number; i++)
{
G();
}

実行にかかる時間はわずか 0.5 秒です。

最初の 2 つの例の条件ステートメントを考えると、なぜ F と H の内容が重要なのでしょうか? それらは決して呼び出されないのに、なぜそれらの動作に違いが生じるのでしょうか? また、Intel プロセッサが高度な分岐予測機能を備えていることを考えると、プロセッサは G() が常に呼び出され、条件ステートメントで時間を無駄にすることさえないことを理解すべきではないでしょうか? 条件付き命令が時間を無駄にする必要があることは理解していますが、なぜそんなに時間を無駄にするのかわかりません。

4

2 に答える 2

0

コンパイラがそれが定数であることを理解していると仮定すると、A次のコードを変換する必要があります。

for(i = 0; i < a_big_number; i++)
{
    if(A != 0) F();
    else G();
}

これに:

if(A != 0)
    for(i = 0; i < a_big_number; ++i)
        F();
else
    for(i = 0; i < a_big_number; ++i)
        G();

F()または、定数がコンパイル時の定数のように見える場合は、関数呼び出しを完全に最適化します。

それが起こらない場合 (つまり、副作用などの可能性があります。コンパイラは最適化の方法と内容を保証しません)、ループは分岐予測の誤りによるパフォーマンス ヒットに遭遇します。A が変更されておらず、呼び出された関数が十分に小さい場合、CPU はループをロックして分岐を記憶する必要があるため、同じ間違いが何度も繰り返されることはありません。一方、ループはアンロールされる可能性があり、これは並列化できず、コード サイズと CPU が追跡しなければならない多くのことだけを吹き飛ばすだけであるため、大きな問題になる可能性があります。

実行時間をどのように測定するかは、ループで呼び出す関数が何をしているのかと同様に、私にとっては謎です。たとえば、プロセス実行のスワップアウトを測定することができます。そのため、測定方法の詳細な説明とともに完全に機能する例を提供しない限り、何が起こっているのかを伝えることは不可能です.

いずれにせよ、あなたの時間測定が間違っているか、表示されていないコードで何か悪いことをしているに違いありません。

于 2012-04-17T21:10:33.450 に答える
0

私の知る限り、コンパイラは分岐が実行されないかどうかを判断できません。コンパイラができる最善のことは、どの分岐がより可能性が高いかを予測することです。

于 2012-04-18T01:06:48.897 に答える