簡體   English   中英

為什么這比memcmp慢

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM