Умножайте векторы 32-битных целых чисел, беря только старшие 32 бита

Я хочу перемножить два 512-битных __m512i вектора из 16 беззнаковых 32-битных целых чисел вместе и взять только старшие 32 бита из 64-битного результата умножения. Хотя в руководстве Intel по встроенным функциям говорится, что _mm512_mulhi_epu32 существует, он не будет компилироваться на моей машине.

Ответ здесь утверждает, что _mm512_srli_epi64(_mm512_mul_epu32(a,b),32) будет работать, но это не так - проблема, похоже, в том, что _mm512_mul_epu32 учитывает только биты 0 ... 31, 64 ... 95 и т. д., игнорируя значения в нечетных позициях.

Как я могу быстрее всего извлечь старшие 32 бита из результатов умножения 32-битного вектора?


person Baaing Cow    schedule 16.11.2020    source источник
comment
Чтобы уточнить, _mm512_mulhi_epi32 существует, но только для архитектуры Knights Corner (часть семейства деталей Xeon Phi). Он не поддерживается процессорами с поддержкой AVX512.   -  person Jason R    schedule 16.11.2020
comment
@JasonR: vpmuludq (felixcloutier.com/x86/pmuludq) принимает 32-битный четный исходный код. элементы (0, 2, 4 и т. д.), а не нижнюю половину. Он полностью встроен в каждый 64-битный элемент. (Таким образом, это может быть реализовано путем маршрутизации целочисленных данных через множители мантиссы FP без необходимости перемешивания). Но да, общая идея правильная - объединить две половинки. Вы можете воспользоваться 32-битным перемешиванием с маскированием слияния, чтобы избежать отдельного vpor или vpblendd.   -  person Peter Cordes    schedule 17.11.2020
comment
@PeterCordes, конечно, ты прав. Я неправильно прочитал текст внутреннего руководства. Я собираюсь удалить другой комментарий, чтобы избежать путаницы.   -  person Jason R    schedule 17.11.2020
comment
Или vpermt2d для выбора элементов из 2x 512-битных векторов с учетом вектора управления. Затем вам просто нужно 2x vpsrlq (для перемещения нечетных элементов каждого ввода) и 2x vpmuludq для подачи этого vpermt2d.   -  person Peter Cordes    schedule 17.11.2020


Ответы (1)


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
comment
Это очень хорошо работает! Я не знаю почему, но на некоторых непостоянных компиляторах вам нужно заменить _MM_SHUFFLE(3,3,1,1) на _MM_PERM_DDBB, чтобы функция скомпилировалась. - person Baaing Cow; 17.11.2020
comment
@BaaingCow: Какие компиляторы не могут обрабатывать макрос Intel _MM_SHUFFLE? Это существовало более десяти лет, возможно, вплоть до встроенных функций SSE1 или даже MMX pshufw. Вы, конечно, можете использовать 0xf5, если вам нужна совместимость с компиляторами, у которых плохой immintrin.h. - person Peter Cordes; 17.11.2020