使用最多SSE 4.2比较两个16字节值是否相等?

时间:2015-06-11 22:19:32

标签: c++ assembly vectorization sse avx

我有这样的结构:

struct {
    uint32_t a;
    uint16_t b;
    uint16_t c;
    uint16_t d;
    uint8_t  e;
} s;

我想以最快的方式比较上述两个结构的平等性。我查看了英特尔内部指南但无法找到整数的比较,可用的选项主要是双精度和单浮点矢量输入。

有人可以建议最好的方法吗?我可以在我的struct中添加一个union来使处理更容易。

我受限于(目前)使用SSE4.2,但如果它们明显更快,任何AVX答案都会受到欢迎。我正在使用GCC 4.8.2

2 个答案:

答案 0 :(得分:2)

@ 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执行按位AND作为其操作的一部分,因此您根本不需要单独的PAND

有一段时间,我认为我的版本可以使用更少的空间和更少的微量,但我最终需要额外的指令,因为没有pcmpneq(所以我需要一个逻辑not) 。所以它更小,相同数量的uops,但延迟明显更差。

.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 uop,因此ptest版本总计为4 uops(包括jcc或其他指令)。 pmovmskb版本也是4 uops,test / jcc宏融合分支,但cmovcc / setcc 5。 (4 andnsetcc / cmovcc / jcc,因为它无法宏观融合。)

(Agner Fog的表格说ptest在Sandybridge上占用1个融合域uop,在支持它的所有其他Intel CPU上占2个。但我不确定我是否相信。)

Haswell的延迟(如果分支不能很好地预测,则很重要):

  • pxor:1 + ptest:2 = 3个周期
  • pcmpeqb:1 + pmovmskb:3 + not:1 + test:1 = 6个周期
  • pcmpeqb:1 + pmovmskb:3 + andn:1 = 5个周期(但不是宏融合,可能还有1个延迟周期?)

因此ptest版本的延迟时间明显缩短:jcc可以更快地执行,以便更快地检测到分支错误预测。

Agner Fog的测试显示ptest在Nehalem上有延迟= 3,在SnB / IvB上有1,在Haswell上有2。

答案 1 :(得分:0)

一个简单的解决方案是在屏蔽后以字节方式减去两个结构,因此只有当所有压缩字节相同时才能获得全零值。此代码采用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 / 32byte-AVX(x)是没有意义的。