У меня есть простая проблема. Имея начальное значение 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 дополнения вместо 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 не подходит для подобных задач. Его производительность хороша только для «вертикальных» операций, но требует много дополнительной работы для «горизонтальных» операций, необходимых здесь.
Спасибо всем ребята за вашу помощь. Решив выяснить, какая версия самая быстрая, я написал тестовое приложение.
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
Скорее удивительно …