有/没有SSE simd操作的结果是不同的
result with/without SSE simd operation is different
我正在尝试对数组(无符号字符(的所有元素求和
但cv::Mat sum的结果与SSE的结果(代码下方(不同
有了sse,数组结果之和比没有大,但为什么??
ex(我得到了2042115的sse sum,但cv::mat的sum结果是2041104。
__m128i srcVal;
__m128i src16bitlo;
__m128i src16bithi;
__m128i src32bitlolo;
__m128i src32bitlohi;
__m128i src32bithilo;
__m128i src32bithihi;
__m128i vsum = _mm_setzero_si128();
for (int i = 0; i < nSrcSize; i += 16)
{
srcVal = _mm_loadu_si128((__m128i*) (pSrc + i));
src16bitlo = _mm_unpacklo_epi8(srcVal, _mm_setzero_si128());
src16bithi = _mm_unpackhi_epi8(srcVal, _mm_setzero_si128());
src32bitlolo = _mm_unpacklo_epi16(src16bitlo, _mm_setzero_si128());
src32bitlohi = _mm_unpackhi_epi16(src16bitlo, _mm_setzero_si128());
src32bithilo = _mm_unpacklo_epi16(src16bithi, _mm_setzero_si128());
src32bithihi = _mm_unpackhi_epi16(src16bithi, _mm_setzero_si128());
vsum = _mm_add_epi32(src32bitlolo, vsum);
vsum = _mm_add_epi32(src32bitlohi, vsum);
vsum = _mm_add_epi32(src32bithilo, vsum);
vsum = _mm_add_epi32(src32bithihi, vsum);
// cout << "sumSrc : " << sumSrc << endl;
}
int sumSrc = vsum.m128i_i32[0] + vsum.m128i_i32[1] + vsum.m128i_i32[2] + vsum.m128i_i32[3];
//int check = sumSrc;
int remainSize = nSrcSize % 16;
if (remainSize > 0)
{
unsigned char* arrTemp = new unsigned char[16](); // 0으로 초기화
memcpy(arrTemp, pSrc + nSrcSize - remainSize -1, remainSize);
__m128i srcVal = _mm_loadu_si128((__m128i*)arrTemp);
vsum = _mm_sad_epu8(srcVal, _mm_setzero_si128());
sumSrc += vsum.m128i_i16[0] + vsum.m128i_i16[1] + vsum.m128i_i16[2] + vsum.m128i_i16[3] + vsum.m128i_i16[4] + vsum.m128i_i16[5] + vsum.m128i_i16[6] + vsum.m128i_i16[7];
}
您有两个错误:
当最终向量延伸超过nSrcSize
时,i < nSrcSize
可能为真。由于您已经在使用带符号的int i
,因此可以使用i < nSrcSize - 15
查找最高的i
值,该值可以加载从i+0
到i+15
的完整16个字节。如果使用size_t,则使用nSrcSize & -16U
。
new unsigned char[16]()
不会将内存归零,因此您正在对一些额外的垃圾进行求和。你不需要new
,但你忘了删除它,所以你泄露了内存!可以使用本地数组,而不动态分配任何内容。
alignas(16) unsigned char arrTemp[16] = {0}; // implicitly initializes later elements to 0
但是可变大小的memcpy对效率来说并不是很好,并且重新加载memcpy结果将导致存储转发停滞。OTOH,你可以只做_mm_add_epi32(vsum, cleanup_sad)
,只做一个水平向量和。
更有效的方法可能是在大小上进行分支(而不是将工作传递给memcpy(,并使用SIMD负载进行8字节和4字节的块。或者,一旦剩余的字节少于8个,就进行一次8字节的加载,该加载不会越过缓存线边界来获得所有字节。
检查从您想要的第一个字节开始的加载是否会越过64字节的边界。如果没有,请执行此操作并将64位左移到零,这样可以安全地进行hsum。如果是,则加载,使以所需的最后一个字节结束,然后右移。您必须将移位计数计算为8 * (8 - bytes_to_keep)
。可以使用标量移位,然后将_mm_cvtsi64_si128
转换为SIMD矢量,也可以直接使用_mm_loadl_epi64
(movq
(并使用SIMD移位。(不幸的是,SSE/AVX没有可变计数字节移位,只有位移位,计数需要在另一个SIMD矢量中。(
FYI,psadbw
对零将无符号字符的向量水平求和为两个q字,这比SIMD循环效率高得多。水平求和SSE无符号字节矢量的最快方法。另请参阅如何使用SIMD计算字符出现次数。在外循环中使用SIMD将字节向量累积为具有更宽元素的SIMD向量。
您已经在清理中使用了psadbw
,但您正在将所有8个16位元素相加,尽管其中6个元素为零。
- Fmod 函数清楚地输出一个预期的双精度值,但 if(fmod == 预期的双精度值)的计算结果不是 true
- std::chrono::duration::count函数的实际结果类型是什么
- 术语的计算结果不是采用0个参数的函数
- 错误 C2064:术语的计算结果不是采用 3 个参数的函数
- C++:术语的计算结果不是采用 1 个参数的函数
- ZTV,ZTS,ZTI 在 gdb x/nfu "vtable_address" 的结果中是什么意思?
- 错误 C2064:term 的计算结果不是采用 1 个参数的函数 - 关于线程的一些东西
- 如何打印返回值的结果?我是一个初学者学习C++
- 即使在C 中越过初始化阵列的极限后,结果也是如此
- 使用FP:快速导致错误的VC 结果(不仅仅是不准确)结果 - 这是编译器错误
- 我收到此错误:错误 c2064:term 的计算结果不是采用 0 个参数的函数,但我不明白为什么
- C++时间()给我的结果几乎是随机的
- 三元运算符的结果不是右值
- 为什么"decltype(i+j)"的结果不是右值引用?
- 简单的代码,看似随机的结果——这是由于过时的引用造成的吗
- wxwidgets Bind()项的计算结果不是在event.h中使用1个参数的函数
- 我正在尝试用C++制作一个费用计算器,我得到了错误C2064:term的计算结果不是一个带1个参数的函数
- 取消引用无效指针但不使用结果是否是C++中的未定义行为
- 字符* 到字符串 c++(结果不是预期的)
- 错误C2064:术语的计算结果不是一个带1个参数的函数,我该如何克服这个错误