我從這個問題中學到了一些有用的東西。讓我們先來看看一些標碼
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
注意,這需要一個指令,因爲包裝的逐位或不設置零標誌。還要注意,標量版本和SIMD版本都需要使用額外的寄存器(在標量情況下爲eax
,在SIMD情況下爲xmm2
)。 所以要回答你的問題,你現在的解決方案是最好的。
但是,如果您沒有使用SSE4.1或更高版本的處理器,則必須使用_mm_movemask_epi8
。其中僅需要SSE2另一種替代方法是使用_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);
}
從GCC相關組件是:
pmovmskb eax, xmm0
pmovmskb edx, xmm1
or edx, eax
jne .L4
使用另一XMM寄存器此使用兩個標量寄存器代替。
請注意,較少的指令並不一定意味着更好的性能。哪種解決方案最好?你必須測試他們每個人找出。
你還能指望什麼?充其量不用或。 – 2014-10-29 10:24:45
我會使用'_mm_movemask_epi8'而不是'_mm_testz_si128',但實際上'_mm_testz_si128'通常更好。只有Nahalem和Westmere的'_mm_movemask_epi8'具有較低的延遲。但Haswell更糟糕。但更重要的是它不會在FLAGS寄存器中設置零或進位標誌,而是'_mm_testz_si128'。所以你現在擁有的可能是最好的。 – 2014-10-29 19:57:58
其實這是我正在尋找的討論類型。我會將其標記爲答案,但這是一條評論。 – ChipK 2014-10-29 23:31:10