为什么.Net使用SIMD而不是x87用于SIMD不固有的数学运算?

Iam*_*mIC 8 .net compiler-construction assembly simd disassembly

这是一个好奇心问题.我正在看这个代码反汇编(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 
Run Code Online (Sandbox Code Playgroud)

...并且发现奇怪的是编译器没有在此处使用x87指令(Power使用日志).当然,我不知道调用位置的代码是什么,但我知道SIMD没有Log功能,这使得这个选择更加奇怪.此外,这里什么都没有,所以为什么SIMD而不是简单的x87?

在较小的一点上,我还发现奇怪的是没有使用x87 FYL2X指令,这是专门针对第一行代码中所示的情况而设计的.

任何人都可以对此有所了解吗?

jle*_*ahy 6

这里有两个不同的点.首先,为什么编译器使用SSE寄存器而不是x87浮点堆栈用于函数参数,其次为什么编译器不仅使用可以计算对数的单个指令.

不使用对数指令是最容易解释的,x86中的对数指令被定义为精确到80位,而你使用的是double,它只有64位.将对数计算为64位而不是80位精度要快得多,并且速度的增加超过了必须在软件而不是硅中完成的速度.

SSE寄存器的使用更难以以令人满意的方式解释.简单的答案是x64调用约定要求函数的前四个浮点参数xmm0通过xmm3.

接下来的问题是,为什么调用约定会告诉你这样做而不是使用浮点堆栈.答案是本机x64代码很少使用x87 FPU,使用SSE代替.这是因为SSE中的乘法和除法更快(再次是80位与64位问题)并且SSE寄存器的操作速度更快(在FPU中,您只能访问堆栈顶部,并旋转FPU堆栈)对于现代处理器来说,这通常是最慢的操作,事实上有些处理器只有一个额外的管道阶段用于此目的).

  • 指令 movsd 正是这样做的,它加载到 XMMn 的低 64 位中。另外 mulsd 仅将寄存器的低半部分相乘。 (2认同)
  • 第一个是正确的,第二个不一定是这样.像mulss这样的指令只将XMMn的最低32位中的单精度浮点数相乘.我不确定编译器如何处理单精度数学. (2认同)