tl; dr
Используйте _mm256_zeroupper();
или _ 2_ вокруг фрагментов кода с использованием AVX (до или после, в зависимости от аргументов функции). Используйте параметр /arch:AVX
только для исходных файлов с AVX, а не для всего проекта, чтобы не нарушить поддержку устаревших кодированных путей кода SSE.
Причина
Я думаю, что лучшее объяснение содержится в статье Intel " Как избежать штрафов за переход AVX-SSE " (PDF). В аннотации говорится:
Переход между 256-битными инструкциями Intel® AVX и устаревшими инструкциями Intel® SSE в программе может привести к снижению производительности, поскольку оборудование должно сохранять и восстанавливать старшие 128 бит регистров YMM.
Разделение кода AVX и SSE на разные блоки компиляции может НЕ помочь, если вы переключаетесь между вызывающим кодом как из объектных файлов с поддержкой SSE, так и из объектных файлов с поддержкой AVX, поскольку переход может происходить при смешивании инструкций AVX или сборки. с любым из (из статьи Intel):
- 128-битные внутренние инструкции
- Встроенная сборка SSE
- Код C / C ++ с плавающей запятой, скомпилированный в Intel® SSE
- Вызов функций или библиотек, которые включают что-либо из вышеперечисленного
Это означает, что могут быть даже штрафы при связывании с внешним кодом с использованием SSE.
Подробности
Инструкциями AVX определены три состояния процессора, и в одном из состояний все YMM регистры разделены, что позволяет использовать нижнюю половину для инструкций SSE. Документ Intel "Intel® AVX State Transitions: Migrating SSE Code to AVX "предоставляет схему этих состояний:
В состоянии B (режим AVX-256) используются все биты регистров YMM. Когда вызывается инструкция SSE, должен произойти переход в состояние C, и здесь есть штраф. Перед запуском SSE верхняя половина всех регистров YMM должна быть сохранена во внутреннем буфере, даже если они нулевые. Стоимость переходов составляет «порядка 50-80 тактов на оборудовании Sandy Bridge». Также существует штраф, идущий от C -> A, как показано на рисунке 2.
Вы также можете найти подробную информацию о штрафах за переключение состояний, вызывающих это замедление, на странице 130, Раздел 9.12, «Переходы между VEX и режимы без VEX "в руководстве по оптимизации Agner Fog (версии обновлено 07.08.2014), на которое есть ссылка в Мистическом ответе. Согласно его руководству, любой переход в / из этого состояния занимает «около 70 тактов на Sandy Bridge». Как говорится в документе Intel, это штраф за переход, которого можно избежать.
Разрешение
Чтобы избежать штрафов за переход, вы можете либо удалить весь унаследованный код SSE, либо дать указание компилятору преобразовать все инструкции SSE в их кодированную VEX форму 128-битных инструкций (если компилятор поддерживает), либо перевести регистры YMM в известное нулевое состояние перед переход между кодом AVX и SSE. По сути, чтобы сохранить отдельный путь кода SSE, вы должны обнулить верхние 128 битов всех 16 регистров YMM (выдавая инструкцию VZEROUPPER
) после любого кода, который использует инструкции AVX. Обнуление этих битов вручную приводит к переходу в состояние A и позволяет избежать дорогостоящих штрафов, поскольку значения YMM не нуждаются в аппаратном хранении во внутреннем буфере. Внутренняя функция, выполняющая эту инструкцию, - _mm256_zeroupper
. Описание этой встроенной функции очень информативно:
Эта встроенная функция полезна для очистки старших битов регистров YMM при переходе между инструкциями Intel® Advanced Vector Extensions (Intel® AVX) и устаревшими инструкциями Intel® Supplemental SIMD Extensions (Intel® SSE). Отсутствует штраф за переход, если приложение очищает старшие биты всех регистров YMM (устанавливает значение «0») с помощью VZEROUPPER
, соответствующей инструкции для этого встроенного компонента, перед переходом между Intel® Advanced Vector Extensions (Intel ® AVX) и устаревшие инструкции Intel® Supplemental SIMD Extensions (Intel® SSE).
В Visual Studio 2010+ (возможно, даже старше) вы получаете это внутреннее с помощью immintrin.h.
Обратите внимание, что обнуление битов другими методами не устраняет штраф - необходимо использовать инструкции VZEROUPPER
или VZEROALL
.
Одно автоматическое решение, реализованное компилятором Intel, заключается в том, чтобы вставить VZEROUPPER
в начало каждой функции, содержащей код Intel AVX, если ни один из аргументов не является регистром YMM или типом данных _10 _ / _ 11 _ / _ 12_ и в конце функций, если возвращаемое значение не является регистром YMM или _13 _ / _ 14 _ / _ 15_ типом данных.
В дикой природе
Это VZEROUPPER
решение используется FFTW для создания библиотеки с поддержкой SSE и AVX. См. simd-avx. :
/* Use VZEROUPPER to avoid the penalty of switching from AVX to SSE.
See Intel Optimization Manual (April 2011, version 248966), Section
11.3 */
#define VLEAVE _mm256_zeroupper
Затем VLEAVE();
вызывается в конце каждой функции с использованием встроенных функций для инструкций AVX.
person
chappjc
schedule
06.02.2015