2

以下の関数は、32 ビット浮動小数点値の絶対値を計算します。

__forceinline static float Abs(float x)
{
    union {
        float x;
        int a;
    } u;
    //u.x = x;
    u.a &= 0x7FFFFFFF;
    return u.x;
}

関数で宣言された共用体 u は変数 x を保持します。これは、関数でパラメーターとして渡される x とは異なります。関数への引数を持つユニオンを作成する方法はありますか - x?

上記の関数のコメントを外した行が、これよりも長く実行される理由はありますか?

__forceinline float fastAbs(float a)
{
    int b= *((int *)&a) & 0x7FFFFFFF;
    return *((float *)(&b));
}

メモリへの読み取り/書き込みの回数をできるだけ少なくして、浮動小数点値の Abs を取得する最良の方法を見つけようとしています。

4

2 に答える 2

5

最初の質問については、課題で自分のやりたいことだけをできない理由がわかりません。コンパイラは実行可能な最適化を実行します。

2 番目のサンプル コード。厳密なエイリアシングに違反しています。だから、同じではありません。

遅い理由については:

これは、現在の CPU が整数ユニットと浮動小数点ユニットを別々に持つ傾向があるためです。このように型をパニングすることで、値をある単位から別の単位に強制的に移動させます。これにはオーバーヘッドがあります。(これは多くの場合メモリを介して行われるため、追加のロードとストアが必要になります。)

2 番目のスニペット aでは、元々は浮動小数点ユニット (x87 FPU または SSE レジスタのいずれか) にあり、 mask を適用するために汎用レジスタに移動する必要があります0x7FFFFFFF。次に、元に戻す必要があります。

最初のスニペット:aコンパイラはおそらく、整数ユニットに直接ロードするほどスマートです。したがって、最初の段階で FPU をバイパスします。

(アセンブリーを見せていただくまで、100% 確信はありません。また、パラメーターがレジスターまたはスタックのどちらで始まるかにも大きく依存します。また、出力が別の浮動小数点演算によってすぐに使用されるかどうかにも大きく依存します。)

于 2011-10-26T16:09:31.260 に答える
2

リリースモードでコンパイルされたコードの逆アセンブルを見ると、違いは非常に明白です。インラインを削除し、2つの仮想関数を使用して、コンパイラーがあまり最適化しないようにし、違いを示します。

これが最初の機能です。

013D1002  in          al,dx  
            union {
                float x;
                int a;
            } u;
            u.x = x;
013D1003  fld         dword ptr [x]   // Loads a float on top of the FPU STACK.
013D1006  fstp        dword ptr [x]   // Pops a Float Number from the top of the FPU Stack into the destination address.
            u.a &= 0x7FFFFFFF;
013D1009  and         dword ptr [x],7FFFFFFFh  // Execute a 32 bit binary and operation with the specified address.
            return u.x;
013D1010  fld         dword ptr [x]  // Loads the result on top of the FPU stack.
        }

これは2番目の関数です。

013D1020  push        ebp                       // Standard function entry... i'm using a virtual function here to show the difference.
013D1021  mov         ebp,esp
            int b= *((int *)&a) & 0x7FFFFFFF;
013D1023  mov         eax,dword ptr [a]         // Load into eax our parameter.
013D1026  and         eax,7FFFFFFFh             // Execute 32 bit binary and between our register and our constant.
013D102B  mov         dword ptr [a],eax         // Move the register value into our destination variable
            return *((float *)(&b));
013D102E  fld         dword ptr [a]             // Loads the result on top of the FPU stack.

最初のケースでは、浮動小数点演算の数とFPUスタックの使用量が多くなります。関数はあなたが要求したとおりに実行されているので、驚くことではありません。したがって、2番目の関数の方が高速であると期待しています。

さて...仮想とインライン化の削除は少し異なります。もちろんコンパイラは良い仕事をするので、ここで逆アセンブリコードを書くのは難しいですが、繰り返しますが、値が定数でない場合、コンパイラはより多くの浮動小数点を使用します最初の関数での操作。もちろん、整数演算は浮動小数点演算よりも高速です。

math.h abs関数を直接使用すると、メソッドよりも遅くなりますか?正しくインライン化されている場合、abs関数はこれを実行します!

00D71016  fabs  

このようなマイクロ最適化は長いコードではわかりにくいですが、関数が浮動小数点演算の長いチェーンで呼び出された場合、値はすでにFPUスタックまたはSSEレジスタにあるため、ファブはより適切に機能します。absは、コンパイラーによってより高速で最適化されます。

コードの一部でループを実行する最適化のパフォーマンスを測定することはできません。コンパイラが実際のコードでどのようにすべてを混合するかを確認する必要があります。

于 2011-10-26T16:32:06.343 に答える