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

时间:2012-09-12 01:15:12

标签: .net assembly compiler-construction 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 

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

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

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

1 个答案:

答案 0 :(得分:6)

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

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

SSE寄存器的使用更难以以满意的方式解释。简单的答案是x64调用约定要求函数的前四个浮点参数在xmm0xmm3传递。

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