Эта версия должна быть оптимальной как для процессоров Intel Sandy/Ivy Bridge и AMD Bulldozer, так и для более поздних версий.
// x = ( x7, x6, x5, x4, x3, x2, x1, x0 )
float sum8(__m256 x) {
// hiQuad = ( x7, x6, x5, x4 )
const __m128 hiQuad = _mm256_extractf128_ps(x, 1);
// loQuad = ( x3, x2, x1, x0 )
const __m128 loQuad = _mm256_castps256_ps128(x);
// sumQuad = ( x3 + x7, x2 + x6, x1 + x5, x0 + x4 )
const __m128 sumQuad = _mm_add_ps(loQuad, hiQuad);
// loDual = ( -, -, x1 + x5, x0 + x4 )
const __m128 loDual = sumQuad;
// hiDual = ( -, -, x3 + x7, x2 + x6 )
const __m128 hiDual = _mm_movehl_ps(sumQuad, sumQuad);
// sumDual = ( -, -, x1 + x3 + x5 + x7, x0 + x2 + x4 + x6 )
const __m128 sumDual = _mm_add_ps(loDual, hiDual);
// lo = ( -, -, -, x0 + x2 + x4 + x6 )
const __m128 lo = sumDual;
// hi = ( -, -, -, x1 + x3 + x5 + x7 )
const __m128 hi = _mm_shuffle_ps(sumDual, sumDual, 0x1);
// sum = ( -, -, -, x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 )
const __m128 sum = _mm_add_ss(lo, hi);
return _mm_cvtss_f32(sum);
}
haddps
неэффективен ни на одном процессоре; лучшее, что вы можете сделать, это один раз перетасовать (чтобы извлечь старшую половину) и одно добавить, повторять, пока не останется один элемент. Сужение до 128-бит в качестве первого шага приносит пользу AMD перед Zen2, и нигде это не является чем-то плохим.
См. Самый быстрый способ выполнить горизонтальную векторную сумму SSE на x86 для более подробной информации об эффективности.
person
Marat Dukhan
schedule
04.11.2012
haddps
после того, как вы выполнилиvextractf128
/addps
шаг. - person Peter Cordes   schedule 17.02.2016