Сдвиг влево (массива float32) с AVX2 и заполнение нулем

Я уже некоторое время использую следующий "трюк" в коде C с SSE2 для чисел с плавающей запятой одинарной точности:

static inline __m128 SSEI_m128shift(__m128 data)
{
    return (__m128)_mm_srli_si128(_mm_castps_si128(data), 4);
}

Для таких данных, как [1.0, 2.0, 3.0, 4.0], это приводит к [2.0, 3.0, 4.0, 0.0], т. Е. Выполняет сдвиг влево на одну позицию и заполняет структуру данных нулем. Если я правильно помню, указанная выше встроенная функция компилируется до одной инструкции (по крайней мере, с помощью gcc).

Я как-то не могу понять, что делать то же самое с AVX2. Как я мог добиться этого эффективным образом?

Похожие вопросы: 1, 2, 3


person s-m-e    schedule 23.05.2020    source источник
comment
Если вы используете gcc, я рекомендую использовать векторные расширения gcc вместо встроенных функций, специфичных для архитектуры, где это возможно. В частности, вы можете использовать __builtin_shuffle(data, (fvectype){0}, (ivectype){1, 2, 3, 4}). Однако имейте в виду, что AVX-векторы длиной более 128 бит состоят из полос, а инструкции по пересечению полос (которые неизбежны при прямом расширении вашего примера) намного медленнее, чем в полосе движения. операций (~ в 3 раза медленнее), поэтому может быть хорошей идеей проверить, действительно ли вам это нужно.   -  person EOF    schedule 23.05.2020
comment
@EOF Спасибо за указатель. Если бы я планировал использовать встроенные функции, специфичные для архитектуры, знаете ли вы, как делать то, что я хочу? :)   -  person s-m-e    schedule 23.05.2020
comment
Конечно. gcc компилирует встроенные функции вектора gcc в следующую сборку: vmovaps %ymm0, %ymm1 vxorps %xmm0, %xmm0, %xmm0 vperm2f128 $33, %ymm0, %ymm1, %ymm0 vpalignr $4, %ymm1, %ymm0, %ymm0. Вы можете перепроектировать это в Intel-Intrinsics, если хотите. В качестве альтернативы разумным решением будет внутренняя функция вектора gcc.   -  person EOF    schedule 23.05.2020
comment
@EOF Это ... полезно.   -  person s-m-e    schedule 23.05.2020
comment
Пожалуйста. Если вы хотите сделать это правильно, вот ссылка на Godbolt с реализацией.   -  person EOF    schedule 23.05.2020
comment
@EOF: другой способ сделать это перемешивание (что было бы лучше в цикле, в котором вы можете загружать векторные константы один раз за пределами цикла): vpermd для выполнения перемешивания с пересечением полосы движения с 32-битными элементами, vpblendd для смешивания с элементом 0.0, где ты хочешь это.   -  person Peter Cordes    schedule 23.05.2020
comment
@PeterCordes Что ж, gcc похоже, нравится код в цикле. Не могли бы вы объяснить, почему vpermd/vpblendd было бы предпочтительнее? Агнер Фог и uops.info показывают vpalignr быть быстрым, очевидно, это не считается инструкцией по пересечению полосы движения.   -  person EOF    schedule 23.05.2020
comment
@EOF: vpermd стоит столько же, сколько vperm2f128 на оборудовании Intel, возможно, несколько меньше на Zen 1. vpblendd составляет 1 мкоп для любого векторного порта ALU на Intel, поэтому это позволяет избежать потенциального узкого места при случайном перемещении портов из vperm2f128 + vpalignr. vpalignr - это просто перетасовка на полосе, поэтому почему нам нужен vperm2f128 для его настройки.   -  person Peter Cordes    schedule 23.05.2020
comment
@PeterCordes Хм, хорошо. Хотя vpalignr нахождение в полосе (для меня) совсем не очевидно, поскольку он перемещает данные с нижней полосы по крайней мере одного входа на верхнюю полосу выхода.   -  person EOF    schedule 23.05.2020
comment
@EOF: Нет, это не так, поэтому так сложно использовать / такой плохой дизайн для расширения palign до 256 бит, и почему GCC нужен vperm2f128. См. 256-битную диаграмму в felixcloutier.com/x86/palignr   -  person Peter Cordes    schedule 23.05.2020
comment
@PeterCordes Ооооо, полосы от источников эффективно повернуты в соответствующую полосу места назначения! Это ... не здорово. Ну, по крайней мере, теперь я, кажется, понял инструкцию, так что спасибо и вам за это.   -  person EOF    schedule 23.05.2020
comment
gcc и clang оптимизируют это до _1 _ / _ 2_ и vblendps: godbolt.org/z/RW7_ds, которые требуется вектор тасования (технически можно использовать тот же вектор для смешения). Это должно быть возможно только с vperm2f128 и vpalignr (vperm2f128 может установить половину в 0) - не требуя вектора тасования, но две операции над p5.   -  person chtz    schedule 23.05.2020
comment
@chtz Это противоположное направление. Это было бы правильным направлением, и это нормально для clang, но gcc это совсем не нравится .   -  person EOF    schedule 23.05.2020
comment
@EOF, argh .. вы правы - я все еще иногда путаюсь с левым и правым (также OP, похоже, действительно требует сдвига вправо, несмотря на то, что заголовок говорит, что сдвиг влево ...)   -  person chtz    schedule 23.05.2020