5

バイト値を合計するための SSE コードを作成しました。(VS2005.)

それは十分に単純であるため、非常にうまく機能します(そして高速です)。一部のサイズのアレイでのみクラッシュが発生します。そして、リリースモードでのみクラッシュします-デバッグでは決してクラッシュしません。多分誰かが「明らかな」バグを見ますか?どんな助けでも感謝します。

__int64 Sum (const unsigned char* pData, const unsigned int& nLength)
{
    __int64 nSum (0);

    __m128i* pp = (__m128i*)pData;

    ATLASSERT( ( (DWORD)pp & 15 ) == 0 ); // pointer must point to address multiple of 16 (cache line)

    __m128i zero = _mm_setzero_si128(),
        a, b, c, d, tmp;

    unsigned int i (0);

    for ( ; i < nLength; i+=64) // 4-fach loop-unroll (x 16)
    {
        a = _mm_sad_epu8( *(pp++), zero);           
        b = _mm_sad_epu8( *(pp++), zero);  // It crashes here.
        c = _mm_sad_epu8( *(pp++), zero);
        d = _mm_sad_epu8( *(pp++), zero);

        // commenting the following line prevents the crash (???)
        tmp = _mm_add_epi64( _mm_add_epi64( _mm_add_epi64( a, b ), c ), d);

        a = _mm_srli_si128 ( tmp, 8 );

        nSum += _mm_cvtsi128_si32( a ) + _mm_cvtsi128_si32( tmp );
    }

    // ... the rest
    if (nLength % 64)
        for (i -= 64; i < nLength; i++)
            nSum += pData [i];

    return nSum;
}

関数は次のように呼び出されます。

unsigned int nLength = 3571653;  // One of the values that causes crash
unsigned char *pData = (unsigned char*) _aligned_malloc(nLength, 16);
Sum (pData,  nLength);
4

3 に答える 3

6

for ループは次のように定義する必要があります。

for ( ; i < (nLength - 63); i+=64)

基本的に、nLength 120 の配列を渡すと想像してください。最初の実行では問題ありません。i は 64 になりました。i < 120 なので、別のループを実行します。残念ながら、128 に到達する前に配列の末尾を通過し、未定義の動作領域に入ります。これは、クラッシュの原因となるアクセス違反 (0xC0000005) として現れる可能性があります。

ここで、nLength=128 の例を取り上げます。これは、最適化されたループで、提案された変更を加えて完全に実行されるはずです。最初のループ i は問題なく、i = 64 です。i は 65 未満なので、別のループが実行されます。i は 128 になり、ループは終了します。i == nLength であるため、外側のループも実行されません。仕事完了:)

于 2013-03-19T21:31:59.070 に答える
4

リクエストに応じて、「ループ内に 4 つのアキュムレータがあり、ループ後にそれらを合計する」と言ったときに心に留めていたことを以下に示します。

__int64 Sum (const unsigned char* pData, const int& nLength)
{
    __int64 nSum (0);

    __m128i* pp = (__m128i*)pData;


    __m128i zero = _mm_setzero_si128(),
        a = _mm_setzero_si128(),
        b = _mm_setzero_si128(),
        c = _mm_setzero_si128(),
        d = _mm_setzero_si128(), tmp;

    int i (0);

    for ( ; i < (nLength - 63); i+=64)
    {
        a = _mm_add_epi64( _mm_sad_epu8( *(pp++), zero ), a );
        b = _mm_add_epi64( _mm_sad_epu8( *(pp++), zero ), b );
        c = _mm_add_epi64( _mm_sad_epu8( *(pp++), zero ), c );
        d = _mm_add_epi64( _mm_sad_epu8( *(pp++), zero ), d );
    }

    tmp = _mm_add_epi64( _mm_add_epi64( a, b ), _mm_add_epi64( c, d ));
    tmp = _mm_add_epi64( _mm_srli_si128( tmp, 8 ), tmp );
    nSum = (_mm_cvtsi128_si32( tmp ) & 0xFFFFFFFFULL) + 
               (((__int64)_mm_cvtsi128_si32( _mm_srli_si128( tmp, 4 ) )) << 32);

    // ... the rest
    for (; i < nLength; i++)
        nSum += pData [i];

    return nSum;
}
于 2013-03-20T10:02:33.967 に答える