今天的问题相当简短。考虑以下玩具C程序shuffle.c
,以便在寄存器xmm0
中反转两个打包的双重程序:
#include <stdio.h>
void main () {
double x[2] = {0.0, 1.0};
asm volatile (
"movupd (%[x]), %%xmm0\n\t"
"shufpd $1, %%xmm0, %%xmm0\n\t" /* method 1 */
//"pshufd $78, %%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
,两个方法/说明都会返回正确的结果x[0] = 1.00, x[1] = 0.00
。 This page表示shufpd
的延迟为6个周期,而intel intrinsic guide表示pshufd
的延迟为1个周期。这听起来非常偏向于pshufd
。但是,该指令对于打包整数是真的。当它用于打包双打时,是否会有与#34;错误类型相关的任何惩罚&#34;?
作为一个类似的问题,我还听说指令movaps
比movapd
小1个字节,它们通过从16位对齐的地址读取128位来做同样的事情。那么我们总是可以使用前者来移动(在XMM之间)/加载(从内存)/存储(到内存)吗?这看起来很疯狂。我认为必须有一些理由拒绝这一点。有人可以给我一个解释吗?谢谢。
答案 0 :(得分:4)
您将始终获得正确的结果,但这对性能至关重要。
首选FP shuffle用于FP数据,这些数据将作为FP数学指令的输入(如addps
或vfma...
,而不是像xorps
这样的insn。)
这避免了某些微体系结构(包括潜在的当前英特尔芯片)的任何额外的旁路延迟延迟。见Agner Fog's microarchitecture guide。 AMD Bulldozer-family在向量整数域中进行所有shuffle,因此无论你使用哪种shuffle都会有一个旁路延迟。
如果它保存了指令,那么无论如何都使用整数shuffle是值得的。 (但通常情况是相反的,你想用shufps
来组合来自两个整数向量的数据。这在更多的情况下都很好,而且大多只是Nehalem,IIRC的问题。)
http://x86.renejeschke.de/html/file_module_x86_id_293.html列出了CPUID 0F3n / 0F2n CPU的延迟,即Pentium4(系列0xF型号2(Northwood)/型号3(Prescott))。这些数字显然完全不相关,甚至不匹配Agner Fog的shufpd
的P4表。
英特尔的内在指南有时也有与实验测试不匹配的数字。有关良好的延迟/吞吐量数字,请参阅Agner Fog's instruction tables,了解详细信息,请参阅微型指南。
movaps
与movapd
:没有现有的微体系结构关注您使用的内容。将来某人可能会设计一个x86 CPU,使double
向量在内部与float
向量分开,但目前唯一的区别是int与FP。
当行为相同时(ps
超过xorps
,xorpd
超过movhps
),始终首选movhpd
指令。
有些编译器(可能是gcc和clang,我忘了)会将_mm_store_si128
整数向量存储编译为movaps
,因为任何现有硬件都没有性能下降,而且缩短了一个字节。 / p>
movaps
/ movups
加载整数向量数据也没有任何缺点,但我对此不太确定。
但 是使用错误的mov指令进行reg-reg移动的一个缺点。两条FP指令之间的movdqa xmm1, xmm2
在Nehalem上是不好的。
re:你的内联asm:
它不需要是volatile
,如果您使用16字节结构或某些内容作为"memory"
输入/输出操作数,则可以删除"+m"
clobber。或__m128d
变量的“+ x”向量寄存器操作数。
除非你在内联asm或独立函数中编写整个循环,否则你可能会从内联函数中获得比内联asm更好的结果。
请参阅x86代码Wiki,以获取指向我的内联asm指南的链接。