李哲源*_*李哲源 2 c assembly sse x86-64 sse2
我对 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:
OKAY,很明显,当我第一次问这个问题时,我缺乏关于 CPU 如何管理浮点运算的一些基本知识。所以专家们往往认为我的问题是无稽之谈。由于我什至没有包含最短的示例 C 代码,因此人们可能也会认为这个问题含糊不清。在这里,我将提供一个评论作为答案,希望对任何不清楚现代 CPU 上的浮点运算的人有用。
现代 CPU 上浮点标量/向量处理的回顾
矢量处理的想法可以追溯到旧时的矢量处理器,但这些处理器已被具有缓存系统的现代架构所取代。所以我们专注于现代 CPU,尤其是x86和x86-64。这些架构是高性能科学计算的主流。
从 i386 开始,Intel 引入了浮点堆栈,可以保存高达 80 位宽的浮点数。此堆栈通常称为x87 或 387 浮点“寄存器”,带有一组x87 FPU 指令. x87 堆栈不是像通用寄存器那样真实的、可直接寻址的寄存器,因为它们位于堆栈上。访问寄存器 st(i) 是通过偏移栈顶寄存器 %st(0) 或简单地 %st。借助 FXCH 指令在当前堆栈顶部 %st 和某个偏移寄存器 %st(i) 之间交换内容,可以实现随机访问。但是 FXCH 可以施加一些性能损失,尽管最小化。x87 堆栈通过默认以 80 位精度计算中间结果来提供高精度计算,以最大限度地减少数值不稳定算法中的舍入误差。但是,x87 指令是完全标量的。
矢量化的第一个努力是MMX 指令集,它实现了整数矢量操作。MMX下的向量寄存器是64位宽的寄存器MMX0、MMX1、...、MMX7。每个都可用于保存 64 位整数,或以“打包”格式保存多个较小的整数。然后可以将一条指令同时应用于两个 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
Run Code Online (Sandbox Code Playgroud)
将在传统 x87 堆栈上调度浮点运算。请注意,这似乎是 32 位 x86 的默认设置,即使 SSE 已经可用。例如,我有一台 2007 年制造的 Intel Core2Duo 笔记本电脑,它已经配备了 SSE 版本,最高版本为 SSE4,而 GCC 仍然默认使用 x87 堆栈,这使得科学计算不必要地变慢。在这种情况下,我们需要使用标志编译
-mfpmath=sse
Run Code Online (Sandbox Code Playgroud)
GCC 将在 XMM 寄存器上调度浮点运算。64 位 x86-64 用户无需担心此类配置,因为这是 x86-64 上的默认设置。这种信号只会影响标量浮点运算。如果我们使用向量指令编写代码并使用标志编译代码
-msse2
Run Code Online (Sandbox Code Playgroud)
那么 XMM 寄存器将是唯一可以进行计算的地方。换句话说,这个标志打开 -mfpmath=sse。有关更多信息,请参阅GCC 的 x86、x86-64 配置。有关编写 SSE2 C 代码的示例,请参阅我的另一篇文章如何要求 GCC 完全展开此循环(即剥离此循环)?.
SSE 指令集虽然非常有用,但并不是最新的向量扩展。AVX高级矢量扩展通过提供 3 操作数和 4 操作数指令增强了 SSE。如果您不清楚这意味着什么,请参阅指令集中的操作数数量。3 操作数指令将科学计算中常见的融合乘加 (FMA)操作优化为1) 使用少 1 个寄存器;2) 减少寄存器之间的显式数据移动量;3) 加速 FMA 计算本身。例如使用 AVX,请参阅@Nominal Animal 对我的帖子的回答。
| 归档时间: |
|
| 查看次数: |
4925 次 |
| 最近记录: |