如果您正在做较长向量的点积,_mm_add_ps
请在内部循环中使用乘法和正则(或 FMA)。 将水平总和保存到最后。
但是,如果您只对一对 SIMD 向量进行点积:
GCC(至少 4.3 版)包括<smmintrin.h>
SSE4.1 级别的内在函数,包括单精度和双精度点积:
_mm_dp_ps (__m128 __X, __m128 __Y, const int __M);
_mm_dp_pd (__m128d __X, __m128d __Y, const int __M);
在 Intel 主流 CPU(不是 Atom/Silvermont)上,这些比使用多条指令手动执行要快一些。
但是在 AMD(包括 Ryzen)上,dpps
速度要慢得多。(参见Agner Fog 的说明表)
作为旧处理器的后备方案,您可以使用此算法来创建向量a
和的点积b
:
__m128 r1 = _mm_mul_ps(a, b);
然后水平求和r1
使用最快的方式在 x86 上进行水平浮点向量求和(参见那里的评论版本,以及为什么它更快。)
__m128 shuf = _mm_shuffle_ps(r1, r1, _MM_SHUFFLE(2, 3, 0, 1));
__m128 sums = _mm_add_ps(r1, shuf);
shuf = _mm_movehl_ps(shuf, sums);
sums = _mm_add_ss(sums, shuf);
float result = _mm_cvtss_f32(sums);
一个缓慢的替代方案每 2 次 shuffle hadd
,这很容易成为 shuffle 吞吐量的瓶颈,尤其是在 Intel CPU 上。
r2 = _mm_hadd_ps(r1, r1);
r3 = _mm_hadd_ps(r2, r2);
_mm_store_ss(&result, r3);