使用 __m512 (AVX512) 水平添加

Rou*_*lan 6 simd intrinsics avx512

如何使用 512 位 AVX 寄存器中的浮点数有效地执行水平加法(即将单个向量中的项加在一起)?对于 128 和 256 位寄存器,这可以使用 _mm_hadd_ps 和 _mm256_hadd_ps 来完成,但没有 _mm512_hadd_ps。Intel 内在函数指南文档 _mm512_reduce_add_ps。它实际上并不对应于单个指令,但它的存在表明存在一种最佳方法,但它似乎没有在 GCC 最新快照附带的头文件中定义,我找不到它与谷歌。

我认为可以使用 _mm512_shuffle_ps 和 _mm512_add_ps 模拟“hadd”,或者我可以使用 _mm512_extractf32x4_ps 将 512 位寄存器分解为四个 128 位寄存器,但我想确保我不会错过更好的东西。

Z b*_*son 6

INTEL 编译器定义了以下内部函数来进行水平求和

_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
Run Code Online (Sandbox Code Playgroud)

然而,据我所知,无论如何,这些都被分解为多个指令,所以我认为除了对 AVX512 寄存器的上部和下部进行水平求和之外,你不会获得任何其他东西。

__m256 low  = _mm512_castps512_ps256(zmm);
__m256 high = _mm256_castpd_ps(_mm512_extractf64x4_pd(_mm512_castps_pd(zmm),1));

__m256d low  = _mm512_castpd512_pd256(zmm);
__m256d high = _mm512_extractf64x4_pd(zmm,1);

__m256i low  = _mm512_castsi512_si256(zmm);
__m256i high = _mm512_extracti64x4_epi64(zmm,1);
Run Code Online (Sandbox Code Playgroud)

要获得水平总和,您可以执行以下操作sum = horizontal_add(low + high)

static inline float horizontal_add (__m256 a) {
    __m256 t1 = _mm256_hadd_ps(a,a);
    __m256 t2 = _mm256_hadd_ps(t1,t1);
    __m128 t3 = _mm256_extractf128_ps(t2,1);
    __m128 t4 = _mm_add_ss(_mm256_castps256_ps128(t2),t3);
    return _mm_cvtss_f32(t4);        
}

static inline double horizontal_add (__m256d a) {
    __m256d t1 = _mm256_hadd_pd(a,a);
    __m128d t2 = _mm256_extractf128_pd(t1,1);
    __m128d t3 = _mm_add_sd(_mm256_castpd256_pd128(t1),t2);
    return _mm_cvtsd_f64(t3);        
}
Run Code Online (Sandbox Code Playgroud)

我从Agner Fog 的矢量类库Intel Instrinsics Guide online获得了所有这些信息和函数。

  • 我通常更喜欢使用直接的“reduce_add”内在函数,因为它清楚地向代码的人类读者和编译器表达了意图,当编译器知道您真正想要做什么时,通常可以更好地优化。 (3认同)
  • @AlexReinking:是的,编译器将它们扩展为不会低效使用“vhaddps”/“pd”(每个 3 uops)的模式。例如 https://godbolt.org/z/PboP3aneK 显示使用 `vpshufd` / `vpaddq` 之类的东西。请参阅[进行水平 SSE 向量和(或其他缩减)的最快方法](/sf/ask/489773511/) 和 [使用 AVX512 或 AVX2 计算所有打包 32 位整数之和的最快方法](https ://stackoverflow.com/q/60108658)它查看 GCC/clang 的 asm 输出。他们都很好。 (2认同)