Какой самый эффективный способ очистить один или несколько регистров ZMM в Knights Landing?

Скажем, я хочу очистить 4 zmm регистров.

Будет ли следующий код обеспечивать максимальную скорость?

vpxorq  zmm0, zmm0, zmm0
vpxorq  zmm1, zmm1, zmm1
vpxorq  zmm2, zmm2, zmm2
vpxorq  zmm3, zmm3, zmm3

На AVX2, если я хотел очистить регистры ymm, vpxor был быстрее, быстрее, чем vxorps, так как vpxor мог работать на нескольких устройствах.

На AVX512 у нас нет vpxor для регистров zmm, только vpxorq и vpxord. Это эффективный способ очистить регистр? Достаточно ли умен процессор, чтобы не создавать ложные зависимости от предыдущих значений регистров zmm, когда я очищаю их с помощью vpxorq?

У меня еще нет физического процессора AVX512, чтобы проверить это - может быть, кто-то тестировал на Knights Landing? Есть ли опубликованные задержки?

3 ответа

Решение

Наиболее эффективным способом является использование неявного обнуления AVX для VLMAX (максимальная ширина векторного регистра, определяемая текущим значением XCR0):

vpxor  xmm6, xmm6, xmm6
vpxor  xmm7, xmm7, xmm7
vpxor  xmm8, xmm0, xmm0   # still a 2-byte VEX prefix as long as the source regs are in the low 8
vpxor  xmm9, xmm0, xmm0

Это только 4-байтовые инструкции (2-байтовый префикс VEX) вместо 6 байтов (4-байтовый префикс EVEX). Обратите внимание на использование исходных регистров в младшем 8, чтобы разрешить 2-байтовый VEX, даже когда адресат xmm8-xmm15. (3-байтовый префикс VEX требуется, когда вторым источником является x/ymm8-15). И да, это все еще признается идиомой обнуления, если оба операнда-источника являются одним и тем же регистром (я проверял, что он не использует исполнительный модуль на Skylake).

Помимо эффектов размера кода, производительность идентична vpxord/q zmm а также vxorps zmm на Skylake-AVX512 и KNL. (И меньший код почти всегда лучше.) Но обратите внимание, что у KNL очень слабый внешний интерфейс, где максимальная пропускная способность декодирования может лишь слегка насытить исполнительные блоки вектора и обычно является узким местом, согласно руководству по микроархам Agner Fog. (Он не имеет кэша uop или буфера цикла, а максимальная пропускная способность составляет 2 инструкции на такт. Кроме того, средняя пропускная способность выборки ограничена 16B за цикл.)

Кроме того, для гипотетических будущих процессоров AMD (или, возможно, Intel), которые декодируют инструкции AVX512 как два 256-битных (или четыре 128-битных), это гораздо более эффективно. Современные процессоры AMD (включая Ryzen) не обнаруживают обнуление идиом до окончания декодирования vpxor ymm0, ymm0, ymm0 до 2 моп, так что это реальная вещь. К сожалению, компиляторы ошибаются: ошибка gcc 80636, ошибка clang 32862.


Обнуление zmm16-31 действительно требует инструкции в формате EVEX; vpxord или же vpxorq одинаково хороший выбор. EVEX vxorps по какой-то причине требуется AVX512DQ (недоступно в KNL), но EVEX vpxord/q является базовым AVX512F.

vpxor   xmm14, xmm0, xmm0
vpxor   xmm15, xmm0, xmm0
vpxord  zmm16, zmm16, zmm16     # or XMM if you already use AVX512VL for anything
vpxord  zmm17, zmm17, zmm17

Префиксы EVEX имеют фиксированную ширину, поэтому использовать zmm0 нечего.

Если цель поддерживает AVX512VL (Skylake-AVX512, но не KNL), вы все равно можете использовать vpxord xmm31, ... для повышения производительности на будущих процессорах, которые декодируют инструкции 512 b в несколько мопов.

