7

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

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

4

3 回答 3

5

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

但是,据我所知,这些指令无论如何都被分解为多条指令,所以我认为除了对 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);

要获得水平总和,您可以这样做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);        
}

我从Agner Fog 的 Vector Class LibraryIntel Instrinsics Guide online获得了所有这些信息和函数。

于 2014-11-13T09:50:00.663 回答
0

我会给 Z boson 支票,因为帖子确实回答了我的问题,但我认为可以改进指令的确切顺序:

inline float horizontal_add(__m512 a) {
    __m512 tmp = _mm512_add_ps(a,_mm512_shuffle_f32x4(a,a,_MM_SHUFFLE(0,0,3,2)));
    __m128 r = _mm512_castps512_ps128(_mm512_add_ps(tmp,_mm512_shuffle_f32x4(tmp,tmp,_MM_SHUFFLE(0,0,0,1))));
    r = _mm_hadd_ps(r,r);
    return _mm_cvtss_f32(_mm_hadd_ps(r,r));
}
于 2014-11-13T23:19:16.313 回答
0

双精度的水平总和:

static inline double _mm512_horizontal_add(__m512d a){
    __m256d b = _mm256_add_pd(_mm512_castpd512_pd256(a), _mm512_extractf64x4_pd(a,1));
    __m128d d = _mm_add_pd(_mm256_castpd256_pd128(b), _mm256_extractf128_pd(b,1));
    double *f = (double*)&d;
    return _mm_cvtsd_f64(d) + f[1];
}

编辑:Peter Cordes 的应用评论

于 2019-09-23T13:12:16.370 回答