我有一个AVX cpu(不支持AVX2),我想计算两个256位整数的按位xor。
由于_mm256_xor_si256
仅在AVX2上可用,我可以使用__m256
将这256位加载为_mm256_load_ps
,然后执行_mm256_xor_ps
。这会产生预期的结果吗?
我主要担心的是,如果内存内容不是有效的浮点数,_mm256_load_ps
不会将寄存器中的位加载到与内存中完全相同的位置吗?
感谢。
答案 0 :(得分:12)
首先,如果您使用256b整数进行其他操作(例如加/减/乘),将它们放入向量寄存器仅用于偶尔的XOR可能不值得转移它们的开销。如果寄存器中已有两个数字(使用最多8个寄存器),则只需要四条xor
指令来获取结果(如果需要避免覆盖目标,则需要4条mov
指令)。破坏性版本可以在SnB上每1.33个时钟周期运行一次,或者在Haswell及更高版本上每个时钟运行一个。 (xor
可以在4个ALU端口中的任何一个上运行)。因此,如果您只是在某些xor
或其他任何内容之间执行单个add/adc
,请坚持使用整数。
以64b块存储到内存然后执行128b或256b加载cause a store-forwarding failure,再添加几个延迟周期。使用movq
/ pinsrq
会比xor
花费更多的执行资源。走另一条路并不是坏事:256b商店 - > 64b负载适用于商店转发。 movq
/ pextrq
仍然很糟糕,但会有更低的延迟(以更多uops为代价)。
FP加载/存储/按位操作在架构上保证不会生成FP异常,即使在表示信令NaN的位模式上使用时也是如此。只有实际的FP数学指令才会列出数学异常:
VADDPS
SIMD浮点例外
溢出,下溢,无效, 精确,非常规。
VMOVAPS
SIMD浮点例外
无。
(来自英特尔的insn ref手册。请参阅x86 wiki以获取该内容和其他内容的链接。)
在英特尔硬件上,加载/存储的风格可以在没有额外延迟的情况下进入FP或整数域。无论数据来自何处,AMD都会使用相同的加载/存储风格。
不同风格的向量移动指令actually matter for register<-register moves。在Intel Nehalem上,使用错误的mov指令可能会导致旁路延迟。在AMD Bulldozer系列中,移动是通过寄存器重命名处理而不是实际复制数据(如Intel IvB及更高版本),dest寄存器继承了src寄存器写入的域。
我所读过的现有设计与movapd
的处理movaps
完全不同。据推测,英特尔为解码简单性创建movapd
与未来规划一样多(例如,允许设计的可能性,其中有双域和单域,具有不同的转发网络)。 (movapd
是movaps
,前缀为66h
,就像每个其他SSE指令的双版本都添加66h
前缀字节一样。或F2
而不是F3
标量指令。)
显然AMD设计了带有辅助信息的FP矢量标签,因为Agner Fog found使用addps
的输出作为addpd
的输入时会有很大的延迟。我不认为movaps
两个addpd
指令之间的xorps
,甚至xorps
都会导致这个问题:只有实际的FP数学。 (FP bitwise boolean ops是Bulldozer-family上的整数域。)
Intel SnB / IvB(唯一具有AVX而非AVX2的Intel CPU)的理论吞吐量:
VMOVDQU ymm0, [A]
VXORPS ymm0, ymm0, [B]
VMOVDQU [result], ymm0
xorps
3个融合域uop可以每0.75个周期发出一次,因为管道宽度是4个融合域uops。 (假设你用于B的寻址模式和结果可以微融合,否则它的5个融合域uops。)
加载端口:SnB上的256b加载/存储需要2个周期(分成128b两半),但这会释放存储使用的端口2/3上的AGU。这是一个专用的存储数据端口,但存储地址计算需要来自加载端口的AGU。
因此,只有128b或更小的加载/存储,SnB / IvB每个周期可以支持两个存储操作(其中至多一个是存储)。对于256b操作,SnB / IvB理论上可以承受两个256b负载和一个256b存储每两个周期。但是,缓存库冲突通常会使这种情况变得不可能。
Haswell有一个专用的存储地址端口,可以支持两个256b负载和一个256b存储每个周期,并且没有缓存存储库冲突。因此,当所有内容都在L1缓存中时,Haswell会更快。
底线:理论上(没有缓存库冲突),这应该使SnB的加载和存储端口饱和,每个周期处理128b。每两个时钟需要一次Port5(唯一的端口VMOVDQU xmm0, [A]
VMOVDQU xmm1, [A+16]
VPXOR xmm0, xmm0, [B]
VPXOR xmm1, xmm1, [B+16]
VMOVDQU [result], xmm0
VMOVDQU [result+16], xmm1
可以运行)。
VXORPS
这将成为地址生成的瓶颈,因为SnB每个周期只能维持两个128b内存操作。它还将在uop缓存中使用2倍的空间,以及更多的x86机器代码大小。除非存在缓存库冲突,否则这应该以每3个时钟256b-xor的吞吐量运行。
在寄存器之间,每个时钟有一个256b VPXOR
和两个128b VPXOR
会使SnB饱和。在Haswell上,每个时钟三个AVX2 256b XORPS
将在每个周期中提供最多的XOR。 (PXOR
和XORPS
执行相同的操作,但VMOVDQU ymm0, [A]
VMOVDQU ymm4, [B]
VEXTRACTF128 xmm1, ymm0, 1
VEXTRACTF128 xmm5, ymm1, 1
VPXOR xmm0, xmm0, xmm4
VPXOR xmm1, xmm1, xmm5
VMOVDQU [res], xmm0
VMOVDQU [res+16], xmm1
的输出可以转发到FP执行单元而无需额外的转发延迟周期。我猜只有一个执行单元具有在FP域中具有XOR结果的布线so Intel CPUs post-Nehalem only run XORPS on one port。)
{{1}}
甚至更多融合域uops(8),而不仅仅是做128b-一切。
加载/存储:两个256b负载留下两个备用周期,用于生成两个存储地址,因此这仍然可以在两个负载/每个周期128b的一个存储中运行。
ALU:两个端口-5 uops(vextractf128),两个端口0/1/5 uop(vpxor)。
因此,这仍然具有每2个时钟一个256b结果的吞吐量,但它在3指令256b版本上的资源更多并且没有优势(在英特尔上)。 / p>
答案 1 :(得分:3)
使用_mm256_load_ps
加载整数没有问题。事实上,在这种情况下,它比使用_mm256_load_si256
(适用于AVX)更好,因为您使用_mm256_load_ps
保留在浮点域中。
#include <x86intrin.h>
#include <stdio.h>
int main(void) {
int a[8] = {1,2,3,4,5,6,7,8};
int b[8] = {-2,-3,-4,-5,-6,-7,-8,-9};
__m256 a8 = _mm256_loadu_ps((float*)a);
__m256 b8 = _mm256_loadu_ps((float*)b);
__m256 c8 = _mm256_xor_ps(a8,b8);
int c[8]; _mm256_storeu_ps((float*)c, c8);
printf("%x %x %x %x\n", c[0], c[1], c[2], c[3]);
}
如果你想留在你可以做的整数域
#include <x86intrin.h>
#include <stdio.h>
int main(void) {
int a[8] = {1,2,3,4,5,6,7,8};
int b[8] = {-2,-3,-4,-5,-6,-7,-8,-9};
__m256i a8 = _mm256_loadu_si256((__m256i*)a);
__m256i b8 = _mm256_loadu_si256((__m256i*)b);
__m128i a8lo = _mm256_castsi256_si128(a8);
__m128i a8hi = _mm256_extractf128_si256(a8, 1);
__m128i b8lo = _mm256_castsi256_si128(b8);
__m128i b8hi = _mm256_extractf128_si256(b8, 1);
__m128i c8lo = _mm_xor_si128(a8lo, b8lo);
__m128i c8hi = _mm_xor_si128(a8hi, b8hi);
int c[8];
_mm_storeu_si128((__m128i*)&c[0],c8lo);
_mm_storeu_si128((__m128i*)&c[4],c8hi);
printf("%x %x %x %x\n", c[0], c[1], c[2], c[3]);
}
_mm256_castsi256_si128
内在函数是免费的。
答案 2 :(得分:1)
您可能会发现,与使用2 x _mm_xor_si128
相比,性能上几乎没有差别。甚至有可能AVX实现会更慢,因为_mm256_xor_ps
在SB / IB / Haswell上的倒数吞吐量为1,而_mm_xor_si128
的倒数吞吐量为0.33。