Как я могу обменять младшие 128 бит и старшие 128 бит в 256-битном регистре AVX (YMM)

Я портирую код SSE SIMD для использования 256-битных расширений AVX и не могу найти никаких инструкций, которые будут смешивать / перемешивать / перемещать высокие 128 бит и младшие 128 бит.

Сюжетная история:

Я действительно хочу, чтобы _1 _ / _ 2_ действовал как _3 _ / _ 4_, только с 256-битными словами. К сожалению, он действует как два вызова HADDPS, действующих независимо на младшие и высокие слова.


person Mark Borgerding    schedule 26.08.2011    source источник
comment
Если вы просто хотите суммировать по горизонтали, обычно вам нужен vextractf128, который везде работает быстро (особенно Zen1), сужаясь до 128-битных векторов. Как суммировать __m256 по горизонтали?. Но вы бы не хотели, чтобы haddps был частью эффективной горизонтальной суммы в первую очередь, так что, надеюсь, это не то, что вы делали ... Если вам не нужно было делать несколько hsums, тогда да, vhaddps может быть полезен, как в Intel AVX: 256-битная версия скалярного произведения для переменных с плавающей запятой двойной точности. А может 2x vperm2f128 + vaddps   -  person Peter Cordes    schedule 17.11.2020


Ответы (3)


Используя VPERM2F128, можно поменять местами младшие 128 и старшие 128 бит (а также другие перестановки). Использование встроенной функции выглядит так:

x = _mm256_permute2f128_ps( x , x , 1)

Третий аргумент - это управляющее слово, которое дает пользователю большую гибкость. Дополнительные сведения см. В Руководстве Intel Instrinsic.

person Mark Borgerding    schedule 28.08.2011
comment
В справочном руководстве Intel указано контрольное слово: VPERM2F128 (прямая ссылка) - AVX2 также имеет VPERM2I128, который в основном делает то же самое - не знаю, почему Intel посчитала, что им нужны две разные инструкции, поскольку тип не должен иметь значения, или должен? - person maxschlepzig; 07.03.2020
comment
Это отвечает на мой вопрос: Почему и то, и другое? vperm2f128 (avx) против vperm2i128 (avx2) - person maxschlepzig; 07.03.2020
comment
valignq также можно использовать для выполнения эквивалента ROR на 512 битах с 64-битным приращением (вместо этого используйте valignd, чтобы получить 32 бита). - person Alexis Wilke; 17.11.2020
comment
@AlexisWilke: Для этого требуется AVX-512. Имея только AVX2, вы можете сразу использовать vpermq для замены половин одного вектора. vperm2f128 требуется только AVX1, но он медленнее, чем vpermq на некоторых процессорах (например, Zen1 и KNL). - person Peter Cordes; 17.11.2020

Я знаю только один способ сделать это с помощью _mm256_extractf128_si256 и _mm256_set_m128i. Например. чтобы поменять местами две половины 256-битного вектора:

__m128i v0h = _mm256_extractf128_si256(v0, 0);
__m128i v0l = _mm256_extractf128_si256(v0, 1);
__m256i v1 = _mm256_set_m128i(v0h, v0l);
person Paul R    schedule 27.08.2011
comment
Вы знаете разницу между _mm256_extractf128_si256 и _mm256_extracti128_si256? Единственное, что я могу сказать, это то, что первый работает с AVX, а второй требует AVX2. Зачем кому-либо использовать вторую версию. Я смотрю на таблицы инструкций Агнера Фога и вижу, что время задержки, пропускная способность и порты идентичны. Может мне стоит задать это как вопрос. - person Z boson; 05.09.2014
comment
Я думал, что уже видел, как это спрашивают где-то на SO, но быстрый поиск не показал его - AFAIK, они фактически такие же. - person Paul R; 05.09.2014
comment
@Zboson: oops - только что нашел вопрос, который я упомянул выше - мне следовало искать инструкции, а не встроенные функции: stackoverflow.com/questions/18996827/ - person Paul R; 05.09.2014
comment
Я считаю, что этот способ медленнее, чем ответ Марка, поскольку каждый extractf и set имеют широту 3, пропускную способность 1. - person mafu; 26.04.2017
comment
@mafu: да, верно - обратите внимание, что clang (и, возможно, другие компиляторы) достаточно умен, чтобы преобразовать приведенное выше в один vperm2f128, что делает его по существу таким же, как ответ Марка. - person Paul R; 26.04.2017
comment
@PaulR Спасибо за разъяснения! - person mafu; 26.04.2017

