Я попытался написать код, который позволяет избежать смешивания инструкций avx и не-avx, а горизонтальная сумма регистра avx, содержащего числа с плавающей запятой, может быть выполнена только avx с помощью
- 1x
vperm2f128
,
- 2x
vshufps
и
- 3x
vaddps
,
в результате получается регистр, в котором все записи содержат сумму всех элементов исходного регистра.
// permute
// 4, 5, 6, 7, 0, 1, 2, 3
// add
// 0+4, 1+5, 2+6, 3+7, 4+0, 5+1, 6+2, 7+3
// shuffle
// 1+5, 0+4, 3+7, 2+6, 5+1, 4+0, 7+3, 6+2
// add
// 1+5+0+4, 0+4+1+5, 3+7+2+6, 2+6+3+7,
// 5+1+4+0, 4+0+5+1, 7+3+6+2, 6+2+7+3
// shuffle
// 3+7+2+6, 2+6+3+7, 1+5+0+4, 0+4+1+5,
// 7+3+6+2, 6+2+7+3, 5+1+4+0, 4+0+5+1
// add
// 3+7+2+6+1+5+0+4, 2+6+3+7+0+4+1+5, 1+5+0+4+3+7+2+6, 0+4+1+5+2+6+3+7,
// 7+3+6+2+5+1+4+0, 6+2+7+3+4+0+5+1, 5+1+4+0+7+3+6+2, 4+0+5+1+6+2+7+3
static inline __m256 hsums(__m256 const& v)
{
auto x = _mm256_permute2f128_ps(v, v, 1);
auto y = _mm256_add_ps(v, x);
x = _mm256_shuffle_ps(y, y, _MM_SHUFFLE(2, 3, 0, 1));
x = _mm256_add_ps(x, y);
y = _mm256_shuffle_ps(x, x, _MM_SHUFFLE(1, 0, 3, 2));
return _mm256_add_ps(x, y);
}
Затем получить значение легко, используя _mm256_castps256_ps128
и _mm_cvtss_f32
:
static inline float hadd(__m256 const& v)
{
return _mm_cvtss_f32(_mm256_castps256_ps128(hsums(v)));
}
Я провел несколько базовых тестов по сравнению с другими решениями с __rdtscp
и не нашел ни одного лучшего с точки зрения среднего количества циклов процессора на моем Intel i5-2500k.
Просматривая таблицы инструкций Agner, я обнаружил (для процессоров Sandy-Bridge):
µops lat. 1/tp count
this:
vperm2f128 1 2 1 1
vaddps 1 3 1 3
vshufps 1 1 1 2
sum 6 13 6 6
Z boson:
vhaddps 3 5 2 2
vextractf128 1 2 1 1
addss 1 3 1 1
sum 8 15 6 4
Stephen Canon:
vextractf128 1 2 1 1
addps 1 3 1 2
movhlps 1 1 1 1
shufps 1 1 1 1
addss 1 3 1 1
sum 8 13 6 6
где для меня (из-за того, что значения довольно похожи) ни один из них явно не лучше (поскольку я не могу предвидеть, что имеет наибольшее значение: количество инструкций, количество микроопераций, задержка или пропускная способность). редактировать, примечание: потенциальная проблема, которая, как я предполагал, существует в следующем, не соответствует действительности. Я подозревал, что - если достаточно иметь результат в регистре ymm - мой hsums
может быть полезен, поскольку он не t требует vzeroupper
для предотвращения штрафа за переключение состояний и, таким образом, может чередоваться/выполняться одновременно с другими вычислениями avx с использованием разных регистров без введения какой-либо точки последовательности.
person
Pixelchemist
schedule
09.11.2016
double
s. Вы также должны быть в состоянии адаптировать его к вашемуfloat
делу. Это наиболее эффективно, если у вас есть несколько элементов__m256
, сумму которых вы хотите вычислить параллельно. - person Jason R   schedule 14.12.2012min
иmax
. Я понимаю, что это не полный ответ (отсюда и комментарий), но он должен помочь вам начать. - person Jason R   schedule 14.12.2012__m128
, можно найти в этой статье. ; требуется ~ 15 инструкций или около того. То, как регистры YMM реализованы на x86, вероятно, усложняет задачу сортировки__m256
, поскольку по большей части вы не можете пересечь 128-битную границу. - person Jason R   schedule 14.12.2012