Подтвердить что ты не робот

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);
4b9b3361

Ответ 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, не требуя постоянной.