x = _mm256_permute4x64_epi64(x, 0b01'00'11'10);

Прочтите об этом здесь. И попробуйте онлайн!

Примечание. Для этой инструкции требуется AVX2 (а не только AVX1).

Как прокомментировал @PeterCordes по скорости на процессорах Zen2 / Zen3 _ mm256_permute2x128_si256 (x, x, i) - лучший вариант, хотя он имеет 3 аргумента по сравнению с функцией _ mm256_permute4x64, i x64_epi > предложено мной с двумя аргументами с. На Zen1 и KNL / KNM (и экскаваторах семейства Bulldozer) _ mm256_permute4x64_epi64 (x, i), предложенный мной, более эффективен. На других процессорах (включая массовый Intel) оба варианта равны.

Как уже было сказано, и _mm256_permute2x128_si256(x, y, i), и _mm256_permute4x64_epi64(x, i) нуждаются в AVX2, а _mm256_permute2f128_si256(x, i) - только в AVX1.

person Arty    schedule 21.05.2021
comment
Для этого требуется AVX2, а не только AVX1, но да, он быстрее на некоторых процессорах, чем VPERM2F128, и то же самое на других. (Включая Zen1 на удивление uops.info и Knight's Landing, где перемешивание с двумя входами происходит медленнее). Я не думаю, что нигде хуже, за исключением процессоров с только AVX1, таких как Sandybridge и Piledriver, которые вообще не могли его запустить. - person Peter Cordes; 22.05.2021
comment
@PeterCordes Спасибо за комментарий! Добавлю, что ему нужен AVX2. Я просто подумал, что когда OP написал, что ему нужна инструкция AVX, он на самом деле мог иметь в виду, что ему нужна любая версия AVX, обычно это так. То же самое, когда кто-то просто говорит, что мне нужно решение SSE, в большинстве случаев он имеет в виду SSE2-SSE4.2. Но да, OP должен уточнить, что ему на самом деле нужно. Тем не менее мое решение было бы полезно для некоторых людей. По крайней мере, для меня этот вопрос возник в Google, когда мне действительно понадобилось решение avx2. - person Arty; 22.05.2021
comment
Да, конечно, хорошо включить этот ответ на этот вопрос, просто важно напомнить людям, какое расширение требует встроенная функция, особенно когда это больше, чем минимальное расширение для использования задействованных типов или упомянутых в вопросе. (Вопрос в использовании FP, а __m256 полностью можно использовать с AVX1. Вы не можете много сделать с __m256i без AVX2, но _ 3_ версия этого перемешивания также является AVX2, как и все другие перемешивания с пересечением полос с детализацией менее 128 бит). - person Peter Cordes; 22.05.2021
comment
Только что заметил, что у Zen 2 быстрее vperm2i128 (задержка 1 мупа 3c), чем vpermq (2 мупа, задержка 6c)! Очень странно, очевидно, что перемешивание с пересечением полос с детализацией менее 128 бит по-прежнему не является единственной задачей AMD, даже в Zen3. (vpermd также составляет 2 мопса, с задержкой 8c от данных до результата или 3c от вектора управления перемешиванием до результата.) Таким образом, очевидно, что этот не обязательно лучший выбор в будущем, с Zen2 занимает довольно значительную долю рынка в последние годы. - person Peter Cordes; 02.06.2021
comment
@PeterCordes Итак, вы говорите, что _ mm256_permute2x128_si256 (x, y, i) / a> более эффективен, чем _ mm256_permute4x64_epi64 (x, i), хотя первый имеет 3 аргумента а вторые 2 аргумента? - person Arty; 02.06.2021
comment
@PeterCordes А как насчет _ mm256_permute2f128_si256) (x, y > по сравнению с _ mm256_permute2x128_si256 (x, y, i)? Оба имеют 3 аргумента, но первый - AVX1, второй - AVX2. - person Arty; 02.06.2021
comment
Да, именно так, на Zen2 / Zen3 _mm256_permute2x128_si256(x, x, i) - лучший вариант, повторяя один и тот же ввод дважды. На Zen1 и KNL / KNM (и экскаваторах семейства Bulldozer) _mm256_permute4x64_epi64(x, i) более эффективен. На других процессорах (включая массовый Intel) оба варианта равны. У процессоров AVX1 нет выбора, доступно только vperm2f128. Даже vpermpd - это AVX2. - person Peter Cordes; 02.06.2021
comment
vperm2f128 (AVX1) и vperm2i128 (AVX2) работают одинаково на каждом процессоре AVX2. Я не думаю, что на реальных процессорах есть дополнительная задержка обхода для использования версии f128 между целочисленными инструкциями AVX2, но, вероятно, неплохо использовать версию i128 - она ​​никогда не должна быть хуже, чем vperm2f128, хотя может быть хуже чем vpermq в зависимости от процессора. - person Peter Cordes; 02.06.2021
comment
@PeterCordes Так что, по крайней мере, с точки зрения кода использование _mm256_permute2f128_si256(x, y, i) всегда лучше, чем _mm256_permute2x128_si256(x, y, i), как я думаю. Потому что оба работают с одинаковой скоростью повсюду, но первый использует только AVX1, а второй требует AVX2, поэтому это означает, что первый будет компилироваться на большем количестве целей и охватывать больше процессоров. Подскажите, пожалуйста, всегда ли я могу использовать _mm256_permute2f128_si256(x, y, i)? Могу ли я использовать это для всех типов регистров __m256i, __m256 и __m256d? Имеет ли значение для процессора, если для целочисленного регистра я использую плавающую версию и наоборот? Все регистры просто YMM? - person Arty; 02.06.2021
comment
оба работают с одинаковой скоростью везде - в этом я не уверен на 100%. Возможно, некоторые процессоры могут иметь дополнительную задержку, если, например, вы используете vperm2f128 между vpaddb ymm, ymm инструкциями. Поэтому, если вы используете другие __m256i встроенные функции, которые также требуют AVX2, используйте _mm256_permute2x128_si256 или _mm256_permute4x64_epi64. Если вы используете __m256 или __m256d в функции, которая требует только AVX1 (и, возможно, FMA), не стоит делать отдельную версию AVX2 только для vpermpd, если вы не хотите настраиваться специально для Zen1 (с учетом его 128-битного вектора аппаратное обеспечение). - person Peter Cordes; 02.06.2021