我试图在intel x86-64上理解SSE的不同MOV指令。
根据this,在2个寄存器之间移动数据时,应使用对齐指令(MOVAPS,MOVAPD和MOVDQA),使用正确操作的类型。将寄存器移动到存储器时使用MOVUPS / MOVAPS,反之亦然,因为类型在移入/移出存储器时不会影响性能。
有没有理由使用MOVDQU和MOVUPD?我在链接上的解释是错误的吗?
答案 0 :(得分:3)
摘要:我不知道最近有任何x86架构在使用"错误"加载指令(即加载指令后跟来自相反域的ALU指令)。
这里有Agner has to say关于绕过延迟的内容,这是您在CPU中的各个执行域之间移动时可能产生的延迟(有时这些是不可避免的,但有时候它们可能是由于使用了错误的"版本的指令而引起的:
Nehalem上的数据绕过延迟在Nehalem上,执行单位是 分为五个"域":
整数域处理通用的所有操作 寄存器。整数向量(SIMD)域处理整数运算 在向量寄存器中。 FP域处理浮点运算 在XMM和x87寄存器中。加载域处理所有内存读取。 存储域处理所有内存存储。有额外的延迟 在一个域中输出操作时的1或2个时钟周期 用作另一个域中的输入。这些所谓的旁路延迟是 见表8.2。
使用加载和存储时,仍然没有额外的旁路延迟 关于错误类型数据的说明。例如,它可以 方便使用MOVHPS对整数数据进行读写 XMM寄存器的上半部分。
最后一段中的重点是我的,并且是关键部分:旁路延迟并不适用于Nehalem加载和存储指令。直观地说,这是有道理的:加载和存储单元专用于整个核心,并且必须以适合任何执行单元的方式使其结果可用(或将其存储在PRF中) - 与ALU情况不同,同样关注于转发不存在。
现在不再关心Nehalem了,但是在Sandy Bridge / Ivy Bridge,Haswell和Skylake的部分中,你会发现这些域名正如Nehalem所讨论的那样,那里整体延迟较少。因此可以假设加载和存储不会因指令类型而遭受延迟的行为仍然存在。
我们也可以测试一下。我写了一个像这样的基准:
bypass_movdqa_latency:
sub rsp, 120
xor eax, eax
pxor xmm1, xmm1
.top:
movdqa xmm0, [rsp + rax] ; 7 cycles
pand xmm0, xmm1 ; 1 cycle
movq rax, xmm0 ; 1 cycle
dec rdi
jnz .top
add rsp, 120
ret
使用movdqa
加载一个值,对其执行整数域操作(pand
),然后将其移动到通用寄存器rax
,以便它可以用作下一循环中movdqa
的地址。我还创建了3个与上述相同的基准测试,除了movdqa
替换为movdqu
,movups
和movupd
。
Skylake-client上的结果(带有最近微代码的i7-6700HQ):
** Running benchmark group Vector unit bypass latency **
Benchmark Cycles
movdqa [mem] -> pxor latency 9.00
movdqu [mem] -> pxor latency 9.00
movups [mem] -> pxor latency 9.00
movupd [mem] -> pxor latency 9.00
在每种情况下,rountrip延迟都是相同的:9个周期,正如预期的那样:负载为6 + 1 + 2个周期,pxor
和movq
。
所有这些测试都添加在uarch-bench中,以防您想在任何其他架构上运行它们(我会对结果感兴趣)。我使用命令行:
./uarch-bench.sh --test-name=vector/* --timer=libpfc
答案 1 :(得分:0)
请注意,您引用的有关SSE移动性能的链接相当陈旧,可能仅适用于较旧代的Intel硬件。我已经了解到最近的微体系结构改善了例如未对齐的加载指令,如果它们用于实际对齐的数据。总而言之,简短的基准测试是适用于您所拥有的特定硬件的有效信息的最佳来源。