我有8个AVX向量,每个向量包含8个浮点数(总共64个浮点数),我想将每个向量中的元素相加在一起(基本上执行8个水平求和)。
目前,我正在使用以下代码:
__m256 HorizontalSums(__m256 v0, __m256 v1, __m256 v2, __m256 v3, __m256 v4, __m256 v5, __m256 v6, __m256 v7)
{
// transpose
const __m256 t0 = _mm256_unpacklo_ps(v0, v1);
const __m256 t1 = _mm256_unpackhi_ps(v0, v1);
const __m256 t2 = _mm256_unpacklo_ps(v2, v3);
const __m256 t3 = _mm256_unpackhi_ps(v2, v3);
const __m256 t4 = _mm256_unpacklo_ps(v4, v5);
const __m256 t5 = _mm256_unpackhi_ps(v4, v5);
const __m256 t6 = _mm256_unpacklo_ps(v6, v7);
const __m256 t7 = _mm256_unpackhi_ps(v6, v7);
__m256 v = _mm256_shuffle_ps(t0, t2, 0x4E);
const __m256 tt0 = _mm256_blend_ps(t0, v, 0xCC);
const __m256 tt1 = _mm256_blend_ps(t2, v, 0x33);
v = _mm256_shuffle_ps(t1, t3, 0x4E);
const __m256 tt2 = _mm256_blend_ps(t1, v, 0xCC);
const __m256 tt3 = _mm256_blend_ps(t3, v, 0x33);
v = _mm256_shuffle_ps(t4, t6, 0x4E);
const __m256 tt4 = _mm256_blend_ps(t4, v, 0xCC);
const __m256 tt5 = _mm256_blend_ps(t6, v, 0x33);
v = _mm256_shuffle_ps(t5, t7, 0x4E);
const __m256 tt6 = _mm256_blend_ps(t5, v, 0xCC);
const __m256 tt7 = _mm256_blend_ps(t7, v, 0x33);
// compute sums
__m256 sum0 = _mm256_add_ps(_mm256_add_ps(tt0, tt1), _mm256_add_ps(tt2, tt3));
__m256 sum1 = _mm256_add_ps(_mm256_add_ps(tt4, tt5), _mm256_add_ps(tt6, tt7));
v0 = _mm256_blend_ps(sum0, sum1, 0xF0);
v1 = _mm256_permute2f128_ps(sum0, sum1, 0x21); // final inter-lane shuffling
return _mm256_add_ps(v0, v1);
}
你可以看到,我只是把向量转置并在最后求和元素。这里我已经使用了两个技巧:在可能的情况下用_mm256_blend_ps替换_mm256_shuffle_ps,以减少英特尔CPU上5号端口的压力,并且在最后使用_mm256_permute2f128_ps + _mm256_blend_ps来执行跨通道混洗。
有没有更好(更快)的计算方法?