我想测试两个 SSE 寄存器是否都不为零,而不会破坏它们。
这是我目前的代码:
uint8_t *src; // Assume it is initialized and 16-byte aligned
__m128i xmm0, xmm1, xmm2;
xmm0 = _mm_load_si128((__m128i const*)&src[i]); // Need to preserve xmm0 & xmm1
xmm1 = _mm_load_si128((__m128i const*)&src[i+16]);
xmm2 = _mm_or_si128(xmm0, xmm1);
if (!_mm_testz_si128(xmm2, xmm2)) { // Test both are not zero
}
这是最好的方法(使用SSE 4.2)吗?
答案 0 :(得分:3)
我从这个问题中学到了一些有用的东西。我们先来看看一些标量代码
extern foo2(int x, int y);
void foo(int x, int y) {
if((x || y)!=0) foo2(x,y);
}
像这样编译gcc -O3 -S -masm=intel test.c
,重要的程序集是
mov eax, edi ; edi = x, esi = y -> copy x into eax
or eax, esi ; eax = x | y and set zero flag in FLAGS if zero
jne .L4 ; jump not zero
现在让我们看看测试SIMD寄存器为零。与标量代码不同,没有SIMD FLAGS寄存器。但是,对于SSE4.1,有SIMD测试指令可以在标量FLAGS寄存器中设置零标志(和进位标志)。
extern foo2(__m128i x, __m128i y);
void foo(__m128i x, __m128i y) {
__m128i z = _mm_or_si128(x,y);
if (!_mm_testz_si128(z,z)) foo2(x,y);
}
使用c99 -msse4.1 -O3 -masm=intel -S test_SSE.c
进行编译,重要的程序集是
movdqa xmm2, xmm0 ; xmm0 = x, xmm1 = y, copy x into xmm2
por xmm2, xmm1 ; xmm2 = x | y
ptest xmm2, xmm2 ; set zero flag if zero
jne .L4 ; jump not zero
请注意,这需要多一条指令,因为压缩的逐位OR不会设置零标志。另请注意,标量版本和SIMD版本都需要使用额外的寄存器(标量情况下为eax
,SIMD情况下为xmm2
)。 所以回答你的问题,你现在的解决方案是你能做的最好的。
但是,如果您没有SSE4.1或更高版本的处理器,则必须使用另一个只需要SSE2的替代方法是使用_mm_movemask_epi8
。_mm_movemask_epi8
extern foo2(__m128i x, __m128i y);
void foo(__m128i x, __m128i y) {
if (_mm_movemask_epi8(_mm_or_si128(x,y))) foo2(x,y);
}
重要的集会是
movdqa xmm2, xmm0
por xmm2, xmm1
pmovmskb eax, xmm2
test eax, eax
jne .L4
请注意,这需要另外一条指令,然后使用SSE4.1 ptest
指令。
到目前为止,我一直在使用pmovmaskb
指令,因为Sandy Bridge处理器之前的延迟优于ptest
。但是,我在Haswell之前意识到了这一点。在Haswell上,pmovmaskb
的延迟比ptest
的延迟更差。它们都具有相同的吞吐量。但在这种情况下,这并不重要。重要的(我之前没有意识到)是pmovmaskb
没有设置FLAGS寄存器,所以它需要另一条指令。 现在我将在关键循环中使用ptest
。感谢您提出问题。
编辑:根据OP的建议,有一种方法可以在不使用其他SSE寄存器的情况下完成。
extern foo2(__m128i x, __m128i y);
void foo(__m128i x, __m128i y) {
if (_mm_movemask_epi8(x) | _mm_movemask_epi8(y)) foo2(x,y);
}
海湾合作委员会的相关议会是:
pmovmskb eax, xmm0
pmovmskb edx, xmm1
or edx, eax
jne .L4
这不使用另一个xmm寄存器,而是使用两个标量寄存器。
请注意,较少的指令并不一定意味着更好的性能。哪种解决方案最好?你必须测试每一个才能找到答案。
答案 1 :(得分:1)
如果使用C / C ++,则无法控制各个CPU寄存器。如果要完全控制,则必须使用汇编程序。