#c #x86 #simd #intrinsics #avx
#c #x86 #simd #встроенные функции #avx
Вопрос:
AVX512 предоставляет нам встроенные функции для суммирования всех ячеек в __mm512
векторе. Однако некоторые из их аналогов отсутствуют: пока нет _mm512_reduce_add_epi8
.
_mm512_reduce_add_ps //horizontal sum of 16 floats
_mm512_reduce_add_pd //horizontal sum of 8 doubles
_mm512_reduce_add_epi32 //horizontal sum of 16 32-bit integers
_mm512_reduce_add_epi64 //horizontal sum of 8 64-bit integers
В принципе, мне нужно реализовать MAGIC
в следующем фрагменте.
__m512i all_ones = _mm512_set1_epi16(1);
short sum_of_ones = MAGIC(all_ones);
/* now sum_of_ones contains 32, the sum of 32 ones. */
Наиболее очевидным способом было бы использовать _mm512_storeu_epi8
и суммировать элементы массива вместе, но это было бы медленно, плюс это могло бы привести к недействительности кэша. Я полагаю, что существует более быстрый подход.
Также бонусные баллы за реализацию _mm512_reduce_add_epi16
.
Комментарии:
1. Поскольку вы наивно упомянули, что это «медленно», это подразумевает, что это критически важно для производительности. Каково здесь использование? Если это большая операция сокращения, есть способы сделать это получше.
Ответ №1:
Прежде всего, _mm512_reduce_add_epi64
не соответствует ни одной инструкции AVX512, но генерирует последовательность перетасовок и дополнений.
Для уменьшения 64 epu8
значений до 8 epi64
значений обычно используется vpsadbw
инструкция (SAD = Сумма абсолютных разностей) по отношению к нулевому вектору, который затем может быть дополнительно уменьшен:
long reduce_add_epu8(__m512i a)
{
return _mm512_reduce_add_epi64(_mm512_sad_epu8(a, _mm512_setzero_si512()));
}
Попробуйте это на godbolt: https://godbolt.org/z/1rMiPH . К сожалению, ни GCC, ни Clang, похоже, не способны оптимизировать функцию, если она используется с _mm512_set1_epi16(1)
.
Для epi8
вместо epu8
0x80
вам нужно сначала добавить 128 к каждому элементу (или xor с помощью vpsadbw
), затем уменьшить его с помощью 64*128
и в конце вычесть 8*128
(или, в конце концов, для каждого промежуточного 64-битного результата). [Обратите внимание, что это было неправильно в предыдущей версии этого ответа]
Ибо epi16
я предлагаю взглянуть на то, какие инструкции _mm512_reduce_add_epi32
и _mm512_reduce_add_epi64
сгенерировать и вывести оттуда, что делать.
В целом, как предположил @Mysticial, наилучший подход к сокращению зависит от вашего контекста. Например, если у вас очень большой массив int64
и вам нужна сумма в виде int64
, вы должны просто сложить их вместе по пакетам и только в самом конце свести один пакет к одному int64
.