Если ваша цель имеет AVX512DQ (Skylake-AVX512, но не KNL), возможно, это хорошая идея для использования vxorps при создании ввода для математической инструкции FP, или vpxord в любом другом случае. Не влияет на Skylake, но некоторые будущие процессоры могут заботиться. Не беспокойтесь об этом, если всегда проще использовать vpxord,


Связанный: оптимальный способ генерировать все единицы в регистре zmm vpternlogd zmm0,zmm0,zmm0, 0xff, (С таблицей поиска всех единиц каждая запись в логической таблице равна 1). vpcmpeqd same,same не работает, потому что версия AVX512 сравнивается с регистром маски, а не вектором.

Это особый случай vpternlogd/q не является специальным случаем, как независимый от KNL или Skylake-AVX512, поэтому попробуйте выбрать холодный регистр. Однако на SKL-avx512 это довольно быстро: 2 на тактовую частоту, согласно моим тестам. (Если вам нужно несколько reg-ов для всех, используйте на vpternlogd и скопируйте результат, особенно, если ваш код будет работать на Skylake, а не только на KNL).


Я выбрал 32-битный размер элемента (vpxord вместо vpxorq) потому что широко используется 32-битный размер элемента, и если один размер элемента будет медленнее, обычно это не 32-битный, что медленно. например pcmpeqq xmm0,xmm0 намного медленнее, чем pcmpeqd xmm0,xmm0 на Сильвермонт. pcmpeqw это еще один способ генерации вектора из всех единиц (до AVX512), но gcc выбирает pcmpeqd, Я почти уверен, что для обнуления xor это никогда не изменится, особенно без маски-регистра, но если вы ищете причину выбрать один из vpxord или же vpxorq Это такая же веская причина, как и любая другая, если только кто-то не обнаружит реальной разницы на любом оборудовании AVX512.

Интересно, что GCC выбирает vpxord, но vmovdqa64 вместо vmovdqa32,


Обнуление XOR вообще не использует порт выполнения на процессорах семейства Intel SnB, включая Skylake-AVX512. (TODO: включите часть этого в этот ответ, и сделайте некоторые другие обновления к нему...)

Но в KNL я почти уверен, что для обнуления xor нужен порт выполнения. Два вектора выполнения могут обычно идти в ногу с внешним интерфейсом, поэтому обработка нуля xor на этапе выпуска / переименования в большинстве ситуаций не будет иметь никакого значения. vmovdqa64 / vmovaps в соответствии с тестированием Агнера Фога, нужен порт (и, что более важно, ненулевая задержка), поэтому мы знаем, что он не обрабатывает те, которые находятся на этапе выпуска / переименования. (Это может быть как Sandybridge и устранять обнуление ксора, но не движения. Но я сомневаюсь в этом, потому что было бы мало пользы.)

Как указывает Коди, таблицы Агнера Фога показывают, что KNL выполняет оба vxorps/d а также vpxord/q на FP0/1 с той же пропускной способностью и задержкой, при условии, что им нужен порт. Я предполагаю, что это только для xmm / ymm vxorps/d, если документация Intel не содержит ошибок и EVEX vxorps zmm может работать на КНЛ.

Кроме того, на Skylake и позже, не обнуление vpxor а также vxorps работать на тех же портах. Преимущество запуска по большему количеству портов для логических векторов с целочисленным вектором - только для Intel Nehalem и Broadwell, то есть для процессоров, которые не поддерживают AVX512. (Это даже имеет значение для обнуления на Nehalem, где ему фактически нужен порт ALU, даже если он признан независимым от старого значения).

Задержка обхода-задержки на Skylake зависит от того, какой порт он выбирает, а не от того, какую инструкцию вы использовали. т.е. vaddps читая результат vandps имеет дополнительный цикл задержки, если vandps было запланировано р0 или р1 вместо р5. См. Руководство по оптимизации Intel для таблицы. Хуже того, эта дополнительная задержка применяется навсегда, даже если результат зачитывается в регистр сотни циклов перед чтением. Он влияет на цепочку депо от другого входа к выходу, поэтому в этом случае он все еще имеет значение. (ТОДО: запишите результаты моих экспериментов и опубликуйте их где-нибудь.)

