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

SIMD/SSE : short dot product and short max value

我正在尝试优化两个 c 样式数组的点积,这些数组的大小为 contant 和 small,类型为 short。

我阅读了一些关于 SIMD 内在函数的文档和许多关于使用此内在函数进行点积优化的博客 posts/articles。

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

据我了解使用 simd intrinsic 的点积,我们使用 __m128i 变量类型和操作返回 __m128i。所以,我不明白的是为什么它不 "overflow" 以及如何将结果转换为可以处理它的值类型?

感谢您的建议

根据数据值的范围,您可能会使用诸如 _mm_madd_epi16 之类的内在函数,它对 16 位数据执行 multiply/add 并生成 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
}