Как векторизовать расчет расстояния с помощью SSE2

А и В являются векторами или длиной N, где N может быть в диапазоне от 20 до 200, скажем.
Я хочу вычислить квадрат расстояния между этими векторами,
то есть d ^ 2 = || A-B || ^ 2.

Пока что у меня есть:

float* a = ...;
float* b = ...;
float d2 = 0;

for(int k = 0; k < N; ++k)
{
float d = a[k] - b[k];
d2 += d * d;
}

Кажется, это работает нормально, за исключением того, что я профилировал свой код, и это узкое место (более 50% времени тратится только на это).
Я использую Visual Studio 2012, на Win 7, с этими параметрами оптимизации: /O2 /Oi /Ot /Oy-,
Насколько я понимаю, VS2012 должен автоматически векторизовать этот цикл (используя SSE2).
Однако, если я вставлю #pragma loop(no_vector) в коде я не вижу заметного замедления, поэтому я предполагаю, что цикл не векторизован. Компилятор подтверждает это этим сообщением:

  info C5002: loop not vectorized due to reason '1105'

Мои вопросы:

  1. Можно ли исправить этот код, чтобы VS2012 мог его векторизовать?
  2. Если нет, то имеет ли смысл пытаться векторизовать код сам?
  3. Можете ли вы порекомендовать мне сайт, чтобы узнать о кодировании SSE2?
  4. Есть ли какое-то значение N, ниже которого векторизация будет контрпродуктивной?
  5. Что такое reason '1105'?

4

Решение

От Документация MSDN, код ошибки 1105 означает, что компилятор не может понять, как свести код к векторизованным инструкциям. Для операций с плавающей запятой указано, что вам нужно указать параметр / fp: fast, чтобы разрешить любые сокращения с плавающей запятой вообще.

4

Другие решения

Это довольно просто реализовать с помощью встроенных функций SSE:

#include "pmmintrin.h"
__m128 vd2 = _mm_set1_ps(0.0f);
float d2 = 0.0f;
int k;

// process 4 elements per iteration
for (k = 0; k < N - 3; k += 4)
{
__m128 va = _mm_loadu_ps(&a[k]);
__m128 vb = _mm_loadu_ps(&b[k]);
__m128 vd = _mm_sub_ps(va, vb);
vd = _mm_mul_ps(vd, vd);
vd2 = _mm_add_ps(vd2, vd);
}

// horizontal sum of 4 partial dot products
vd2 = _mm_hadd_ps(vd2, vd2);
vd2 = _mm_hadd_ps(vd2, vd2);
_mm_store_ss(&d2, vd2);

// clean up any remaining elements
for ( ; k < N; ++k)
{
float d = a[k] - b[k];
d2 += d * d;
}

Обратите внимание, что если вы можете гарантировать, что a а также b выровнены 16 байтов, то вы можете использовать _mm_load_ps скорее, чем _mm_loadu_ps что может помочь производительности, особенно на старых (до Nehalem) процессорах.

Отметим также, что для таких циклов, как этот, где очень мало арифметических инструкций относительно количества нагрузок, производительность может быть ограничена пропускной способностью памяти, и ожидаемое ускорение от векторизации может не реализоваться на практике.

6

По вопросам рекламы [email protected]