如果您要计算较长向量的点积,请在内循环中使用乘法和正则 _mm_add_ps(或 FMA)。 将水平和保存到最后。
但如果您只对一对 SIMD 向量进行点积:
GCC(至少 4.3 版)包含具有 SSE4.1 级别内在函数的 <smmintrin.h>,包括单精度和双精度点积:
_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's instruction tables)
作为旧处理器的后备方案,您可以使用此算法创建向量 a 和 b 的点积:
__m128 r1 = _mm_mul_ps(a, b);
然后水平求和 r1 使用 Fastest way to do horizontal float vector sum on 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);
一个缓慢的替代方案每个hadd 需要 2 次 shuffle,这很容易成为 shuffle 吞吐量的瓶颈,尤其是在 Intel CPU 上。
r2 = _mm_hadd_ps(r1, r1);
r3 = _mm_hadd_ps(r2, r2);
_mm_store_ss(&result, r3);