我编写了用于汇总字节值的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);
答案 0 :(得分:6)
您的for循环需要定义如下:
for ( ; i < (nLength - 63); i+=64)
基本上想象你传入一个nLength 120的数组。你在第一次运行时没问题。我现在等于64.我&lt; 120所以你做另一个循环。不幸的是,在达到128之前传递数组的末尾并进入未定义的行为区域。这可能表现为访问冲突(0xC0000005),这会导致您崩溃。
现在以nLength = 128为例,它应该在你的优化循环中完美运行我的建议修改。第一个循环我很好,i = 64.我小于65,所以执行另一个循环。我现在等于128并循环退出。外部循环也不会运行,因为i == nLength。完成工作:))
答案 1 :(得分: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;
}
答案 2 :(得分:0)
我自己正在研究这种东西,我发现harold's answer对我现在正在做的类似事情非常有帮助。但是,我想提出的另一个建议是,如果你的应用程序没有太多麻烦,你是否考虑在最后用零填充你的数据,这样你就不需要在结束时执行这项额外的工作了。你的功能?