这是好奇心的问题。我正在看这个代码反汇编(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指令,这是专门针对第一行代码中所示的情况而设计的。
任何人都可以对此有所了解吗?
答案 0 :(得分:6)
这里有两个不同的点。首先,为什么编译器使用SSE寄存器而不是x87浮点堆栈用于函数参数,其次为什么编译器不仅使用可以计算对数的单个指令。
不使用对数指令最容易解释,x86中的对数指令被定义为精确到80位,而你使用的是double,它只是64位。将对数计算为64位而不是80位的精度要快得多,并且速度的增加超过了必须在软件而不是硅中进行的操作。
SSE寄存器的使用更难以以满意的方式解释。简单的答案是x64调用约定要求函数的前四个浮点参数在xmm0
到xmm3
传递。
接下来的问题是,为什么调用约定会告诉你这样做而不是使用浮点堆栈。答案是本机x64代码很少使用x87 FPU,使用SSE代替。这是因为SSE中的乘法和除法更快(再次是80位与64位问题)并且SSE寄存器的操作速度更快(在FPU中,您只能访问堆栈顶部,并旋转FPU堆栈)对于现代处理器来说,这通常是最慢的操作,事实上有些处理器只有一个额外的管道阶段用于此目的。)