SIMD/SSE:短点积和短最大值

SIMD/SSE : short dot product and short max value

本文关键字:最大值 SIMD SSE      更新时间:2023-10-16

我正在尝试优化两个c样式数组的点积,这两个数组的大小都很小,类型也很短。

我已经阅读了一些关于SIMD本质的文档,以及许多关于使用这个本质优化点产品的博客文章。

然而,我不明白使用这种内部函数的短数组上的点积如何能给出正确的结果。在制作点积时,计算出的值可以(在我的情况下总是)大于SHORT_MAX,所以有和。因此,我将它们存储在double类型的变量中。

正如我所理解的使用simd内在的点积,我们使用__m128i变量类型,运算返回__m128i。所以,我不明白的是,为什么它不"溢出",以及如何将结果转换为可以处理它的值类型?

感谢您的建议

根据数据值的范围,您可以使用诸如_mm_madd_epi16之类的内部函数,该函数对16位数据执行乘法/加法运算,并生成32位项。然后,您需要周期性地将32位项累积为64位。你需要多久做一次取决于你的输入数据的范围,例如,如果它是12位灰度图像数据,那么在存在溢出的可能性之前,你可以在每次迭代8个元素(即512个输入点)的情况下进行64次迭代。然而,在最坏的情况下,如果您的输入数据使用完整的16位范围,那么您需要在每次迭代(即每8点)中进行额外的64位累加。

仅就记录而言,以下是我如何为2个36大小的int16数组制作点积:

double dotprod(const int16_t* source, const int16_t* target, const int size){
#ifdef USE_SSE
    int res[4];
    __m128i* src = (__m128i *) source;
    __m128i* t = (__m128i *) target;
    __m128i s = _mm_madd_epi16(_mm_loadu_si128(src), mm_loadu_si128(t));
    ++src;
    ++t;
    s = _mm_add_epi32(s, _mm_madd_epi16(_mm_loadu_si128(src), _mm_loadu_si128(t)));
    ++src;
    ++t;
    s = _mm_add_epi32(s, _mm_madd_epi16(_mm_loadu_si128(src), _mm_loadu_si128(t)));
    ++src;
    ++t;
    s = _mm_add_epi32(s, _mm_madd_epi16(_mm_loadu_si128(src), _mm_loadu_si128(t)));
    /* return the sum of the four 32-bit sub sums */
    _mm_storeu_si128((__m128i*)&res, s);
    return res[0] + res[1] + res[2] + res[3] + source[32] * target[32] + source[33] * target[33] + source[34] * target[34] + source[35] * target[35];
#elif USE_AVX
    int res[8];
    __m256i* src = (__m256i *) source;
    __m256i* t = (__m256i *) target;
    __m256i s = _mm256_madd_epi16(_mm256_loadu_si256(src), _mm256_loadu_si256(t));
    ++src;
    ++t;
    s = _mm256_add_epi32(s, _mm256_madd_epi16(_mm256_loadu_si256(src), _mm256_loadu_si256(t)));
    /* return the sum of the 8 32-bit sub sums */
    _mm256_storeu_si256((__m256i*)&res, s);
    return res[0] + res[1] + res[2] + res[3] + res[4] + res[5] + res[6] + res[7] + source[32] * target[32] + source[33] * target[33] + source[34] * target[34] + source[35] * target[35];
#else
    return source[0] * target[0] + source[1] * target[1] + source[2] * target[2] + source[3] * target[3] + source[4] * target[4]+ source[5] * target[5] + source[6] * target[6] + source[7] * target[7] + source[8] * target[8] + source[9] * target[9] + source[10] * target[10] + source[11] * target[11] + source[12] * target[12] + source[13] * target[13] + source[14] * target[14] + source[15] * target[15] + source[16] * target[16] + source[17] * target[17] + source[18] * target[18] + source[19] * target[19] + source[20] * target[20] + source[21] * target[21] + source[22] * target[22] + source[23] * target[23] + source[24] * target[24] + source[25] * target[25] + source[26] * target[26] + source[27] * target[27] + source[28] * target[28] + source[29] * target[29] + source[30] * target[30] + source[31] * target[31] + source[32] * target[32] + source[33] * target[33] + source[34] * target[34] + source[35] * target[35];
#endif
}