Следуя совету Пола Р., чтобы посмотреть, что генерируют компиляторы кода, мы видим, что ICC использует VPXORD обнулить один регистр ZMM, затем VMOVAPS скопировать этот обнуленный регистр XMM в любые дополнительные регистры, которые должны быть обнулены. Другими словами:

vpxord    zmm3, zmm3, zmm3
vmovaps   zmm2, zmm3
vmovaps   zmm1, zmm3
vmovaps   zmm0, zmm3

GCC делает то же самое, но использует VMOVDQA64 для ZMM-ZMM перемещается регистр:

vpxord      zmm3, zmm3, zmm3
vmovdqa64   zmm2, zmm3
vmovdqa64   zmm1, zmm3
vmovdqa64   zmm0, zmm3

GCC также пытается запланировать другие инструкции между VPXORD и VMOVDQA64, ICC не демонстрирует это предпочтение.

Clang использует VPXORD обнулить все регистры ZMM независимо, а именно:

vpxord  zmm0, zmm0, zmm0
vpxord  zmm1, zmm1, zmm1
vpxord  zmm2, zmm2, zmm2
vpxord  zmm3, zmm3, zmm3

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


Это довольно сильно говорит о том, что VPXORD это инструкция, которую вы должны использовать для очистки 512-битного регистра ZMM.

Зачем VPXORD вместо VPXORQ? Ну, вы заботитесь только о разнице в размерах, когда маскируете, поэтому, если вы просто обнуляете регистр, это действительно не имеет значения. Оба являются 6-байтовыми инструкциями и, согласно таблицам инструкций Агнера Фога, о высадке рыцарей:

  • Оба выполняются на одном и том же количестве портов (FP0 или FP1),
  • Оба декодируют до 1 мкоп
  • Оба имеют минимальную задержку 2 и обратную пропускную способность 0,5.
    (Обратите внимание, что этот последний пункт подчеркивает серьезный недостаток KNL - все векторные инструкции имеют задержку не менее 2 тактов, даже простые, которые имеют задержку в 1 цикл на других микроархитектурах.)

Там нет явного победителя, но компиляторы, кажется, предпочитают VPXORD так что я бы тоже придерживался этого.

Как насчет VPXORD / VPXORQ против VXORPS / VXORPD? Ну, как вы упоминаете в этом вопросе, инструкции с упакованными целыми числами обычно могут выполняться на большем количестве портов, чем их аналоги с плавающей запятой, по крайней мере на процессорах Intel, что делает первое предпочтительным. Тем не менее, это не относится к Knights Landing. Будь то упакованное целое число или число с плавающей запятой, все логические инструкции могут выполняться как на FP0, так и на FP1, и имеют одинаковую задержку и пропускную способность, поэтому теоретически вы можете использовать любой из них. Кроме того, поскольку обе формы команд выполняются на блоках с плавающей запятой, штраф за пересечение доменов (задержка пересылки) не взимается, как вы видели бы в других микроархитектурах. Мой вердикт? Придерживайтесь целочисленной формы. Это не пессимизация в KNL, а выигрыш при оптимизации под другие архитектуры, поэтому будьте последовательны. Это меньше, вы должны помнить. Оптимизация достаточно сложна, как она есть.

Кстати, то же самое верно, когда дело доходит до выбора между VMOVAPS а также VMOVDQA64, Обе они являются 6-байтовыми инструкциями, они имеют одинаковую задержку и пропускную способность, они оба выполняются на одних и тех же портах, и нет никаких задержек обхода, о которых вы должны беспокоиться. Для всех практических целей это можно рассматривать как эквивалент при нацеливании на Рыцари.

И, наконец, вы спросили, "достаточно ли умен процессор, чтобы не создавать ложные зависимости от предыдущих значений регистров ZMM, когда [вы] очищаете их с помощью VPXORD / VPXORQ "Ну, я не знаю наверняка, но я так себе представляю. XOR регистров с самим собой, чтобы очистить его, уже давно стал идиомой, и, как известно, его распознают другие процессоры Intel, так что я могу Не представляю, почему этого не будет в KNL. Но даже если это не так, это все равно самый оптимальный способ очистки регистра.

