C++ 使用SSE 4.2比较两个16字节的值是否相等?
我有一个这样的结构:C++ 使用SSE 4.2比较两个16字节的值是否相等?,c++,assembly,vectorization,sse,avx,C++,Assembly,Vectorization,Sse,Avx,我有一个这样的结构: struct { uint32_t a; uint16_t b; uint16_t c; uint16_t d; uint8_t e; } s; 我想以最快的方式比较上面两种结构的相等性。我看了《英特尔Intrinsics指南》,但找不到整数的比较,可用的选项主要是双精度和单浮点向量输入 有人能告诉我最好的方法吗?我可以向我的结构中添加一个并集,以简化处理 我(目前)仅限于使用SSE4.2,但如果AVX的回答速度明显更快,我也欢迎
struct {
uint32_t a;
uint16_t b;
uint16_t c;
uint16_t d;
uint8_t e;
} s;
我想以最快的方式比较上面两种结构的相等性。我看了《英特尔Intrinsics指南》,但找不到整数的比较,可用的选项主要是双精度和单浮点向量输入
有人能告诉我最好的方法吗?我可以向我的结构中添加一个并集,以简化处理
我(目前)仅限于使用SSE4.2,但如果AVX的回答速度明显更快,我也欢迎。我使用的是GCC4.8.2一个简单的解决方案是在屏蔽后按字节减去两个结构,这样只有当所有压缩字节都相同时,才能得到一个全零值。这段代码是MASM格式的,但您肯定可以将其改编为gcc AT&T语法或内部语言:
.data
mask11byte db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0,0,0,0,0
.code
pand xmm1, xmmword ptr [mask11byte]
pand xmm2, xmmword ptr [mask11byte]
psubb xmm1, xmm2
ptest xmm1, xmm1 ; SSE 4.1
setz al ; AL=TRUE for equal
添加:因为结构的大小是11字节,所以256bit/32字节的AVX(x)没有意义。@zx485应该写的是:
.data
mask11byte db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0,0,0,0,0
.code
pxor xmm1, xmm2 ; equiv to psubb, but runs on all 3 vector execution ports
ptest xmm1, xmmword ptr [mask11byte] ; SSE 4.1
setz al ; AL=TRUE for equal
只要没有什么不好的事情发生(浮点异常),您就不需要在计算之前屏蔽操作数,即使它们包含垃圾。由于PTEST
执行按位和作为其操作的一部分,因此根本不需要单独的PAND
有一段时间,我认为我有一个版本可以使用更少的空间和更少的UOP,但我最终需要一个额外的指令,因为没有pcmpneq
(所以我需要一个逻辑而不是)。因此它更小,UOP的数量相同,但延迟明显更差
.code
PCMPEQB xmm1, xmm2 ; bytes of xmm1 = 0xFF on equal
PMOVMSKB eax, xmm1 ; ax = high bit of each byte of xmm1
NOT eax
TEST eax, 0x7FF ; zero flag set if all the low 11 bits are zero
SETZ al ; 17 bytes
; Or one fewer insn with BMI1's ANDN. One fewer uop if test can't macro-fuse
ANDN eax, eax, [mask11bits] ; only test the low 11 bits.
; ANDN version takes 20 bytes, plus 2B of data
.data
mask11bits dw 07ffh
test
可以与jcc
进行宏融合,因此,如果您将其用作跳转条件,而不是实际执行setz
,那么您将在规模上领先。(因为您不需要16B掩码常量。)
ptest
需要2个计量单位,因此ptest
版本总共是4个计量单位(包括jcc
或其他指令)。pmovskb
版本也是4个UOP,带有test
/jcc
宏融合分支,但5个UOP带有cmovcc
/setcc
。(4带有和n
,带有任何setcc
/cmovcc
/jcc
,因为它不能进行宏融合`。)
(Agner Fog的表格显示,ptest
在Sandybridge上取1个融合域uop,在所有支持它的英特尔CPU上取2个。不过,我不确定我是否相信这一点。)
Haswell上的延迟(如果分支不能很好地预测,则很重要):
pxor
:1+ptest
:2=3个周期
pcmpeqb
:1+pmovmskb
:3+非
:1+测试
:1=6个周期
pcmpeqb
:1+pmovmskb
:3+和n
:1=5个周期(但不是宏融合,因此可能还有1个周期的延迟?)
因此,ptest
版本的延迟显著缩短:jcc
可以更快地执行,以更快地检测分支预测失误
Agner Fog的测试显示,ptest
在Nehalem上的延迟为3,在SnB/IvB上的延迟为1,在Haswell上的延迟为2。您可以使用PCMPEQ
系列中的任何整数比较,我看不出您的问题。此结构基本上是打包的。你能假设这永远是真的吗?似乎memcmp(&s1,&s2,sizeof(struct s))
可能是最少的时间投资。利用memcmp提供的任何优化功能。@JonathonReinhart yes可以假设压缩。@Jester\u mm\u cmpeq\u epi64只接受64位输入?该结构只有11个字节;因此,在进行比较之前,您需要屏蔽掉未使用的5个字节(以防它们包含垃圾);或者比较压缩字节并屏蔽未使用的5个字节的结果。在现代64位计算机上,使用uint16\u t e
将其设置为12个字节,并使用枯燥的老式“非SIMD”整数比较进行64位比较和32位比较可能会更快(特别是如果您没有处理这些结构的数组)……或者,您可以使用pxor
而不是psub
,而且只需pand
一次。您根本不需要pand
。将掩码用作一个操作数,以ptest
;这就是它的目的!您可以在psubb
(字节减法)或pxor
之后进行掩码,但是pxor
可以在psubb
无法运行的一个端口(SnB端口0)上运行,这是一个很好的调用。