Если вы выполняете скалярное произведение более длинных векторов, используйте умножение и регулярный _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
значительно медленнее. (См. таблицы инструкций 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 перетасовки за hadd
, что легко ограничит пропускную способность перетасовки, особенно на процессорах Intel.
r2 = _mm_hadd_ps(r1, r1);
r3 = _mm_hadd_ps(r2, r2);
_mm_store_ss(&result, r3);
person
caf
schedule
08.11.2010