Проблема с оптимизацией кода AVX-512 (NASM)

Делать мне было нечего, поэтому я решил изучить AVX-512 и попробовать написать с его помощью что-нибудь на ассемблере. Я пытаюсь оптимизировать фрагмент кода, который обрабатывает большие объемы данных, используя инструкции AVX-512. Цель — максимизировать возможности векторных регистров и минимизировать количество циклов процессора.

Проблема вот в чем: я хочу использовать маскирование для обработки только части элементов в регистрах zmm0 и zmm1 в зависимости от определенного условия. Однако инструкции AVX-512 с масками (например, vaddps) требуют маски в регистре k0-k7:

vmovups zmm0, [rsi] ; float[16] <= zmm0
vmovups zmm1, [rsi+64]
 
; some code here
 
vaddps zmm0, zmm0, zmm11 
vmovups [rdi], zmm0
 
add rsi, 128 ; ptr => next[data]
add rdi, 64 ; ptr => next[data] ?to: write

При этом условие, по которому я хочу замаскировать данные, получается путем сравнения двух других регистров zmm.

Итак, вот вопрос:

Есть ли способ эффективно сгенерировать маску в регистре k на основе сравнения значений в регистрах zmm, а затем использовать ее для выборочной обработки данных с помощью инструкций AVX-512? А может быть, есть другой способ добиться желаемого результата с помощью AVX-512, не прибегая к маскам?

Я помню, что есть vpcmpd, который сравнивает значения векторных регистров, и якобы можно сделать что-то вроде k1 = zmm0 > zmm1 + k2 = zmm0 < zmm2, но, честно говоря, я понятия не имею, насколько это может быть эффективно; Я попробовал, но из-за нехватки знаний отказался от этой идеи.

Возможно, вы ищете VPMOVB2M/VPMOVW2M/VPMOVD2M/VPMOVQ2M Преобразование векторного регистра в маску

Jester 24.04.2024 23:36
vpcmpd это именно то, что вы хотите. Ну, возможно vpcmps, поскольку остальная часть вашего кода ps, но это не имеет значения. Вы можете использовать версию, имеющую входной регистр маски для формирования логического «и». Значения маски, которые были ложными после первого cmp, остаются ложными после второго, если они были замаскированы во втором cmp. Почему вы думаете, что это должно быть неэффективно?
Homer512 25.04.2024 02:02

Помните, что инструкция AVX занимает одинаковое количество времени независимо от того, работает ли она с каждым элементом или только с некоторыми из них. Таким образом, вы можете выполнить серию операций с полным регистром, а затем замаскировать ненужные значения в конце.

prl 25.04.2024 02:10

ТАСМ? Я думал, что Turbo Assembler поддерживает только 16- и 32-битные режимы и до сих пор не обновляется новыми функциями, такими как поддержка AVX-512! Я бы порекомендовал NASM для рукописного asm с использованием синтаксиса Intel, если только TASM вам действительно не подходит.

Peter Cordes 25.04.2024 03:19

@Питер Кордес Ой, я виноват. Я имел в виду NASM, даже не заметил опечатку "tasm" :)

jknoptrix_ 25.04.2024 15:14

@Homer512 Спасибо. Кроме того, я действительно не думал, что это может быть заразно; Я просто усомнился в своих силах, потому что не знаю, как это реализовать на самом деле, даже если решение простое. Я так понимаю, ты имеешь в виду что-то в этом роде? vpcmpd k1, zmm0, zmm1, 1 ; greatervpcmpd k2, zmm0, zmm2, 2 ; less

jknoptrix_ 25.04.2024 15:18

Тогда вам следует отредактировать свой вопрос, чтобы исправить его.

Peter Cordes 25.04.2024 15:53

Для сравнения ФП вам нужен vcmpps. felixcloutier.com/x86/cmpps . NASM должен поддерживать псевдонимы типа vcmpgtps, которые подразумевают немедленное действие.

Peter Cordes 25.04.2024 15:55

Я к тому, что вам не нужны две маски k1 и k2. На самом деле выполнение логических операций над такими масками, как kandw, происходит довольно медленно. Для этого вам понадобится только один. Примерно так: godbolt.org/z/Khss8MYfY

Homer512 25.04.2024 22:07
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
9
126
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Подводя итог обсуждению комментариев: вы были правы, предположив, что семейство инструкций vcmp, таких как vcmpps, является подходящим способом сделать это. Инструкции AVX512 в маске обычно выполняются быстро. Когда это возможно, используйте инструкции маски обнуления, такие как vaddps zmm1{k1}{z}, zmm2, zmm3, вместо инструкций по слиянию, таких как vaddps zmm1{k1}, zmm3, zmm0, чтобы избежать зависимости от содержимого предыдущего регистра.

При использовании регистров маски следует учитывать, что некоторые инструкции по их вычислению выполняются довольно медленно. Например, kadd имеет задержку 4 на Intel согласно uops.info, в то время как kand имеет задержку только 1, но пропускная способность все равно только 1.

Однако зачастую таким образом можно избежать комбинирования масок. vcmp сам принимает маску ввода. Выходная маска будет равна нулю там, где входная маска была нулевой. Это соединение И. Например, условие zmm1 < zmm2 && zmm2 < zmm3 можно записать как

vcmpps  k1, zmm1, zmm2, 1     ; _CMP_LT_OS
vcmpps  k1{k1}, zmm2, zmm3, 1

Мы не можем сформировать соединение ИЛИ таким образом, но мы все равно можем избежать использования двух регистров маски. Например, zmm1 < zmm2 || zmm2 < zmm3 то же самое, что ! (! (zmm1 < zmm2) && ! (zmm2 < zmm3)) согласно законам Де Моргана

vcmpps  k1, zmm1, zmm2, 5     ; _CMP_NLT_US
vcmpps  k1{k1}, zmm2, zmm3, 5
knotw   k1, k1

С другой стороны, использование двух масок и их объединение через korw устранит зависимость ввода от одной vcmp к другой, потенциально увеличивая параллелизм на уровне инструкций.

Другие вопросы по теме