为什么 .NET 使用 SIMD 而不是 x87 来进行非 SIMD 固有的数学运算?

问题描述 投票:0回答:1

这首先是好奇心的问题。我正在查看这段代码反汇编(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 指令,该指令是专为第一行代码中显示的情况而设计的。

有人能解释一下吗?

.net assembly simd sse x87
1个回答
11
投票

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

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

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

xmm0
xmm3
处传递。

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

© www.soinside.com 2019 - 2024. All rights reserved.