vpmuludq
, также известный как _mm512_mul_epu32
, принимает четные исходные 32-битные элементы (0, 2, 4 и т. д.) 1. Это позволяет ему эффективно выполняться в каждом 64-битном блоке, подавая младшие 32 бита входных данных в множители мантиссы FP. Это расширение, также известное как полное умножение, а не умножение с высокой половиной, поэтому, конечно, он должен игнорировать некоторые входные данные (потому что никакие математические инструкции SIMD не имеют двух векторных адресатов).
Таким образом, вам нужно использовать его дважды, чтобы получить все результаты с высокой половиной, которые вы хотите: один раз с четными элементами и один раз с нечетными элементами в четных позициях (сдвиньте вправо оба входных вектора). Затем вам нужно чередовать высокие половины этих 64-битных элементов.
Уловка делает это эффективно: AVX-512 vpermt2d
для выбора 32-битных элементов из 2 исходных векторов выполняет свою работу за один муп. Так что это здорово, особенно в цикле, который позволяет компилятору поднять нагрузку на векторную константу управления перемешиванием. Другие варианты включают _mm512_mask_shuffle_epi32
(vpshufd
с маскированием слияния) для копирования верхних половин вниз за 1 вектор, и слияние с другим вектором результатов при наличии элемента управления слиянием в регистре k
. (Один из результатов vpmuludq
имеет верхние половины там, где вы хотите, потому что входы были смещены вправо). vmovshdup
(_mm512_mask_movehdup_ps
) выполняет то же перемешивание в машинном коде на 1 байт меньше, немедленной необходимости нет. Это неудобно с встроенными функциями, потому что вам нужно преобразовать __m512i
в __m512
с помощью _mm512_castsi512_ps
, но производительность должна быть такой же.
Или даже сохранить дважды, с маскировкой для 2-го хранилища, но это, вероятно, плохо, потому что одно из хранилищ должно быть смещено (и, следовательно, пересечение строки кэша для 64-байтовых хранилищ). Тем не менее, он избегает каких-либо дополнительных ошибок ALU.
Более очевидный вариант (как в случае с AVX2) - это vpsrld
(_mm512_srli_epi64(v,32)
) один из них, а затем vpblendd
. Но это стоит 2 отдельных мупа ALU, а использование 512-битных векторов на текущих процессорах означает, что есть только 2 порта выполнения векторного ALU, которые могут их обрабатывать. Также vpblendd
не имеет версии AVX-512; есть только смеси, которые принимают управляющий операнд в регистре k
. (Использование shift / AND и OR для слияния будет еще хуже, и все равно потребуется векторная константа)
__m512i mulhi_epu32_512(__m512i a, __m512i b)
{
__m512i evens = _mm512_mul_epu32(a,b);
__m512i odds = _mm512_mul_epu32(_mm512_srli_epi64(a,32), _mm512_srli_epi64(b,32));
return _mm512_mask_shuffle_epi32(odds, 0x5555, evens, _MM_SHUFFLE(3,3,1,1));
// _mm512_mask_movehdup_ps may be slightly more efficient, saving 1 byte of code size
}
Для автономной функции clang оптимизирует перетасовку с маской слияния в vpermi2d
с векторной константой из памяти вместо mov eax, 0x5555
/ kmovw k1, eax
или чего-то еще. Меньше ошибок при включенной настройке, но возможны промахи в кэше. GCC компилирует его, как написано. https://godbolt.org/z/v4M7PK показывает оба. Для тела цикла (с поднятой установкой) в любом случае будет один муп, но с маской слияния vpshufd
имеет только 1 цикл задержки по сравнению с 3 для пересечения полосы движения vpermi2d
/ vpermt2d
. (https://uops.info/ и https://agner.org/optimize/)
Сноска 1: Связанные вами вопросы и ответы либо не полностью описывают проблему и / или решение, либо на самом деле нужны только 2 числа (внизу вектора?), а не 2 вектора < / em> чисел.
person
Peter Cordes
schedule
17.11.2020
_mm512_mulhi_epi32
существует, но только для архитектуры Knights Corner (часть семейства деталей Xeon Phi). Он не поддерживается процессорами с поддержкой AVX512. - person Jason R   schedule 16.11.2020vpmuludq
(felixcloutier.com/x86/pmuludq) принимает 32-битный четный исходный код. элементы (0, 2, 4 и т. д.), а не нижнюю половину. Он полностью встроен в каждый 64-битный элемент. (Таким образом, это может быть реализовано путем маршрутизации целочисленных данных через множители мантиссы FP без необходимости перемешивания). Но да, общая идея правильная - объединить две половинки. Вы можете воспользоваться 32-битным перемешиванием с маскированием слияния, чтобы избежать отдельногоvpor
илиvpblendd
. - person Peter Cordes   schedule 17.11.2020vpermt2d
для выбора элементов из 2x 512-битных векторов с учетом вектора управления. Затем вам просто нужно 2xvpsrlq
(для перемещения нечетных элементов каждого ввода) и 2xvpmuludq
для подачи этогоvpermt2d
. - person Peter Cordes   schedule 17.11.2020