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 没有 Log 功能,这使得这个选择更加奇怪。此外,这里没有任何东西是并行化的,那么为什么是 SIMD 而不是简单的 x87?

顺便说一句,我还发现没有使用 x87 FYL2X指令很奇怪,该指令专为第一行代码中所示的情况而设计。

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

4

1 回答 1

8

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

不使用对数指令最容易解释,x86 中的对数指令被定义为精确到 80 位,而您使用的是只有 64 位的双精度指令。计算精度为 64 位而不是 80 位的对数要快得多,而且速度的提高足以弥补必须在软件中而不是在硅片中进行的计算。

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

下一个问题当然是为什么调用约定告诉你这样做而不是使用浮点堆栈。答案是本机 x64 代码根本很少使用 x87 FPU,而是使用 SSE 代替。这是因为 SSE 中的乘法和除法更快(又是 80 位与 64 位的问题),并且 SSE 寄存器的操作速度更快(在 FPU 中,您只能访问堆栈顶部,并旋转 FPU 堆栈通常是现代处理器上最慢的操作,实际上有些处理器专门为此目的增加了一个额外的流水线阶段)。

于 2012-09-18T12:38:48.110 回答