ТЛ; др
Используйте _mm256_zeroupper();
или _mm256_zeroall();
вокруг разделов кода с использованием 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: миграция кода SSE в AVX " содержит диаграмму этих состояний:
В состоянии B (режим AVX-256) используются все биты регистров YMM. Когда вызывается инструкция SSE, должен произойти переход в состояние C, и здесь есть штраф. Верхняя половина всех регистров YMM должна быть сохранена во внутреннем буфере, прежде чем SSE сможет запуститься, даже если они оказались нулями. Стоимость переходов составляет порядка 50-80 тактов на оборудовании Sandy Bridge. Существует также штраф от C -> A, как показано на рисунке 2.
Вы также можете найти подробную информацию о штрафе за переключение состояний, вызвавшем это замедление, на стр. 130, Раздел 9.12, «Переходы между VEX и не-VEX режимами» в Руководство по оптимизации Agner Fog (версия обновлена 2014-08-07), ссылка на которую Mystical's answer . Согласно его руководству, любой переход в / из этого состояния занимает «около 70 тактов на Песчаном мосту». Как говорится в документе 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 (Intel® SSE).
В Visual Studio 2010+ (возможно, даже старше) вы получаете это встроенное сimmintrin.h.
Обратите внимание, что обнуление битов другими методами не устраняет штраф - необходимо использовать инструкции VZEROUPPER
или VZEROALL
.
Одно автоматическое решение, реализуемоеКомпилятор Intel должен вставить VZEROUPPER
в начале каждой функции, содержащей код Intel AVX, если ни один из аргументов не является регистром YMM или типом данных __m256
/ __m256d
/ __m256i
, и в конце функций, если возвращаемое значение не является регистром YMM или __m256
/ __m256d
/ __m256i
тип данных.
В дикой природе
Это решение VZEROUPPER
используется FFTW для создания библиотеки с поддержкой как SSE, так и AVX.См. simd-avx.h :
/* 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.