8个32位浮点数的水平总和

Wal*_*ter 3 x86 sse simd avx

如果我有8个打包的32位浮点数(__m256),那么提取所有8个元素的水平和的最快方法是什么?同样,如何获得水平最大值和最小值?换句话说,以下C++函数的最佳实现是什么?

float sum(__m256 x);  ///< returns sum of all 8 elements
float max(__m256 x);  ///< returns the maximum of all 8 elements
float min(__m256 x);  ///< returns the minimum of all 8 elements
Run Code Online (Sandbox Code Playgroud)

Ste*_*non 6

快速记下来(因此未经测试):

float sum(__m256 x) {
    __m128 hi = _mm256_extractf128_ps(x, 1);
    __m128 lo = _mm256_extractf128_ps(x, 0);
    lo = _mm_add_ps(hi, lo);
    hi = _mm_movehl_ps(hi, lo);
    lo = _mm_add_ps(hi, lo);
    hi = _mm_shuffle_ps(lo, lo, 1);
    lo = _mm_add_ss(hi, lo);
    return _mm_cvtss_f32(lo);
}
Run Code Online (Sandbox Code Playgroud)

对于最小/最大,替换_mm_add_ps_mm_add_ss_mm_max_*_mm_min_*.

请注意,这对于一些操作来说是很多工作; AVX并不是真正有效地进行水平操作.如果您可以批量处理多个向量,则可以使用更有效的解决方案.


Z b*_*son 5

虽然斯蒂芬·佳能的答案可能是找到水平最大值/最小值的理想选择,但我认为可以为水平和找到更好的解决方案。

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

  • 请注意,“ VHADDPS”在Sandy Bridge / Ivy Bridge上具有5个周期的延迟,因此这实际上可能比Stephen Canon的实现效率低(所有指令通常为1个周期的延迟)。 (2认同)
  • @PaulR,我承认我过去过于强调指令的数量。现在我会查看整体延迟和吞吐量,并使用 IACA 之类的东西(并在我的应用程序中进行测试)。但无论如何,我想我最初是从 Agner Fog 的 VCL(这就是我学习 SSE 和 AVX)提出的这个解决方案。如果您必须在 Agner Fog 和 Stephen Canon 之间的解决方案上打赌,您会怎么打赌?我想我会抛硬币。 (2认同)
  • @PaulR,恭喜您使用simd黄金标签! (2认同)