Если вы создаете скалярное произведение длинных векторов, используйте умножение и регулярное значение _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 (не Atom / Silvermont) они выполняются несколько быстрее, чем при ручном выполнении нескольких инструкций.
Но на AMD (включая Ryzen) dpps
значительно медленнее. (См. Таблицы инструкций Агнера Фога )
В качестве запасного варианта для более старых процессоров вы можете использовать этот алгоритм для создания точечного произведения векторов 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 тасов за hadd
, что легко ограничивает пропускную способность тасования, особенно на процессорах Intel.
r2 = _mm_hadd_ps(r1, r1);
r3 = _mm_hadd_ps(r2, r2);
_mm_store_ss(&result, r3);