9

これは何よりも好奇心の問題です。私はこのコードの逆アセンブル(C#、64ビット、リリースモード、VS 2012 RC)を見ていました:

            double a = 10d * Math.Log(20d, 2d);
000000c8  movsd       xmm1,mmword ptr [00000138h] 
000000d0  movsd       xmm0,mmword ptr [00000140h] 
000000d8  call        000000005EDC7F50 
000000dd  movsd       mmword ptr [rsp+58h],xmm0 
000000e3  movsd       xmm0,mmword ptr [rsp+58h] 
000000e9  mulsd       xmm0,mmword ptr [00000148h] 
000000f1  movsd       mmword ptr [rsp+30h],xmm0 
            a = Math.Pow(a, 6d);
000000f7  movsd       xmm1,mmword ptr [00000150h] 
000000ff  movsd       xmm0,mmword ptr [rsp+30h] 
00000105  call        000000005F758220 
0000010a  movsd       mmword ptr [rsp+60h],xmm0 
00000110  movsd       xmm0,mmword ptr [rsp+60h] 
00000116  movsd       mmword ptr [rsp+30h],xmm0 

...そしてコンパイラがここのログにx87命令を使用していないのは奇妙だとわかりました(Powerはログを使用します)。もちろん、呼び出し場所にどのコードがあるのか​​はわかりませんが、SIMDにはログ機能がないため、この選択はさらに奇妙になります。さらに、ここでは何も並列化されていないのに、なぜ単純なx87ではなくSIMDなのですか?

控えめに言っても、x87 FYL2X命令が使用されていないのも奇妙だと思いました。これは、コードの最初の行に示されているケースのために特別に設計されたものです。

誰かがこれに光を当てることができますか?

4

1 に答える 1

8

ここには2つの別々のポイントがあります。まず第一に、コンパイラが関数の引数にx87浮動小数点スタックではなくSSEレジスタを使用する理由、そして第二に、コンパイラが対数を計算できる単一の命令を使用しない理由です。

対数命令を使用しないのが最も簡単に説明できます。x86の対数命令は80ビットまで正確であると定義されていますが、doubleを使用しているのは64ビットのみです。対数を80ビットの精度ではなく64ビットに計算する方がはるかに高速であり、速度はシリコンではなくソフトウェアで計算する必要があることを補って余りあります。

SSEレジスタの使用は、満足のいく方法で説明するのがより困難です。xmm0簡単な答えは、x64呼び出し規約では、関数への最初の4つの浮動小数点引数がを介して渡される必要があるということxmm3です。

次の質問は、もちろん、呼び出し規約が浮動小数点スタックを使用するのではなく、これを行うように指示するのはなぜですか。答えは、ネイティブx64コードがx87 FPUを使用することはめったになく、代わりにSSEを使用することです。これは、SSEでは乗算と除算が高速であり(80ビットと64ビットの問題)、SSEレジスタの操作が高速であるためです(FPUでは、スタックの最上位にのみアクセスでき、FPUスタックをローテーションできます)。多くの場合、最新のプロセッサでは最も遅い操作です。実際、この目的のためだけに追加のパイプラインステージがあるものもあります)。

于 2012-09-18T12:38:48.110 に答える