У меня есть код, который я пытаюсь ускорить. Во-первых, я использовал встроенные функции SSE и увидел значительный выигрыш. Теперь я пытаюсь понять, могу ли я сделать то же самое с внутренними функциями AVX. Код, по сути, берет два массива, складывает или вычитает их по мере необходимости, возводит результат в квадрат и затем суммирует все эти квадраты.
Ниже приведена несколько упрощенная версия кода с использованием встроенных функций sse:
float chiList[4] __attribute__((aligned(16)));
float chi = 0.0;
__m128 res;
__m128 nres;
__m128 del;
__m128 chiInter2;
__m128 chiInter;
while(runNum<boundary)
{
chiInter = _mm_setzero_ps();
for(int i=0; i<maxPts; i+=4)
{
//load the first batch of residuals and deltas
res = _mm_load_ps(resids+i);
del = _mm_load_ps(residDeltas[param]+i);
//subtract them
nres = _mm_sub_ps(res,del);
//load them back into memory
_mm_store_ps(resids+i,nres);
//square them and add them back to chi with the fused
//multiply and add instructions
chiInter = _mm_fmadd_ps(nres, nres, chiInter);
}
//add the 4 intermediate this way because testing
//shows it is faster than the commented out way below
//so chiInter2 has chiInter reversed
chiInter2 = _mm_shuffle_ps(chiInter,chiInter,_MM_SHUFFLE(0,1,2,3));
//add the two
_mm_store_ps(chiList,_mm_add_ps(chiInter,chiInter2));
//add again
chi=chiList[0]+chiList[1];
//now do stuff with the chi^2
//alternatively, the slow way
//_mm_store_ps(chiList,chiInter);
//chi=chiList[0]+chiList[1]+chiList[2]+chiList[3];
}
Это подводит меня к моему первому вопросу: Есть ли способ сделать последнюю часть (где я беру 4 числа с плавающей запятой в chiInter и суммирую их в одно число с плавающей запятой) более элегантно?
В любом случае, теперь я пытаюсь реализовать это с помощью встроенных функций avx, большая часть этого процесса довольно проста, к сожалению, я задерживаюсь, пытаясь сделать последний бит, пытаясь сжать 8 промежуточных значений chi в одно значение.
Ниже приведен аналогично упрощенный фрагмент кода для встроенных функций avx:
float chiList[8] __attribute__((aligned(32)));
__m256 res;
__m256 del;
__m256 nres;
__m256 chiInter;
while(runNum<boundary)
{
chiInter = _mm256_setzero_ps();
for(int i=0; i<maxPts; i+=8)
{
//load the first batch of residuals and deltas
res = _mm256_load_ps(resids+i);
del = _mm256_load_ps(residDeltas[param]+i);
//subtract them
nres = _mm256_sub_ps(res,del);
//load them back into memory
_mm256_store_ps(resids+i,nres);
//square them and add them back to chi with the fused
//multiply and add instructions
chiInter = _mm256_fmadd_ps(nres, nres, chiInter);
}
_mm256_store_ps(chiList,chiInter);
chi=chiList[0]+chiList[1]+chiList[2]+chiList[3]+
chiList[4]+chiList[5]+chiList[6]+chiList[7];
}
Мой второй вопрос: Есть ли какой-нибудь метод, подобный тому, который я использовал с SSE выше, который позволит мне выполнить это последнее добавление быстрее? или, если есть лучший способ сделать то, что я сделал во встроенных функциях SSE, есть ли у него эквивалент для встроенных функций AVX?
maxPts
достаточно велико, то общее время будет зависеть от того, что происходит внутри цикла for, и любой код преамбулы / постамбулы не будет иметь значения с точки зрения производительности. - person Paul R   schedule 29.03.2014chi
, числа с плавающей запятой (с плавающей запятой одинарной точности) имеют только 24 бита значимости en.wikipedia.org/wiki/Single-precision_floating-point_format по мере роста накопленной ошибки вы потеряете точность, и она может дойти до точки, в которой вы прекратите накапливать дальше (промежуточный квадрат дельты остатков может быть в 2 ^ 24 раза меньше, чем ваш текущий накопленныйchi
, и когда ЦП нормализуется для сложения, они обращаются к нулю. - person amdn   schedule 29.03.2014