我对MOVSD汇编指令感到困惑。我写了一些计算一些矩阵乘法的数字代码,简单地使用没有SSE内在函数的普通C代码。我甚至没有包含用于编译的SSE2内在函数的头文件。但是当我检查汇编器输出时,我看到了:
1)使用128位向量寄存器XMM; 2)调用SSE2指令MOVSD。
我知道MOVSD基本上是在单双精度浮点上运行。它只使用XMM寄存器的低64位并设置高64位0.但我不理解两件事:
1)我从不给编译器任何使用SSE2的提示。另外,我使用GCC而不是英特尔编译器。据我所知,intel编译器会自动寻找矢量化的机会,但GCC不会。那么GCC如何知道使用MOVSD?或者,这个x86指令是否在SSE指令集之前很久,而SSE2中的_mm_load_sd()内在函数只是为了提供向后兼容性来使用XMM寄存器进行标量计算?
2)为什么编译器不使用其他浮点寄存器,80位浮点堆栈或64位浮点寄存器?为什么必须使用XMM寄存器(通过设置高64位0并基本上浪费该存储)? XMM是否提供更快的访问权限?
顺便说一句,我还有另外一个关于SSE2的问题。我无法看到_mm_store_sd()和_mm_storel_sd()之间的区别。两者都将较低的64位值存储到地址。有什么不同?性能差异??对齐差异??
谢谢。
更新1:
好的,显然当我第一次提出这个问题时,我缺乏一些关于CPU如何管理浮点运算的基本知识。因此专家倾向于认为我的问题是无意义的。由于我没有包括最短的样本C代码,人们可能会认为这个问题也很模糊。在这里,我将提供a review作为答案,希望这对任何不清楚现代CPU浮点运算的人都有用。
答案 0 :(得分:4)
回顾现代CPU上的浮点标量/矢量处理
矢量处理的概念可追溯到旧时代vector processors,但这些处理器已被具有缓存系统的现代架构所取代。因此,我们专注于现代CPU,尤其是x86和x86-64。这些架构是main stream in high performance scientific computing。
自i386以来,英特尔推出了浮点堆栈,其中可以保存高达80位宽的浮点数。此堆栈通常称为x87 or 387 floating point "registers",其中包含一组x87 FPU instructions。 x87堆栈不是真正的,可直接寻址的寄存器,如通用寄存器,因为它们位于堆栈中。访问寄存器st(i)是通过偏移堆栈顶部寄存器%st(0)或简单地%st。借助于在当前堆栈顶部%st和一些偏移寄存器%st(i)之间交换内容的指令FXCH,可以实现随机访问。但FXCH可以施加一些性能损失,尽管最小化。 x87堆栈通过默认情况下以80位精度计算中间结果来提供高精度计算,以最小化数值不稳定算法中的舍入误差。但是,x87指令完全是标量。
向量化的第一个努力是MMX instruction set,它实现了整数向量运算。 MMX下的向量寄存器是64位宽寄存器MMX0,MMX1,...,MMX7。每个都可用于保存64位整数,或者包含在#34;中的多个小整数。格式。然后可以将单个指令同时应用于两个32位整数,四个16位整数或八个8位整数。因此,现在存在用于标量整数运算的传统通用寄存器,以及用于没有共享执行资源的整数向量运算的新MMX。但MMX使用标量x87 FPU操作共享执行资源:每个MMX寄存器对应x87寄存器的低64位,而x87寄存器的高16位未使用。这些MMX寄存器均可直接寻址。但是混叠使得在同一应用程序中使用浮点和整数向量操作变得困难。为了最大限度地提高性能,程序员通常只在一种模式或另一种模式下使用处理器,尽可能延迟它们之间相对较慢的切换。
后来,SSE在x87堆栈的旁边创建了一组独立的128位宽寄存器XMM0-XMM7。 SSE指令将专门集中在单精度浮点运算(32位)上;使用MMX寄存器和MMX指令集仍然执行整数向量运算。但是现在两个操作可以同时进行,因为它们不共享执行资源。 重要的是要知道SSE不仅要进行浮点向量运算,还要进行浮点标量运算。从本质上讲,它提供了一个浮动操作发生的新位置,而x87堆栈不再是执行浮动操作的先决条件。使用XMM寄存器进行标量浮点运算比使用x87堆栈更快,因为所有XMM寄存器都更容易访问,而x87堆栈不能在没有FXCH的情况下随机访问。当我发布我的问题时,我显然没有意识到这一事实。我不清楚的另一个概念是通用寄存器是整数/地址寄存器。即使它们在x86-64上是64位,它们也不能保持64位浮点。主要原因是与通用寄存器相关的执行单元是ALU(算术和逻辑单元),不适用于浮点计算。
SSE2是一个重大进步,因为它扩展了向量数据类型,因此SSE2指令(标量或向量)可以与所有C标准数据类型一起使用。事实上,这种扩展使MMX过时了。此外,x87堆栈不像以前那么重要。由于有两个可以进行浮点运算的替代位置,因此可以指定编译器的选项。例如,对于GCC,使用标志
进行编译-mfpmath=387
将在旧版x87堆栈上安排浮点运算。 请注意,这似乎是32位x86的默认设置,即使SSE已经可用。例如,我有一台2007年制造的Intel Core2Duo笔记本电脑,它已经配备了SSE版本到版本SSE4,而GCC仍将默认使用x87堆栈,这使得科学计算不必要地变慢。在这种情况下,我们需要使用标志
进行编译-mfpmath=sse
并且GCC将在XMM寄存器上安排浮点运算。 64位x86-64用户无需担心此类配置,因为这是x86-64上的默认配置。这种信号只会影响标量浮点运算。如果我们使用向量指令编写代码并使用标志编译代码
-msse2
然后XMM寄存器将是唯一可以进行计算的地方。换句话说,这个标志打开-mfpmath = sse。有关更多信息,请参阅GCC's configuration of x86, x86-64。有关编写SSE2 C代码的示例,请参阅我的其他帖子How to ask GCC to completely unroll this loop (i.e., peel this loop)?。
SSE指令集虽然非常有用,但并不是最新的矢量扩展。 AVX advanced vector extensions通过提供3个操作数和4个操作数指令来增强SSE。如果您不清楚这意味着什么,请参阅number of operands in instruction set。 3操作数指令通过1)减少1个寄存器来优化科学计算中常见的fused multiply-add (FMA)操作; 2)减少寄存器之间明确的数据移动量; 3)加速FMA计算本身。例如,使用AVX,请参阅@Nominal Animal's answer to my post。