为什么现代编译器更喜欢 SSE 而不是 FPU 来进行单浮点运算

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

我最近尝试读取代码的二进制程序集,发现很多浮点运算是使用 XMM 寄存器和 SSE 指令完成的。例如下面的代码:

float square(float a) {
    float b = a + (a * a);
    return b;
} 

将被编译成

        push    rbp
        mov     rbp, rsp
        movss   DWORD PTR [rbp-20], xmm0
        movss   xmm0, DWORD PTR [rbp-20]
        mulss   xmm0, xmm0
        movss   xmm1, DWORD PTR [rbp-20]
        addss   xmm0, xmm1
        movss   DWORD PTR [rbp-4], xmm0
        movss   xmm0, DWORD PTR [rbp-4]
        pop     rbp
        ret

其他编译器的结果类似。 https://godbolt.org/z/G988PGo6j

还有

-O3
旗帜

        movaps  xmm1, xmm0
        mulss   xmm0, xmm0
        addss   xmm0, xmm1
        ret

这是否意味着使用 SIMD 寄存器和指令的操作通常比使用普通寄存器和 FPU 更快?

另外,我很好奇编译器使用 SSE 的决定可能会失败的具体情况。

c++ c assembly floating-point sse
1个回答
0
投票

SSE 是作为 x87 FPU 的替代品而开发的,因为 x87 FPU 的设计有点特殊并且难以为其生成代码。 主要问题是:

  • 基于堆栈的处理器(例如 x87 FPU)的代码生成不如基于寄存器的处理器那样容易理解,这使得许多编译器生成的代码充满了低效的额外
    fxch
    fld
    fst(p)
    指令。 使用 SSE 这样的基于寄存器的架构更容易实现这一点。
  • SSE支持SIMD操作,如果使用的话,可以大大加快数组上的浮点操作速度。 X87不支持这样的东西
  • 在x87 FPU上,使用的数据类型是Intel的80位浮点格式。 浮点运算的精度是使用控制寄存器配置的,并且更改的成本很高。 因此,为 x87 单元生成代码的编译器将以全精度运行所有计算,即使程序员只需要单精度也是如此。 这会稍微改变结果并降低性能,因为更高精度的操作可能需要更多时间才能完成。 此外,每次从 x87 单元加载和存储到 x87 单元都涉及隐式数据类型转换。 另一方面,SSE 单元对所用指令的精度进行编码,允许编译器准确地使用程序员要求的精度。
  • 最近,CPU 制造商减少了改进 x87 FPU 的投资(甚至收回了现有的改进,比如
    fxch
    进行了重命名),导致 x87 和 SSE 之间的性能差距越来越大。

如果代码大小有问题或者您需要 80 位浮点格式,我建议仅使用 x87 FPU。 否则请坚持使用 SSE 或(在最新的处理器上)AVX。

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