より長いベクトルの内積を実行している場合は_mm_add_ps
、内側のループ内で乗算と通常(またはFMA)を使用します。 水平方向の合計を最後まで保存します。
ただし、SIMDベクトルの1つのペアだけの内積を実行している場合:
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);
次に、x86で水平フロートベクトル合計を実行するための最速の方法をr1
使用した水平合計(これのコメントバージョンと、それがより高速である理由については、そこを参照してください)。
__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);
遅い代替手段は、1つあたり2シャッフルのコストがかかりますhadd
。これは、特にIntel CPUで、シャッフルスループットのボトルネックになりやすくなります。
r2 = _mm_hadd_ps(r1, r1);
r3 = _mm_hadd_ps(r2, r2);
_mm_store_ss(&result, r3);