2016-05-31 4 views
1

Я компилирую тот же тест, используя gcc -O2 -march = native flags. Тем не менее, интересно, когда я смотрю на objdump, он на самом деле создает некоторые инструкции, такие как vxorpd и т. Д., Которые, я думаю, должны появляться только при включении ftree-vectorize (и O2 не должен включать это по умолчанию?) Если я добавляю флаг -m32 для компиляции в 32-битной инструкции эти упакованные инструкции исчезли. Любой, кто встретил подобные ситуации, мог дать некоторые объяснения? Благодарю.Странное поведение компиляции gcc6.1 -O2

+0

На ваш вопрос будет проще ответить конкретным примером. –

ответ

3

XORPD - классическая инструкция SSE2, которая выполняет побитовое логическое XOR по двум значениям с плавающей запятой с двойной точностью.

VXORPD - это векторная версия той же инструкции. По сути, это классическая инструкция SSE2 XORPD с VEX prefix. Это то, что префикс «V» означает в коде операции. Он был представлен с помощью AVX (Advanced Vector Extensions) и поддерживается в любой архитектуре, поддерживающей AVX. (На самом деле две версии - версия с кодировкой VEX.128, работающая на 128-битных регистрах AVX, и версия с кодировкой VEX.256, которая работает на 256-битных регистрах AVX2.)

Все устаревшие SSE и инструкции SSE2 могут иметь добавленный им префикс VEX, предоставляя им форму из трех операндов и позволяя им взаимодействовать и планировать более эффективно с другими новыми инструкциями AVX. Он также избегает the high cost of transitions between VEX and non-VEX modes. В противном случае эти новые кодировки сохраняют одинаковое поведение. Таким образом, компиляторы обычно генерируют VEX-префиксные версии этих инструкций всякий раз, когда их поддерживает целевая архитектура. Очевидно, что в вашем случае march=native указывает архитектуру, которая поддерживает, как минимум, AVX.

В GCC и Clang вы получите эти инструкции, даже если оптимизация отключена (-O0), поэтому вы обязательно получите их, когда будут включены оптимизации. Ни переключатель -ftree-vectorize, ни какой-либо из других оптимизационных оптимизаторов для векторизации не нужно включать, потому что это фактически не имеет никакого отношения к векторизации вашего кода. Точнее, поток кода не изменился, просто кодировка инструкций.

Вы можете увидеть это с простейшими кода мыслимым:

double Foo() 
{ 
    return 0.0; 
} 
Foo(): 
     vxorpd xmm0, xmm0, xmm0 
     ret 

Так что объясняет, почему вы видите VXORPD и его друг, когда вы скомпилировать 64-битные построить с переключателем -march=native ,

Это оставляет вопрос о том, почему вы не принимаете не см. Его, когда вы бросаете переключатель -m32 (что означает сгенерировать код для 32-разрядных платформ). Команды SSE и AVX по-прежнему доступны при настройке на эти платформы, и я считаю, что они будут использоваться при определенных обстоятельствах, но их нельзя использовать так же часто из-за значительных различий в 32-разрядной ABI. В частности, для 32-разрядного ABI требуется, чтобы значения с плавающей запятой возвращались в стек с плавающей точкой x87. Поскольку для этого требуется использование инструкций с плавающей запятой x87, оптимизатор имеет тенденцию придерживаться тех, за исключением случаев, когда он сильно векторизует секцию кода. Это единственный раз, когда на самом деле имеет смысл перетасовать значения из стека x87 в регистры SIMD и обратно. В противном случае это утечка производительности для практически никакой практической пользы.

Вы также можете увидеть это в действии.Посмотрите, какие изменения в выходе просто бросая -m32 переключатель:

Foo(): 
     fldz 
     ret 

FLDZ является инструкцией x87 FPU для загрузки постоянного нуля в верхней части стека с плавающей точкой, где он готов быть возвращены вызывающего абонента.

Очевидно, что, поскольку вы делаете код более сложным, вы с большей вероятностью измените эвристику оптимизатора и убедите его испустить инструкции SIMD. Вы, скорее всего, все равно, если сможете включить оптимизацию на основе векторизации.

+0

Привет, Коди Грей, спасибо за ваш ответ. Еще один вопрос для наблюдения. Я видел большую скорость из-за этих vex prefixed инструкций при компиляции с использованием fastmath. Вы знаете, как эти инструкции vex-prefix работают лучше, чем исходные инструкции x87? Спасибо – PST

+0

@PST Ну, обычные инструкции SSE, как правило, быстрее, чем инструкции x87. Для этого есть несколько сложных причин. Одним из наиболее значительных является то, что FPU x87 работает с системой на основе стека со всеми ограничениями, связанными со службой, тогда как в реализации SSE используются регистры. Это означает, что время не теряется, толкает/выскакивает значения в стеке или обменивается значениями в разных положениях стека. Другая причина, по которой SSE быстрее, чем x87, - это просто новая реализация и была оптимизирована соответствующим образом. –

+0

Затем, мой ответ уже объясняет, почему инструкции с привилегиями SSE с префиксом VEX быстрее, чем обычные инструкции SSE. Таким образом, вы получаете преимущество от двух улучшений производительности: сначала переключитесь с x87 на SSE, а затем переключитесь с SSE на VES-кодированный SSE. Инженеры Intel должны были справиться с чем-то за последние 15-20 лет. :-) –

1

Просто чтобы добавить к Cody Gray's very good answer, вы можете проверить параметры внутренней защиты gcc, выведя их на ассемблер и включив -fverbose-asm.

Например:

gcc -O2 -fverbose-asm -S -o test.S test.c 

перечислит в test.S все варианты оптимизации позволили на выбранном уровне оптимизации (здесь -O2).

+0

См. Также http://gcc.godbolt.org/, где вы можете увидеть выходной поток компилятора с отключенным шумом. –