使用 AVX 内在函数对 __m512i 中的 8 位整数求和
Summing 8-bit integers in __m512i with AVX intrinsics
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
的奖励积分。
首先,_mm512_reduce_add_epi64
并不对应单条AVX512指令,而是生成了一系列的shuffle和additions。
要将 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
异或),然后使用 vpsadbw
减少它并在end subtract 64*128
(或每个中间 64 位结果上的 8*128
)。 [请注意,在此答案的先前版本中这是错误的]
对于 epi16
,我建议查看 _mm512_reduce_add_epi32
和 _mm512_reduce_add_epi64
生成的指令,并从那里导出要执行的操作。
总的来说,正如@Mysticial 所建议的,这取决于您的上下文,减少的最佳方法是什么。例如,如果你有一个非常大的 int64
数组并且想要一个总和为 int64
,你应该将它们逐个添加到一起,并且只在最后将一个数据包减少到一个 [=25] =].
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
的奖励积分。
首先,_mm512_reduce_add_epi64
并不对应单条AVX512指令,而是生成了一系列的shuffle和additions。
要将 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)
.
对于 epi8
而不是 epu8
,您需要首先将 128 添加到每个元素(或与 0x80
异或),然后使用 vpsadbw
减少它并在end subtract 64*128
(或每个中间 64 位结果上的 8*128
)。 [请注意,在此答案的先前版本中这是错误的]
对于 epi16
,我建议查看 _mm512_reduce_add_epi32
和 _mm512_reduce_add_epi64
生成的指令,并从那里导出要执行的操作。
总的来说,正如@Mysticial 所建议的,这取决于您的上下文,减少的最佳方法是什么。例如,如果你有一个非常大的 int64
数组并且想要一个总和为 int64
,你应该将它们逐个添加到一起,并且只在最后将一个数据包减少到一个 [=25] =].