以可移植的方式访问 __m128i 变量的字段

Accessing the fields of a __m128i variable in a portable way

我正在尝试使用 SIMD 指令来加速 uint8_t 数组中元素的总和(即,求和)。为此,我复制了这个问题中投票最多的答案:

Sum reduction of unsigned bytes without overflow, using SSE2 on Intel

该答案中显示的总和减少程序是这样的:

uint16_t sum_32(const uint8_t a[32])
{
    __m128i zero = _mm_xor_si128(zero, zero);
    __m128i sum0 = _mm_sad_epu8(
                        zero,
                        _mm_load_si128(reinterpret_cast<const __m128i*>(a)));
    __m128i sum1 = _mm_sad_epu8(
                        zero,
                        _mm_load_si128(reinterpret_cast<const __m128i*>(&a[16])));
    __m128i sum2 = _mm_add_epi16(sum0, sum1);
    __m128i totalsum = _mm_add_epi16(sum2, _mm_shuffle_epi32(sum2, 2));
    return totalsum.m128i_u16[0];
}

我的问题是 return 操作 (totalsum.m128i_u16[0]) 似乎只适用于 Microsoft,但我使用的是基于 UNIX 的平台。

我查看了 SIMD 内部函数列表,函数 _mm_storeu_ps(a, t) 似乎做的事情与我要求的类似,但 t 必须是一个 __m128 变量和一个 a浮动。我试图通过将我的结果从 __m128i 转换为 __m128 来使用该函数,但它没有用。

是否有另一种方法可以检索 __m128i 变量的前 16 位并将它们存储到 uint16_t 变量中?我是 SIMD 编程的新手。

顺便说一句,有没有更好的解决方案来实现和减少?。这个答案是9年前的。我想现在是更好的选择。

_mm_extract_epi16 compile-time 已知索引。

对于第一个元素_mm_cvtsi128_si32给出了更有效的指令。这会起作用,因为:

  • _mm_sad_epu8 将位 16 到 63 填充为零
  • 您通过 uint16_t return type
  • 将结果截断为 16 位

编译器可能会根据其中一个原因自行进行此优化,但不是全部,因此最好使用 _mm_cvtsi128_si32.