Между внутренними компонентами и действительными инструкциями, находящимися внизу, есть некоторое расхождение.
AVX:
Все 3 из них генерируют точно такую же инструкцию,vperm2f128
:
_mm256_permute2f128_pd()
_mm256_permute2f128_ps()
_mm256_permute2f128_si256()
Разница только в типах -которые не существуют на уровне команд.
vperm2f128
- это 256-битная инструкция с плавающей запятой.В AVX нет «настоящих» 256-битных целочисленных SIMD-инструкций.Таким образом, даже если _mm256_permute2f128_si256()
является «целочисленным» свойством, на самом деле это просто синтаксический сахар для этого:
_mm256_castpd_si256(
_mm256_permute2f128_pd(
_mm256_castsi256_pd(x),
_mm256_castsi256_pd(y),
imm
)
);
, который совершает круговой переход из целочисленного домена в домен FP - таким образом, возникают задержки обхода.Каким бы уродливым это ни выглядело, это единственный способ сделать это на земле только для AVX.
vperm2f128
- не единственная инструкция для получения этого лечения, я нахожу по крайней мере 3 из них:
vperm2f128
/ _mm256_permute2f128_si256()
vextractf128
/ _mm256_extractf128_si256()
vinsertf128
/ _mm256_insertf128_si256()
Вместе,похоже, что использование этих встроенных функций заключается в загрузке данных в виде 256-битных целочисленных векторов и их перемешивании в несколько 128-битных целочисленных векторов для целочисленных вычислений.Точно так же, где вы храните 256-битные векторы.
Без этих "хакерских" встроенных функций вам потребуется использовать множество встроенных типов.
В любом случае, компетентный компилятор попытаетсяоптимизировать типы также.Таким образом, он будет генерировать загрузку / сохранение и перемешивание с плавающей точкой, даже если вы используете 256-битные целочисленные загрузки.Это уменьшает количество задержек обхода только до одного слоя.(при переходе от FP-shuffle к 128-разрядному целочисленному вычислению)
AVX2:
AVX2 очищает это безумие, добавляя правильное 256-разрядное целое числоПоддержка SIMD для всего, включая shuffles.
Инструкция vperm2i128
является новой вместе с новым встроенным для нее _mm256_permute2x128_si256()
.
Это, наряду с _mm256_extracti128_si256()
и _mm256_inserti128_si256()
позволяет выполнять 256-битное целочисленное SIMD и фактически полностью оставаться в целочисленной области.
Различие между целочисленными версиями FP одной и той же инструкции связано с задержками обхода.В старых процессорах имели место задержки для перемещения данных из доменов int <-> FP.Хотя сами регистры SIMD не зависят от типа, аппаратная реализация - нет.И есть дополнительная задержка, чтобы получить данные, выводимые инструкцией FP, на вход в целочисленную инструкцию.(и наоборот)
Таким образом, было важно (с точки зрения производительности) использовать правильный тип команды для соответствия фактическому типу данных, с которым он работал.
На новейших процессорах (Skylakeи позже?), кажется, больше нет задержек обхода int / FP в отношении команд shuffle.Несмотря на то, что набор команд все еще имеет это различие, команды перетасовки, которые выполняют одно и то же с разными «типами», вероятно, теперь отображаются в один и тот же элемент управления.