Настройка GCC по умолчанию (-mtune=generic
) включает -mavx256-split-unaligned-load
и -mavx256-split-unaligned-store
, потому что это дает незначительное ускорение на некоторых ЦП (например, Sandybridge первого поколения и некоторых ЦП AMD) в некоторых случаяхкогда память фактически выровнена во время выполнения.
Используйте -O3 -mno-avx256-split-unaligned-load -mno-avx256-split-unaligned-store
, если вы этого не хотите, или лучше, используйте -mtune=haswell
. или используйте -march=native
для оптимизации для собственногокомпьютер.Там нет настройки "generic-avx2".(https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html).
Intel Sandybridge выполняет 256-битную загрузку как один моп, который занимает 2 цикла в порте загрузки. (В отличие от AMD, который декодирует все 256-битные векторные инструкции как 2 отдельных мопа.) Sandybridge имеетпроблема с невыровненными 256-битными загрузками (если адрес фактически выровнен во время выполнения). Я не знаю подробностей и не нашел много конкретной информации о том, что такое замедление. Возможно, потому, что он использует кэш-память банка, с16-байтовые банки? Но IvyBridge лучше обрабатывает 256-битные загрузки и все еще имеет кэш-память в банках.
Согласно сообщению в списке рассылки GCC о коде, который реализует опцию (https://gcc.gnu.org/ml/gcc-patches/2011-03/msg01847.html), "Он ускоряет некоторые тесты SPEC CPU 2006 до 6%."(я думаю, что это для Sandybridge, единственного процессора Intel AVX, который существовал в то время.)
Ноесли память на самом деле выровнена на 32 байта во время выполнения, это чистый недостаток даже на Sandybridge и большинстве процессоров AMD 1 . Так что с этой опцией настройки вы потенциально потеряете только frО, не говоря вашему компилятору о гарантиях выравнивания.И если ваш цикл работает на выровненной памяти в большинстве случаев времени, вам лучше скомпилировать хотя бы этот модуль компиляции с -mno-avx256-split-unaligned-load
или параметрами настройки, которые подразумевают это.
Разделение в программном обеспечении налагаетстоимость все время.Благодаря аппаратной обработке он выровнял регистр совершенно эффективно (за исключением хранилищ на Piledriver 1 ), причем неправильно выровненный регистр может быть медленнее, чем при программном разделении на некоторых процессорах.Так что это пессимистический подход, и он имеет смысл, если действительно вероятно, что данные действительно выровнены во время выполнения, а не просто не всегда выровнены во время компиляции.например, может быть, у вас есть функция, которая вызывается большую часть времени с выровненными буферами, но вы все равно хотите, чтобы она работала для редких / небольших случаев, когда она вызывается с выровненными буферами.В этом случае стратегия разделенной загрузки / хранения не подходит даже для Sandybridge.
Обычно буферы выровнены по 16 байтам, но не выровнены по 32 байтам, потому что malloc
на x86-64 glibc (иnew
в libstdc ++) возвращает 16-байтовые выровненные буферы (потому что alignof(maxalign_t) == 16
).Для больших буферов указатель обычно составляет 16 байтов после начала страницы, поэтому он всегда выравнивается при выравнивании больше 16. Используйте aligned_alloc
.
Обратите внимание, что -mavx
и -mavx2
вообще не изменяет настройки : gcc -O3 -mavx2
по-прежнему настраивает все ЦП, включая те, которые на самом деле не могут выполнять инструкции AVX2.Это довольно глупо, потому что вы должны использовать одну невыровненную 256-битную загрузку, если настраиваете «средний процессор AVX2».К сожалению, gcc не имеет возможности сделать это, и -mavx2
не подразумевает -mno-avx256-split-unaligned-load
или что-то еще. См. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80568 и https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78762 для запросов функций для настройки влияния выбора набора команд .
Именно поэтому вы должны использовать -march=native
для создания двоичных файловдля локального использования, или, возможно, -march=sandybridge -mtune=haswell
для создания бинарных файлов, которые могут работать на широком спектре машин, но, вероятно, будут в основном работать на более новом оборудовании с AVX.(Обратите внимание, что даже процессоры Skylake Pentium / Celeron не имеют AVX или BMI2; возможно, на процессорах с какими-либо дефектами в верхней половине 256-битных исполнительных блоков или файлов регистрации они отключают декодирование префиксов VEX и продают их как младшиеPentium.) Параметры настройки
gcc8.2 следующие.(-march=x
подразумевает -mtune=x
).https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html.
Я проверил в проводнике компилятора Godbolt , скомпилировав с -O3 -fverbose-asm
и просмотрев комментарии, которые включают полный дамп всех подразумеваемых опций.Я включил _mm256_loadu/storeu_ps
функций и простой цикл с плавающей точкой, который может автоматически векторизовать, поэтому мы также можем посмотреть, что делает компилятор.
Использовать -mprefer-vector-width=256
(gcc8) или -mno-prefer-avx128
(gcc7 и более ранние версии)), чтобы переопределить параметры настройки, такие как -mtune=bdver3
и получить 256-битную автоматическую векторизацию, если хотите, а не только с ручной векторизацией.
- default /
-mtune=generic
: оба -mavx256-split-unaligned-load
и -store
.Возможно, все менее и менее уместно, поскольку Intel Haswell и более поздние становятся все более распространенными, а недостаток последних процессоров AMD, я думаю, все еще невелик.Особенно расщепление невыровненных загружает , которые не включены опциями настройки AMD. -march=sandybridge
и -march=ivybridge
: разделите оба.(Мне кажется, я читал, что IvyBridge улучшил обработку невыровненных 256-битных загрузок или хранилищ, поэтому он менее подходит для случаев, когда данные могут быть выровнены во время выполнения.) -march=haswell
и более поздние версии: опция разделения не включена. -march=knl
: опция разделения не включена.(Silvermont / Atom не имеет AVX) -mtune=intel
: опция разделения не включена.Даже с gcc8 автоматическая векторизация с -mtune=intel -mavx
выбирает достижение границы выравнивания для массива назначения для чтения / записи, в отличие от обычной стратегии gcc8, заключающейся в простом использовании unaligned.(Опять же, еще один случай обработки программного обеспечения, который всегда имеет свою стоимость, по сравнению с предоставлением аппаратному обеспечению разрешения в исключительном случае.)
-march=bdver1
(бульдозер): -mavx256-split-unaligned-store
, но не грузит.Он также устанавливает gcc8, эквивалентный gcc7 и более ранним -mprefer-avx128
(автоматическая векторизация будет использовать только 128-битный AVX, но, конечно, встроенные функции могут по-прежнему использовать 256-битные векторы). -march=bdver2
(Piledriver),bdver3
(Steamroller), bdver4
(Экскаватор).такой же, как бульдозер.Они автоматически векторизуют цикл FP a[i] += b[i]
с программной предварительной выборкой и достаточным развертыванием для предварительной выборки только один раз для каждой строки кэша! -march=znver1
(Zen): -mavx256-split-unaligned-store
, но не загружаются, по-прежнему автоматическая векторизация только с128-битный, но на этот раз без предварительной выборки SW. -march=btver2
( AMD Fam16h, также известный как Jaguar ): опция разделения не включена, автоматическая векторизация, как у семейства Bulldozer только с 128-битовые векторы + предварительная выборка SW. -march=eden-x4
(через Eden с AVX2): опция разделения не включена, но опция -march
даже не включает -mavx
, и используется векторизацияmovlps
/ movhps
8-байтовые нагрузки, что действительно глупо.По крайней мере, используйте movsd
вместо movlps
, чтобы сломать ложную зависимость.Но если вы включите -mavx
, он будет использовать 128-битную невыровненную загрузку.Это действительно странное / противоречивое поведение, за исключением случаев, когда для этого есть какой-то странный интерфейс.
options (включается, например, как часть -march = sandybridge, предположительно также для семейства Bulldozer (-march = bdver2 - piledriver)Это не решает проблему, когда компилятор знает, что память выровнена.
Сноска 1: AMD Piledriver имеет ошибку производительности, которая делает 256-битную пропускную способность хранилищаУжасно: даже vmovaps [mem], ymm
выровненных магазинов, работающих по одному на 17-20 часов, согласно микроарху Agner Fog pdf (https://agner.org/optimize/). Этого эффекта нет в бульдозере или Steamroller / Excavator.
Агнер Фог говоритПропускная способность 256-битного AVX в целом (не загружается / не хранится конкретно) на Bulldozer / Piledriver обычно хуже, чем у 128-битного AVX, отчасти потому, что он не может декодировать инструкции в 2-2 моп-паттерне. Steamroller приближает 256-битный кбезубыточность (если это не стоит дополнительных тасовок). Но регистр-регистр vmovaps ymm
инструкции по-прежнему только выигрывают от mov-elimiнация для младших 128 битов на семействе бульдозеров.
Но программное обеспечение с закрытым исходным кодом или двоичные дистрибутивы, как правило, не могут позволить себе роскошь строить с -march=native
на каждой целевой архитектуре, поэтому при создании двоичного файла, который может работать на любом процессоре, поддерживающем AVX, есть компромисс.Получение большого ускорения с помощью 256-битного кода на некоторых процессорах, как правило, того стоит, если на других процессорах нет катастрофических недостатков.
Разделение невыровненных загрузок / хранилищ - это попытка избежать больших проблем на некоторых процессорах.Это требует дополнительной пропускной способности UOP и дополнительных ALU UPS на последних процессорах.Но, по крайней мере, vinsertf128 ymm, [mem], 1
не нужен тасующий модуль на порту 5 в Haswell / Skylake: он может работать на любом векторном порте ALU.(И это не микроплавкий предохранитель, поэтому он стоит 2 мегабайта полосы пропускания внешнего интерфейса.)
PS:
Большая часть кода не компилируется современными компиляторами,поэтому изменение «общей» настройки сейчас займет некоторое время, прежде чем код, скомпилированный с обновленной настройкой, начнет использоваться.(Конечно, большая часть кода компилируется только с -O2
или -O3
, и эта опция в любом случае влияет только на AVX code-gen. Но многие люди, к сожалению, используют -O3 -mavx2
вместо -O3 -march=native
. Поэтому они могут пропустить FMA, BMI1 / 2, popcnt и другие вещи, которые поддерживает их процессор.