SSE накопительное суммирование

У меня есть простая проблема. Имея начальное значение uint_32 (скажем, 125) и __m128i операндов для добавления, например (+ 5, + 10, -1, -5). То, что я хотел бы получить как можно быстрее, это вектор (125 + 5, 125 + 5 + 10, 125 + 5 + 10 — 1, 125 + 5 + 10 — 1 — 5), то есть кумулятивно добавлять значения из операндов к начальному значению. Пока единственное решение, которое я могу придумать, — это добавление 4 __m128i переменных. Например, они будут

/* pseudoSSE code... */
__m128i src =     (125,125,125,125)
__m128i operands =(5,10,-1,-5)

/*  Here I omit the partitioning of operands into add1,..add4 for brevity  */

__m128i add1 =    (+05,+05,+05,+05)
__m128i add2 =    (+00,+10,+10,+10)
__m128i add3 =    (+00,+00,-01,-01)
__m128i add4 =    (+00,+00,+00,-05)
__m128i res1 = _mm_add_epu32( add1, add2 )
__m128i res2 = _mm_add_epu32( add3, add4 )
__m128i res3 = _mm_add_epu32( res1, add2 )
__m128i res  = _mm_add_epu32( res3, src  )

Вот так я получаю то, что хотел. Для этого решения мне нужно будет установить все переменные add_, а затем выполнить 4 добавления. Я действительно спрашиваю, можно ли сделать это быстрее. Либо через какой-то другой алгоритм, либо, может быть, с помощью некоторых специализированных функций SSE, которые я еще не знаю (что-то вроде _mm_cumulative_sum ()). Большое спасибо.

3

Решение

Вы можете добавить еще параллелизм и использовать 3 дополнения вместо 4:

const __m128i src = _mm_set1_epi32(125);
const __m128i operands = _mm_set_epi32(5,10,-1,-5);

const __m128i shift1 =
_mm_add_epi32(operands,
_mm_and_si128(_mm_shuffle_epi32(operands, 0xF9),
_mm_set_epi32(0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF)));

const __m128i shift2 =
_mm_add_epi32(shift1,
_mm_and_si128(_mm_shuffle_epi32(shift1, 0xFE),
_mm_set_epi32(0,0,0xFFFFFFFF,0xFFFFFFFF)));

const __m128i res = _mm_add_epi32(src, shift2);

Здесь используется набор инструкций SSE2. Новые наборы инструкций позволяют заменить _mm_and_si128 / _mm_shuffle_epi32 одной инструкцией, например _mm_shuffle_epi8.

Накопленная сумма рассчитывается с 2 сложениями, как показано ниже:

   a    b    c    d
+      a    b    c
------------------
a   a+b  b+c  c+d
+           a   a+b
------------------
a   a+b a+b+c a+b+c+d

SSE не подходит для подобных задач. Его производительность хороша только для «вертикальных» операций, но требует много дополнительной работы для «горизонтальных» операций, необходимых здесь.

5

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

Спасибо всем ребята за вашу помощь. Решив выяснить, какая версия самая быстрая, я написал тестовое приложение.

1 / nonSSE версия делает все так, как вы ожидаете.

int iRep;
int iCycle;
int iVal = 25;
int a1, a2, a3, a4;
int dst1 [4];
for ( iCycle = 0; iCycle < CYCLE_COUNT; iCycle++ )
for ( iRep = 0; iRep < REP_COUNT; iRep++ )
{
a1 = a2 = a3 = a4 = iRep;
dst1[0] = iVal + a1;
dst1[1] = dst1[0] + a2;
dst1[2] = dst1[1] + a3;
dst1[3] = dst1[2] + a4;
}

2 / Дополнения SSE-4 делают то, что я предложил, т.е.

__m128i _a1, _a2, _a3, _a4;
__m128i _res1, _res2, _res3;
__m128i _val;
__m128i _res;

for ( iCycle = 0; iCycle < CYCLE_COUNT; iCycle++ )
for ( iRep = 0; iRep < REP_COUNT; iRep++ ){
a1 = a2 = a3 = a4 = iRep;

_val = _mm_set1_epi32( iVal );
_a1  = _mm_set_epi32 (a1, a1, a1, a1 );
_a2  = _mm_set_epi32 (a2, a2, a2, 0  );
_a3  = _mm_set_epi32 (a3, a3, 0 , 0  );
_a4  = _mm_set_epi32 (a4, 0 , 0 , 0  );

_res1 = _mm_add_epi32( _a1, _a2     );
_res2 = _mm_add_epi32( _a3, _a4     );
_res3 = _mm_add_epi32( _val, _res1  );
_res  = _mm_add_epi32( _res3, _res2 );
}

3 / Дополнения SSE-3 делают то, что предложил Евгений, то есть

__m128i shift1, shift2, operands ;
for ( iCycle = 0; iCycle < CYCLE_COUNT; iCycle++ )
for ( iRep = 0; iRep < REP_COUNT; iRep++ ){
a1 = a2 = a3 = a4 = iRep;

_val = _mm_set1_epi32( iVal );
operands = _mm_set_epi32(a1,a2,a3,a4);

shift1 = _mm_add_epi32( operands,
_mm_and_si128(_mm_shuffle_epi32(operands, 0xF9),   _mm_set_epi32(0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF)   ));
shift2 = _mm_add_epi32( shift1,
_mm_and_si128(_mm_shuffle_epi32(shift1, 0xFE),     _mm_set_epi32(0,0,0xFFFFFFFF,0xFFFFFFFF)            ));
_res = _mm_add_epi32(_val, shift2);
}

Результаты для

 #define REP_COUNT    100000
#define CYCLE_COUNT  100000

являются

  non-SSE        -> 6.118s
SSE-4additions -> 20.775s
SSE-3additions -> 14.873s

Скорее удивительно …

1

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