3

の2行を比較しようとしていますpixel

Aは、4つの値(RGBA)を含むpixelものとして定義されます。structfloat

私が使用していない理由memcmpは、1番目の異なるピクセルの位置を返す必要があるためmemcmpです。

私の最初の実装はSSE組み込み関数を使用しており、以下よりも約30%遅くなりますmemcmp

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;
}

次に、値をfloatではなく整数として扱うと、処理速度が少し速くなり、現在は。よりもわずか20%遅くなっていることがわかりましたmemcmp

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;
}

他の質問で読んだことから、のMS実装memcmpもを使用して実装されてSSEいます。私の質問は、MSの実装には、私が持っていない他のトリックがありますか?バイトごとの比較を行っても、それでもどのように高速ですか?

アラインメントは問題ですか?に4つの浮動小数点数が含まれている場合、pixelピクセルの配列はすでに16バイト境界に割り当てられていませんか?

/o2とすべての最適化フラグをコンパイルしています。

4

3 に答える 3

3

このmemcmpSSE実装、特に__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]);
....
于 2013-02-10T11:02:39.090 に答える
3

私はSSE(およびMMX / 3DNow!)を使用してstrcmp / memcmp最適化を作成しました。最初のステップは、配列が可能な限り整列されるようにすることです。最初または最後のバイト、あるいはその両方を実行する必要がある場合があります。一度に」。

ループに到達する前にデータを整列させることができれば(コードが割り当てを行う場合)、それは理想的です。

2番目の部分はループを展開することです。そのため、ループが非常に長いと仮定すると、「ループが最後にない場合は、ループの最初に戻る」ということはあまりありません。

「今すぐ残す」条件を実行する前に、入力の次のデータをプリロードすることも役立つ場合があります。

編集:最後の段落には例が必要な場合があります。このコードは、少なくとも2つの展開されたループを想定しています。

 __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; 
}

大まかにそのようなもの。

于 2013-02-10T11:25:28.527 に答える
0

私はMacを使用しているため、直接あなたを助けることはできませんが、何が起こるかを理解する簡単な方法があります。

デバッグモードでmemcpyにステップインし、逆アセンブリビューに切り替えます。memcpyは単純な小さな関数なので、すべての実装のコツを簡単に理解できます。

于 2013-02-10T10:12:57.250 に答える