Какой штраф смешивает EVEX и VEX кодированную схему?

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

Есть несколько вопросов и ответов, как это. Решения зависят от того, как вы программируете (обычно вы должны использовать zeroupper после переходов. Но мой вопрос касается схемы в кодировке EVEX. Насколько нет таких внутренних, как _mm512_zeroupper() Кажется, что нет никакого штрафа при совместном использовании команд в кодировке VEX и EVEX. Однако EVEX является 4-байтовым, а VEX является 3-байтовым, а также длина вектора составляет 512 бит и 256 бит соответственно.

Потому что AVX-512 недоступен (по крайней мере для меня). Я хотел спросить, есть ли что-то, о чем нужно знать, когда мы хотим смешать их.

1 ответ

Решение

Не существует штрафа за смешивание VEX 128 / 256 или EVEX 128 / 256 / 512 на любых текущих процессорах, и нет никаких оснований ожидать каких-либо штрафов на будущие процессоры.

Все кодированные инструкции VEX и EVEX определены для обнуления старших байтов регистра вектора назначения, независимо от максимальной ширины вектора, поддерживаемой ЦП. Это делает их ориентированными на будущее для любых более широких векторов будущего, не нуждаясь в уродливых вещах vzeroupper,


(Однако есть и связанное замедление: см . Комментарии @BeeOnRope о записи полного 512-битного регистра, который действует до vzeroupper в SKX, если вы пишете регистр ZMM явно (не через неявное расширение нуля соответствующего регистра YMM или XMM). Это заставляет каждую более узкую векторную инструкцию действовать так, как если бы она была 512-битной инструкцией для пределов турбо частоты.

Нет ложных зависимостей или дополнительных тактов, просто каждый такт не такой короткий, как с полным турбо. Порт 1 не выключен: у нас все еще есть 3 на часы vpaddd xmm/ymm,

Это "глобальное" состояние ядра: один загрязненный регистр zmm0..15 повредит все ядро, и только vzeroupper/all буду восстанавливать выше турбо. (Но сообщения в zmm16..31 по сообщениям не являются проблемой). Простое написание нижних половин затронутых регистров ZMM с обычными расширяющимися нулями инструкциями YMM VEX или EVEX XMM не выведет вас из этого "режима" / состояния. Даже обнуление идиома, как VEX vpxor или EVEX vpxord загрязненный регистр не помогает. vpxord zmm0,zmm0,zmm0 на самом деле может вызвать проблему, что странно для идиомы обнуления.

Два разных эксперимента, выполненных пользователем Mysticial и BeeOnRope (см. Комментарии), показывают, что физический регистровый файл SKX имеет 512-битные записи; микробенчмарк, который зависит от размера вектора PRF, чтобы найти ILP, обнаружил "умозрительный размер PRF от 150 до 158", то же самое для 256-битных или 512-битных векторов. (И мы знаем, что это правильно для 256-битного размера PRF, основываясь на опубликованной Intel информации о Skylake-клиенте и проведенных там экспериментах.) Таким образом, мы можем исключить режим, в котором для хранения архитектурного регистра ZMM требуется 2 записи PRF и вдвое больше порты чтения / записи.

Мое текущее предположение при объяснении состоит в том, что, возможно, есть верхний PRF 256, физически дальше от планировщика, чем PRF основного вектора, или просто дополнительная ширина, разделяющая то же самое индексирование в PRF основного вектора. Задержка распространения скорости света может ограничить максимальное турбо при включении питания верхнего 256 PRF, если это так. Эта гипотеза аппаратного дизайна не проверяется программным обеспечением, но совместима только с vzeroupper / vzeroall выход из плохого состояния (если я прав, отключение верхней части PRF 256, потому что эта одна инструкция дает нам понять, что она не используется). Я не уверен, почему zmm16..31 не имеет значения для этого, хотя.

Процессор отслеживает, являются ли какие-либо верхние 256 частей ненулевыми, поэтому xsaveopt можно использовать более компактный блок, если это возможно. Взаимодействие с ядром xsaveopt / restore возможно в обработчиках прерываний, но в основном я упоминаю об этом просто как еще одну причину, по которой процессоры отслеживают это.

Обратите внимание, что эта проблема грязного верха ZMM не связана с смешением VEX и EVEX. У вас возникла бы та же проблема, если бы вы использовали кодировки EVEX для всех 128-битных и 256-битных инструкций. Проблема заключается в смешении 512-битных с более узкими векторами в процессорах первого поколения AVX512, где 512-битные являются немного растянутыми, и они более оптимизированы для более коротких векторов. (Завершение работы порта 1 и большая задержка для порта FMA).

Интересно, было ли это намеренно, или это была ошибка дизайна.



Использование VEX, когда это возможно, в коде AVX512 - это хорошо.

VEX сохраняет размер кода по сравнению с EVEX. Иногда при распаковке или конвертации между элементами ширины вы можете получить более узкие векторы.

(Даже если учесть вышеупомянутую проблему со смешением 512-битных с более короткими векторами, 128/256-битные инструкции не хуже, чем их 512-битный эквивалент. Они сохраняют максимальный турбо-режим, когда не должны, но это все.)

VEX-код vpxor xmm0,xmm0,xmm0 это уже самый эффективный способ обнулить регистр ZMM, экономя 2 байта против vpxord zmm0,zmm0,zmm0 и работает по крайней мере так же быстро. MSVC делает это некоторое время, и clang 6.0 (trunk) делает это тоже после того, как я сообщил о пропущенной оптимизации. ( GCC против лягушки на Годболт.

Даже не считая размера кода, он потенциально быстрее на будущих процессорах, которые разделяют команды 512b на две операции по 256b. (См. Ответ Агнера Фога о том, быстрее ли обнуление vxorps на AMD Jaguar/Bulldozer/Zen с регистрами xmm, чем ymm?).

Точно так же, горизонтальные суммы должны сузиться до 256b, а затем до 128b в качестве первых шагов, чтобы они могли использовать более короткие инструкции VEX, а инструкции 128b - меньше моп на некоторых процессорах. Кроме того, перетасовки в переулке часто быстрее, чем пересечение переулка.



Справочная информация о том, почему SSE/AVX является проблемой

См. Также пост Агнера Фога за 2008 год на форумах Intel и остальную ветку с комментариями о дизайне AVX, когда он был впервые анонсирован. Он правильно указывает, что если бы Intel планировала расширение до более широких векторов при разработке SSE, в первую очередь, и предоставила способ сохранить / восстановить полный вектор независимо от ширины, это не было бы проблемой.

Также интересно, что в 2013 году Агнер прокомментировал AVX512 и последовавшее обсуждение на форуме Intel: AVX-512 - это большой шаг вперед - но повторение прошлых ошибок!


Когда AVX был впервые представлен, они могли определить поведение устаревших инструкций SSE, чтобы обнулить верхнюю полосу, что позволило бы избежать необходимости vzeroupper и имеющий сохраненное верхнее состояние (или ложные зависимости).

Соглашения о вызовах просто позволили бы функциям перекрывать верхние полосы векторных регистров (как это уже делают существующие соглашения о вызовах).

Проблема заключается в асинхронном сгущении верхних полос кодами, не поддерживающими AVX, в ядрах. Операционные системы уже должны быть осведомлены о AVX, чтобы сохранить / восстановить полное векторное состояние, и инструкции AVX дают сбой, если операционная система не установила бит в MSR, который обещает такую ​​поддержку. Итак, вам нужно ядро ​​с поддержкой AVX для использования AVX, так в чем же проблема?

Проблема в основном заключается в устаревших двоичных драйверах устройств Windows, которые вручную сохраняют / восстанавливают некоторые регистры XMM "вручную", используя устаревшие инструкции SSE. Если бы это делало неявное обнуление, это нарушило бы состояние AVX для пользовательского пространства.

Вместо того чтобы делать AVX небезопасным для включения в системах Windows с использованием таких драйверов, Intel разработала AVX, чтобы унаследованные версии SSE оставляли верхнюю полосу неизменной. Эффективное выполнение кода SSE, не поддерживающего AVX, требует некоторого наказания.

У нас есть дистрибутив программного обеспечения только для двоичных файлов для Microsoft Windows, чтобы поблагодарить Intel за решение обойтись без штрафов за переход SSE/AVX.

Код ядра Linux должен вызывать kernel_fpu_begin / kernel_fpu_end вокруг кодов-векторов, которые запускают обычный код сохранения / восстановления, который должен знать о AVX или AVX512. Таким образом, любое ядро, созданное с поддержкой AVX, будет поддерживать его в каждом драйвере / модуле (например, RAID5/RAID6), который хочет использовать SSE или AVX, даже в двоичном модуле ядра, не поддерживающем AVX (при условии, что он написан правильно, а не сохранение / восстановление пары xmm или ymm regs).

Windows имеет аналогичный механизм сохранения / восстановления, ориентированный на будущее, KeSaveExtendedProcessorState, что позволяет использовать код SSE/AVX в коде ядра (но не обработчики прерываний). ИДК, почему водители не всегда используют это; возможно это медленно или не существовало сначала. Если он был доступен достаточно долго, то это просто вина авторов / распространителей драйверов только для двоичного кода, а не самих Microsoft.

(ИДК насчет OS X тоже. Если бинарные драйверы сохраняют / восстанавливают регистры xmm "вручную" вместо того, чтобы сообщать ОС, что следующему переключению контекста необходимо восстановить состояние FP, а также целое число, то они тоже являются частью проблемы.)

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