SSE (SIMD): умножить вектор на скалярные
Общей операцией, которую я выполняю в своей программе, является масштабирование векторов с помощью скаляра (V * s, например [1,2,3,4] * 2 == [2,4,6,8]). Есть ли инструкция SSE (или AVX) для этого, отличная от первой загрузки скаляра в каждой позиции в векторе (например, _mm_set_ps (2,2,2,2)), а затем умножения?
Вот что я делаю сейчас:
__m128 _scalar = _mm_set_ps(s,s,s,s);
__m128 _result = _mm_mul_ps(_vector, _scalar);
Я ищу что-то вроде...
__m128 _result = _mm_scale_ps(_vector, s);
Ответы
Ответ 1
В зависимости от вашего компилятора вы можете немного улучшить генерации кода, используя _mm_set1_ps
:
const __m128 scalar = _mm_set1_ps(s);
__m128 result = _mm_mul_ps(vector, scalar);
Однако скалярные константы, подобные этому, нужно только инициализировать один раз, вне любых циклов, поэтому стоимость исполнения должна быть неактуальной. (Если скалярное значение не меняется в цикле?)
Как всегда, вы должны посмотреть на код, который генерирует ваш компилятор, а также попробовать запустить свой код под достойным профилировщиком, чтобы увидеть, где именно находятся горячие точки.
Ответ 2
Нет инструкции для умножения вектора на скаляр. Там, однако, некоторые инструкции для загрузки одних и тех же скалярных значений во все позиции в векторном регистре.
Набор инструкций AVX предоставляет _mm_broadcast_ss
/_mm256_broadcast_ss
/_mm256_broadcast_sd
встроенные функции для заполнения регистров SSE и AVX с тем же значением float/double.
В наборе инструкций SSE3 вы можете найти _mm_loaddup_pd
intrinsic, который заполняет регистр SSE с тем же двойным значением.
В других версиях SSE обычно лучше всего загрузить скалярное значение с помощью _mm_load_ss
/_mm_load_sd
, а затем скопировать его ко всем элементам векторного регистра с помощью _mm_shuffle_ps
/_mm_unpacklo_pd
.
Ответ 3
Я не знаю ни одной инструкции, которая делает то, что вы хотите. Действительно ли заданная операция является узким местом? Если вы умножаете большой вектор на одну и ту же константу, время, необходимое для заполнения регистра XMM/YMM четырьмя копиями константы, должно быть очень малой частью общего времени.
Как простая оптимизация, если константа равна 2, как это было в вашем примере, вместо этого вы можете заменить умножение на команду add, не требуя постоянной.