【发布时间】:2015-01-08 22:40:09
【问题描述】:
如果我想在两个 __m128i 变量之间进行按位相等测试,我需要使用 SSE 指令还是可以使用 ==?如果不是,我应该使用哪个 SSE 指令?
【问题讨论】:
-
见*.com/q/6042399/3919155 __m128
如果我想在两个 __m128i 变量之间进行按位相等测试,我需要使用 SSE 指令还是可以使用 ==?如果不是,我应该使用哪个 SSE 指令?
【问题讨论】:
虽然使用_mm_movemask_epi8 是一种解决方案,但如果您的处理器采用SSE4.1,我认为更好的解决方案是使用在FLAGS 寄存器中设置零或进位标志的指令。 This saves a test or cmp instruction。
要做到这一点,你可以这样做:
if(_mm_test_all_ones(_mm_cmpeq_epi8(v1,v2))) {
//v0 == v1
}
编辑:正如 Paul R 指出的 _mm_test_all_ones 生成两条指令:pcmpeqd 和 ptest。 _mm_cmpeq_epi8 总共是三个指令。这是一个更好的解决方案,总共只使用两条指令:
__m128i neq = _mm_xor_si128(v1,v2);
if(_mm_test_all_zeros(neq,neq)) {
//v0 == v1
}
这会生成
pxor %xmm1, %xmm0
ptest %xmm0, %xmm0
【讨论】:
_mm_test_all_ones 是一个生成两条指令的宏:_mm_cmpeq_epi32 和 _mm_testc_si128,因此您的解决方案中总共有三个 SSE 指令。不过,将其与 _mm_movemask_epi8 的“old skool”实现进行基准测试会很有趣。
_mm_testX_si128 (PTEST) 的设计似乎存在一个根本缺陷,因为你不能轻易地用它来测试全为 1,因此您总是需要一条额外的指令来在某个点反转所有位。
ptest 如果您正在分支,则比较结果实际上并没有更快:它是 2 uops 加上 jcc 的 1。 pmovmskb 为 1,cmp/jcc 宏熔断为 1。但在 CPU 上,pxor 可以在比pcmpeqb/w/d/q 更多的端口上运行,这很有趣。
您可以使用比较,然后从比较结果中提取掩码:
__m128i vcmp = _mm_cmpeq_epi8(v0, v1); // PCMPEQB
uint16_t vmask = _mm_movemask_epi8(vcmp); // PMOVMSKB
if (vmask == 0xffff)
{
// v0 == v1
}
这适用于 SSE2 及更高版本。
正如@Zboson 所指出的,如果您有 SSE 4.1,那么您可以这样做,可能会稍微高效一些,因为它是两个 SSE 指令,然后是一个标志测试(ZF ):
__m128i vcmp = _mm_xor_si128(v0, v1); // PXOR
if (_mm_testz_si128(vcmp, vcmp)) // PTEST (requires SSE 4.1)
{
// v0 == v1
}
FWIW 我刚刚在 Haswell Core i7 上对这两种实现进行了基准测试,使用 clang 编译测试工具,时序结果非常相似 - SSE4 实现似乎稍微快一些,但很难衡量差异。
【讨论】:
考虑使用 SSE4.1 指令ptest:
if(_mm_testc_si128(v0, v1)) {if equal}
else {if not}
ptest 计算 a 和掩码中 128 位(表示整数数据)的按位与,如果结果为零则返回 1,否则返回 0。
【讨论】: