对 SHUFPD 或 PSHUFD 是否有偏好来反转 XMM 中的两个 packed double?

Any preference to SHUFPD or PSHUFD for reversing two packed double in an XMM?

今天的问题很短。考虑以下玩具 C 程序 shuffle.c,用于反转寄存器 xmm0 中的两个打包双精度数:

#include <stdio.h>

void main () {
  double x[2] = {0.0, 1.0};
  asm volatile (
    "movupd  (%[x]), %%xmm0\n\t"
    "shufpd  , %%xmm0, %%xmm0\n\t"  /* method 1 */
    //"pshufd  , %%xmm0, %%xmm0\n\t"  /* method 2 */
    "movupd  %%xmm0, (%[x])\n\t"
    :
    : [x] "r" (x)
    : "xmm0", "memory");
  printf("x[0] = %.2f, x[1] = %.2f\n", x[0], x[1]);
  }

干运行后:gcc -msse3 -o shuffle shuffle.c | ./test,两者methods/instructions都会return得到正确的结果x[0] = 1.00, x[1] = 0.00This page says that shufpd has a latency of 6 cycles, while the intel intrinsic guide 表示 pshufd 只有 1 个周期的延迟。这听起来像是对 pshufd 的极大偏爱。但是,该指令确实适用于压缩整数。将它用于打包双打时,会不会有任何与 "wrong type" 相关的惩罚?

作为一个类似的问题,我也听说指令 movapsmovapd 小 1 个字节,它们通过从 16 位对齐地址读取 128 位来做同样的事情。那么我们是否总是可以使用前者进行移动(在 XMM 之间)/加载(从内存)/存储(到内存)?这看起来很疯狂。我认为必须有一些理由拒绝这一点。有人可以给我一个解释吗?谢谢。

您总是会得到正确的结果,但这对性能很重要。

对于将作为 FP 数学指令输入的 FP 数据(如 addpsvfma...,而不是像 xorps 这样的 insns),首选 FP 洗牌。

这避免了某些微体系结构(包括可能当前的 Intel 芯片)上的任何额外旁路延迟延迟。参见 Agner Fog's microarchitecture guide。 AMD Bulldozer 系列在向量整数域中进行所有洗牌,因此无论您使用哪种洗牌,都会有旁路延迟。

如果它节省了指令,那么无论如何使用整数洗牌都是值得的。 (但通常情况恰恰相反,你想使用 shufps 来组合来自两个整数向量的数据。在更多情况下这很好,而且主要只在 Nehalem,IIRC 上才有问题。)


http://x86.renejeschke.de/html/file_module_x86_id_293.html 列出了 CPUID 0F3n/0F2n CPUs 的延迟,即 Pentium4(系列 0xF 模型 2(Northwood)/模型 3(Prescott))。这些数字显然完全无关紧要,甚至不匹配 Agner Fog 的 P4 table for shufpd.

英特尔的内在函数指南有时也有与实验测试不匹配的数字。请参阅 Agner Fog's instruction tables 以获得良好的 latency/throughput 数字,以及微架构指南以了解详细信息。


movaps vs. movapd:没有现有的微架构关心你使用的是哪个。将来有人可能会设计一个 x86 CPU,使 double 向量在内部与 float 向量分开,但目前唯一的区别是 int 与 FP。

当行为相同时(xorps 优于 xorpdmovhps 优于 movhpd),始终首选 ps 指令。


一些编译器(可能是 gcc 和 clang,我忘记了)会将 _mm_store_si128 整数向量存储编译为 movaps,因为任何现有硬件都没有性能下降,而且它更短一个字节.

IIRC,使用 movaps / movups 加载整数向量数据也没有性能缺点,但我不太确定。

不过, 对 reg-reg 移动使用错误的 mov 指令会带来性能上的不利影响。 movdqa xmm1, xmm2 在 Nehalem 上两个 FP 指令之间是错误的。


回复:你的内联汇编:

它不需要是 volatile,如果您使用 16 字节结构或类似 "+m" input/output 的东西,您可以删除 "memory" 破坏符操作数。或 __m128d 变量的“+x”向量寄存器操作数。

与内联 asm 相比,内联函数可能会获得更好的结果,除非您在内联 asm 或独立函数中编写整个循环。

请参阅 标签 wiki 以获得 link 我的内联 asm 指南。