使用AVX内部函数在__mm512中求和8位整数

时间:2019-03-22 09:41:53

标签: c x86 simd intrinsics avx

AVX512为我们提供了对__mm512向量中的所有单元求和的内在函数。但是,其中一些对应项缺失:尚无_mm512_reduce_add_epi8

_mm512_reduce_add_ps     //horizontal sum of 16 floats
_mm512_reduce_add_pd     //horizontal sum of 8 doubles
_mm512_reduce_add_epi32  //horizontal sum of 16 32-bit integers
_mm512_reduce_add_epi64  //horizontal sum of 8 64-bit integers

基本上,我需要在以下代码段中实现MAGIC

__m512i all_ones = _mm512_set1_epi16(1);
short sum_of_ones = MAGIC(all_ones);
/* now sum_of_ones contains 32, the sum of 32 ones. */

最明显的方法是使用_mm512_storeu_epi8并将数组的元素加在一起,但这会很慢,而且可能会使缓存无效。我想有一种更快的方法。

同样可以实现_mm512_reduce_add_epi16

1 个答案:

答案 0 :(得分:4)

首先,_mm512_reduce_add_epi64不对应于单个AVX512指令,但它会产生一系列随机播放和加法运算。

要将64个epu8的值减少到8个epi64的值,通常针对零向量使用vpsadbw指令(SAD =绝对差之和),然后可以进一步将其减少:

long reduce_add_epu8(__m512i a)
{
    return _mm512_reduce_add_epi64(_mm512_sad_epu8(a, _mm512_setzero_si512()));
}

尝试使用Godbolt:https://godbolt.org/z/1rMiPH。不幸的是,如果与_mm512_set1_epi16(1)一起使用,则GCC和Clang似乎都无法优化该功能。

对于epi8而不是epu8,您需要首先将128添加到每个元素(或与0x80进行xor),然后使用vpsadbw对其进行缩减,最后减去64*128(或每个中间64位结果上的8*128)。 [请注意,在此答案的先前版本中这是错误的]

对于epi16,我建议看看_mm512_reduce_add_epi32_mm512_reduce_add_epi64产生什么指令并从那里得出要做什么。


总体而言,正如@Mysticial所建议的那样,最佳的减少方法取决于您的上下文。例如,如果您有一个非常大的int64数组,并且希望总和为int64,则应该按包将它们加在一起,并且仅在最后将一个包减少为一个{{1 }}。