Альтернативой может быть что-то вроде перемещения значения 0 из памяти, что является не только существенно более длинной инструкцией для кодирования, но также требует от вас уплаты штрафа за доступ к памяти. Это не будет победой... если, возможно, вы не были связаны с пропускной способностью, так как VMOVAPS с операндом памяти выполняется на другом блоке (выделенном блоке памяти, а не на любом из блоков с плавающей запятой). Тем не менее, вам понадобится довольно убедительный тест для обоснования такого решения по оптимизации. Это определенно не стратегия "общего назначения".

Или, может быть, вы могли бы сделать вычитание реестра с самим собой? Но я сомневаюсь, что это с большей вероятностью будет признано свободным от зависимостей, чем XOR, и все остальное в характеристиках выполнения будет таким же, так что это не является веской причиной для отказа от стандартной идиомы.

В обоих этих случаях фактор практичности вступает в игру. Когда дело доходит до толчка, вы должны написать код, чтобы другие люди могли его читать и поддерживать. Так как это навсегда заставит всех, кто читает ваш код, запнуться, у вас должна быть действительно веская причина сделать что-то странное.


Следующий вопрос: должны ли мы повторно выдавать VPXORD инструкции, или мы должны скопировать один обнуленный регистр в другие?

Что ж, VPXORD а также VMOVAPS имеют равные задержки и пропускную способность, декодируют до одного и того же числа мопов и могут работать на том же количестве портов. С этой точки зрения это не имеет значения.

Как насчет данных зависимости? Наивно можно предположить, что повторное выполнение XOR лучше, поскольку перемещение зависит от исходного XOR. Возможно, именно поэтому Clang предпочитает повторное выполнение XOR и почему GCC предпочитает планировать другие инструкции между XOR и MOV. Если бы я писал код быстро, без каких-либо исследований, я бы, вероятно, написал его так, как это делает Кланг. Но я не могу точно сказать, является ли это наиболее оптимальным подходом без эталонов. И поскольку ни у кого из нас нет доступа к процессору Knights Landing, их будет нелегко найти.:-)

Эмулятор разработчика программного обеспечения Intel поддерживает AVX-512, но неясно, будет ли это симулятор с точным циклом, который подойдет для тестирования производительности и оптимизации. В этом документе одновременно указывается, что это так ("Intel SDE полезен для анализа производительности, настройки разработки компилятора и разработки приложений для библиотек".) И что это не так ("Обратите внимание, что Intel SDE является программным эмулятором и в основном используется для эмуляции будущих инструкций. Он не является точным циклом и может быть очень медленным (до 100х). Это не эмулятор с точной производительностью. "). Нам нужна версия IACA, которая поддерживает Knights Landing, но, увы, этого не произошло.


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

Во многих отношениях мы видели, что это происходит из-за уникальных аспектов микроархитектуры Knights Landing. В частности, тот факт, что большинство векторных инструкций выполняется на одном из двух модулей с плавающей запятой, и что они имеют идентичные задержки и пропускную способность, подразумевается, что нет штрафов за пересечение домена, о которых вам нужно беспокоиться, а вы - нет особое преимущество в том, что команды с упакованными целыми числами предпочтительнее команд с плавающей точкой. Вы можете увидеть это на базовой диаграмме (оранжевые блоки слева - это две векторные единицы):

Схема / схема ядра микропроцессора Intel's Knights Landing, показывающая, что имеется только 2 векторных блока

Используйте любую последовательность инструкций, которая вам больше нравится.

Я собрал простую C-программу тестирования с использованием встроенных функций и скомпилировал с ICC 17 - сгенерированный код, который я получаю для обнуления 4 регистров zmm (в -O3) является:

    vpxord    %zmm3, %zmm3, %zmm3                           #7.21
    vmovaps   %zmm3, %zmm2                                  #8.21
    vmovaps   %zmm3, %zmm1                                  #9.21
    vmovaps   %zmm3, %zmm0                                  #10.21
Другие вопросы по тегам