[英]Why is this slower than memcmp
我試圖比較兩行pixel
。
甲pixel
被定義為一個struct
含有4個float
值(RGBA)。
我不使用memcmp
的原因是因為我需要返回第一個不同像素的位置, memcmp
不會這樣做。
我的第一個實現使用SSE
內在函數,比memcmp
慢約30%:
inline int PixelMemCmp(const Pixel* a, const Pixel* b, int count)
{
for (int i = 0; i < count; i++)
{
__m128 x = _mm_load_ps((float*)(a + i));
__m128 y = _mm_load_ps((float*)(b + i));
__m128 cmp = _mm_cmpeq_ps(x, y);
if (_mm_movemask_ps(cmp) != 15) return i;
}
return -1;
}
然后我發現將值視為整數而不是浮點數加速了一些事情,現在比memcmp
慢了約20%。
inline int PixelMemCmp(const Pixel* a, const Pixel* b, int count)
{
for (int i = 0; i < count; i++)
{
__m128i x = _mm_load_si128((__m128i*)(a + i));
__m128i y = _mm_load_si128((__m128i*)(b + i));
__m128i cmp = _mm_cmpeq_epi32(x, y);
if (_mm_movemask_epi8(cmp) != 0xffff) return i;
}
return -1;
}
從我在其他問題上看到的內容, memcmp
的MS實現也是使用SSE
實現的。 我的問題是,MS實現的其他技巧是什么呢?我不這樣做? 即使它進行逐字節比較,它仍然如何更快?
對齊是一個問題嗎? 如果pixel
包含4個浮點數,則不會在16字節邊界上分配像素數組?
我正在使用/o2
和所有優化標志進行編譯。
您可能想要檢查此memcmp SSE實現 ,特別是__sse_memcmp
函數,它從一些健全性檢查開始,然后檢查指針是否對齊:
aligned_a = ( (unsigned long)a & (sizeof(__m128i)-1) );
aligned_b = ( (unsigned long)b & (sizeof(__m128i)-1) );
如果它們沒有對齊,則逐字節比較指針,直到對齊地址的開始:
while( len && ( (unsigned long) a & ( sizeof(__m128i)-1) ) )
{
if(*a++ != *b++) return -1;
--len;
}
然后將剩余內存與SSE指令進行比較,類似於您的代碼:
if(!len) return 0;
while( len && !(len & 7 ) )
{
__m128i x = _mm_load_si128( (__m128i*)&a[i]);
__m128i y = _mm_load_si128( (__m128i*)&b[i]);
....
我用SSE(和MMX / 3DNow!)編寫了strcmp / memcmp優化,第一步是確保數組盡可能對齊 - 你可能會發現你必須做第一個和/或最后一個字節“一個一次“。
如果您可以在數據進入循環之前對齊[如果您的代碼進行了分配],那么這是理想的。
第二部分是展開循環,所以你不會得到這么多“如果循環不在最后,跳回循環的開頭” - 假設循環很長。
您可能會發現在執行“我們現在離開”條件之前預加載輸入的下一個數據也有幫助。
編輯:最后一段可能需要一個例子。 此代碼假定至少有兩個展開的循環:
__m128i x = _mm_load_si128((__m128i*)(a));
__m128i y = _mm_load_si128((__m128i*)(b));
for(int i = 0; i < count; i+=2)
{
__m128i cmp = _mm_cmpeq_epi32(x, y);
__m128i x1 = _mm_load_si128((__m128i*)(a + i + 1));
__m128i y1 = _mm_load_si128((__m128i*)(b + i + 1));
if (_mm_movemask_epi8(cmp) != 0xffff) return i;
cmp = _mm_cmpeq_epi32(x1, y1);
__m128i x = _mm_load_si128((__m128i*)(a + i + 2));
__m128i y = _mm_load_si128((__m128i*)(b + i + 2));
if (_mm_movemask_epi8(cmp) != 0xffff) return i + 1;
}
大概是這樣的。
我無法直接幫助你,因為我正在使用Mac,但有一種簡單的方法可以找出發生的情況:
您只需在調試模式下進入memcpy並切換到反匯編視圖。 由於memcpy是一個簡單的小函數,您將輕松找出所有實現技巧。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.