Все следующие инструкции делают то же самое: установить %eax
на ноль. Какой способ оптимален (требует наименьшего числа машинных циклов)?
xorl %eax, %eax
mov $0, %eax
andl $0, %eax
performance
assembly
optimization
x86
micro-optimization
balajimc55
источник
источник
Ответы:
TL; DR Резюме :
xor same, same
является лучшим выбором для всех процессоров . Никакой другой метод не имеет перед ним преимуществ, и он имеет хоть какое-то преимущество перед любым другим методом. Это официально рекомендовано Intel и AMD, а также тем, что делают компиляторы. В 64-битном режиме все еще используйтеxor r32, r32
, потому что запись 32-битного регистра обнуляет верхние 32 .xor r64, r64
это пустая трата байта, потому что ему нужен префикс REX.Еще хуже то, что Silvermont распознает только
xor r32,r32
дескрипцию, а не 64-битный размер операнда. Таким образом, даже когда префикс REX все еще требуется, потому что вы обнуляете r8..r15, используйтеxor r10d,r10d
, а неxor r10,r10
.Примеры целочисленных GP:
Обнуление векторного регистра обычно лучше всего выполнять с помощью
pxor xmm, xmm
. Обычно это то, что делает gcc (даже до использования с инструкциями FP).xorps xmm, xmm
может иметь смысл. Это на один байт корочеpxor
, но для этогоxorps
требуется порт выполнения 5 на Intel Nehalem, при этом онpxor
может работать на любом порту (0/1/5). (Задержка задержки обхода 2c Nehalem между целым числом и FP обычно не имеет значения, поскольку выполнение вне очереди обычно может скрыть ее в начале новой цепочки зависимостей).В микроархитектурах семейства SnB ни один из вариантов xor-zeroing даже не требует порта выполнения. На AMD и Intel до Nehalem P6 / Core2
xorps
иpxor
обрабатываются одинаково (как векторные целочисленные инструкции).Использование AVX-версии векторной инструкции 128b обнуляет и верхнюю часть reg, поэтому
vpxor xmm, xmm, xmm
это хороший выбор для обнуления YMM (AVX1 / AVX2) или ZMM (AVX512) или любого будущего векторного расширения.vpxor ymm, ymm, ymm
при этом не требует дополнительных байтов для кодирования и работает так же на Intel, но медленнее на AMD до Zen2 (2 мупа). Обнуление AVX512 ZMM потребует дополнительных байтов (для префикса EVEX), поэтому предпочтительнее обнуление XMM или YMM.Примеры XMM / YMM / ZMM
См. Является ли vxorps-zeroing на AMD Jaguar / Bulldozer / Zen быстрее с регистрами xmm, чем ymm? и
Каков наиболее эффективный способ очистить один или несколько регистров ZMM в Knights Landing?
Наполовину связанный: самый быстрый способ установить значение __m256 для всех битов ONE и эффективно
установить все биты в регистре процессора на 1, также охватывает регистры
k0..7
маски AVX512 . SSE / AVX неvpcmpeqd
работает на многих (хотя по-прежнему нужен uop для записи единиц), но AVX512vpternlogd
для регистров ZMM даже не ломается. Внутри цикла подумайте о копировании из другого регистра вместо повторного создания с помощью мупа ALU, особенно с AVX512.Но обнуление обходится дешево: xor-обнуление регистра xmm внутри цикла обычно так же хорошо, как и копирование, за исключением некоторых процессоров AMD (Bulldozer и Zen), у которых есть удаление mov для векторных регистров, но все же требуется муп ALU для записи нулей для xor -zeroing.
Что особенного в обнулении идиом вроде xor на разных архивах
Некоторые процессоры распознают
sub same,same
идиомы обнуленияxor
, но все процессоры, которые распознают любые идиомы обнуления, распознаютxor
. Просто используйте,xor
чтобы вам не приходилось беспокоиться о том, какой процессор распознает идиому обнуления.xor
(в отличие от общепризнанной идиомы обнуленияmov reg, 0
) имеет некоторые очевидные и некоторые тонкие преимущества (краткий список, затем я расширю их):mov reg,0
. (Все процессоры)Меньший размер машинного кода (2 байта вместо 5) всегда является преимуществом: более высокая плотность кода приводит к меньшему количеству промахов в кэше инструкций и лучшей выборке инструкций и, возможно, пропускной способности декодирования.
Преимущество отказа от использования исполнительного модуля для xor в микроархитектурах семейства Intel SnB незначительно, но экономит электроэнергию. Более вероятно, что это будет иметь значение для SnB или IvB, у которых есть только 3 порта выполнения ALU. Haswell и более поздние версии имеют 4 порта выполнения, которые могут обрабатывать целочисленные инструкции ALU, в том числе
mov r32, imm32
, поэтому при идеальном принятии решений планировщиком (что не всегда происходит на практике) HSW все еще может поддерживать 4 мупа за такт, даже когда всем им нужен ALU порты исполнения.См. Мой ответ на другой вопрос об обнулении регистров для получения более подробной информации.
Сообщение в блоге Брюса Доусона, на которое Майкл Петч указал (в комментарии к вопросу), указывает, что
xor
это обрабатывается на этапе переименования регистра без необходимости в исполнительной единице (ноль мопов в неиспользуемом домене), но упускает тот факт, что это все еще один муп в слитном домене. Современные процессоры Intel могут выдавать и выводить из эксплуатации 4 микропроцессора с объединенными доменами за такт. Отсюда и ограничение в 4 нуля на такт. Повышенная сложность оборудования для переименования регистров - это только одна из причин ограничения ширины дизайна до 4 (Брюс написал несколько очень отличных сообщений в блоге, таких как его серия по математике FP и проблемам x87 / SSE / округления , что я и делаю. настоятельно рекомендую).На AMD Bulldozer семейство процессоров ,
mov immediate
работает на один и те же EX0 / EX1 порты выполнения целого , какxor
.mov reg,reg
также может работать на AGU0 / 1, но только для копирования регистров, а не для немедленной настройки. Так AFAIK, на AMD единственное преимуществоxor
надmov
является короче кодирования. Это также может сэкономить ресурсы физических регистров, но я не видел никаких тестов.Признанные идиомы обнуления позволяют избежать штрафов за частичные регистры на процессорах Intel, которые переименовывают частичные регистры отдельно от полных регистров (семейства P6 и SnB).
xor
будет помечать регистр как имеющие верхние части обнуляются , поэтомуxor eax, eax
/inc al
/inc eax
избегает обычного штрафа частичного регистра , который предварительно IVB процессоров имеют. Даже безxor
этого IvB нужен только uop слияния, когда изменяются старшие 8bits (AH
), а затем читается весь регистр, и Haswell даже удаляет это.Из руководства Agner Fog по микроархитектуре, стр. 98 (раздел Pentium M, на который ссылаются более поздние разделы, включая SnB):
pg82 этого руководства также подтверждает, что
mov reg, 0
он не распознается как идиома обнуления, по крайней мере, в ранних проектах P6, таких как PIII или PM. Я был бы очень удивлен, если бы они потратили транзисторы на его обнаружение на более поздних процессорах.xor
устанавливает флаги , что означает, что вы должны быть осторожны при тестировании условий. Посколькуsetcc
, к сожалению, он доступен только с 8-битным адресатом , обычно вам нужно избегать штрафов за частичную регистрацию.Было бы неплохо, если бы x86-64 перепрофилировал один из удаленных кодов операций (например, AAM) на бит 16/32/64
setcc r/m
, с предикатом, закодированным в 3-битном поле регистра источника поля r / m (способ некоторые другие инструкции с одним операндом используют их как биты кода операции). Но они этого не сделали, да и для x86-32 это не поможет.В идеале вы должны использовать
xor
/ устанавливать флаги /setcc
/ читать полный регистр:Это имеет оптимальную производительность на всех ЦП (без остановок, слияния мопов или ложных зависимостей).
Все сложнее, если вы не хотите выполнять xor перед инструкцией по установке флага . например, вы хотите выполнить ответвление по одному условию, а затем установитьcc по другому условию с теми же флагами. например
cmp/jle
,sete
и вы либо не имеют запасной регистр, или вы хотите сохранить своюxor
команду из не-принятого пути кода в целом.Не существует общепризнанных идиом обнуления, не влияющих на флаги, поэтому лучший выбор зависит от целевой микроархитектуры. В Core2 вставка объединяющего мупа может вызвать остановку на 2 или 3 цикла. Вроде бы дешевле на SnB, но я не тратил много времени на измерения. Использование
mov reg, 0
/setcc
привело бы к значительным потерям на старых процессорах Intel и несколько хуже на новых процессорах Intel.Использование
setcc
/movzx r32, r8
, вероятно, является лучшей альтернативой для семейств Intel P6 и SnB, если вы не можете выполнить xor-zero перед инструкцией по установке флага. Это должно быть лучше, чем повторение теста после обнуления xor. (Даже не считайтеsahf
/lahf
илиpushf
/popf
). IvB может устранитьmovzx r32, r8
(т.е. обработать это с переименованием регистров без исполнительного блока или задержки, как xor-zeroing). Haswell и более поздние версии удаляют только обычныеmov
инструкции, поэтомуmovzx
берет исполнительный блок и имеет ненулевую задержку, что делает test /setcc
/movzx
хуже, чемxor
/ test /setcc
, но все же не хуже, чем test /mov r,0
/setcc
(и намного лучше на старых процессорах).Использование
setcc
/movzx
без обнуления сначала плохо для AMD / P4 / Silvermont, потому что они не отслеживают зависимости отдельно для подрегистров. Было бы ложное изменение старого значения регистра. Использованиеmov reg, 0
/setcc
для обнуления / разрыва зависимостей, вероятно, является лучшей альтернативой, когдаxor
/ test /setcc
не является вариантом.Конечно, если вам не нужно
setcc
, чтобы выходной сигнал был шире 8 бит, вам не нужно ничего обнулять. Однако остерегайтесь ложных зависимостей от ЦП, отличных от P6 / SnB, если вы выбираете регистр, который недавно был частью длинной цепочки зависимостей. (И будьте осторожны, чтобы не вызвать частичную остановку регистрации или дополнительную ошибку, если вы вызываете функцию, которая может сохранить / восстановить регистр, часть которого вы используете.)and
с немедленным нулем не имеет особого значения, поскольку не зависит от старого значения на любых процессорах, о которых я знаю, поэтому он не разрывает цепочки зависимостей. У него нет преимуществ передxor
и много недостатков.Это полезно только для написания микробенчмарков, когда вам нужна зависимость как часть теста задержки, но вы хотите создать известное значение путем обнуления и добавления.
См. Http://agner.org/optimize/ для получения подробной информации о микроархитектуре , в том числе о том, какие идиомы обнуления распознаются как нарушение зависимости (например
sub same,same
, на некоторых, но не на всех процессорах, в то времяxor same,same
как распознается на всех).mov
Действительно разрывает цепочку зависимостей от старого значения регистра (независимо от исходного значения, ноль или нет, потому что такmov
работает).xor
Разрывает цепочки зависимостей только в особом случае, когда src и dest - один и тот же регистр, поэтому онmov
не включен в список специально признанных прерывателей зависимостей. (Кроме того, потому что это не признано идиомой обнуления, с другими преимуществами, которые она несет.)Интересно, что самая старая конструкция P6 (PPro через Pentium III) не распознавала
xor
-zeroing как средство для разрушения зависимостей, а только как идиому обнуления для целей избежания задержек частичного регистра , поэтому в некоторых случаях стоило использовать оба,mov
а затемxor
- обнуление в таком порядке, чтобы разорвать деп, а затем снова обнулить + установить бит внутреннего тега, чтобы старшие биты были равны нулю, поэтому EAX = AX = AL.См. Пример 6.17 Агнера Фога. в его микроархитектуре pdf. Он говорит, что это также относится к P2, P3 и даже (раннему?) PM. В комментарии к связанному сообщению в блоге говорится, что это был только PPro, но я тестировал Katmai PIII, а @Fanael тестировал на Pentium M, и мы оба обнаружили, что это не нарушает зависимость задержки -связанная
imul
цепочка. К сожалению, это подтверждает результаты Агнера Фог.TL: DR:
Если это действительно делает ваш код более приятным или сохраняет инструкции, тогда, конечно, используйте ноль,
mov
чтобы не касаться флагов, если вы не создаете проблемы с производительностью, кроме размера кода. Единственная разумная причина отказа от использования флагов - это избегатьxor
, но иногда вы можете выполнить xor-zero перед установкой флагов, если у вас есть запасной регистр.mov
-Нулевое значение впередиsetcc
лучше для задержки, чемmovzx reg32, reg8
после (кроме Intel, когда вы можете выбирать разные регистры), но хуже для размера кода.источник
mov reg, src
также разрывает цепочки dep для процессоров OO (независимо от того, является ли src imm32[mem]
или другим регистром). Это нарушение зависимостей не упоминается в руководствах по оптимизации, потому что это не особый случай, который происходит только тогда, когда src и dest являются одним и тем же регистром. Это всегда происходит с инструкциями, не зависящими от их назначения. (за исключением реализации Intelpopcnt/lzcnt/tzcnt
ложного депозита на назначении)mov
освобождает, только нулевую задержку. Часть «не использовать порт выполнения» обычно не важна. Пропускная способность слитых доменов может легко стать узким местом, особенно. с грузами или магазинами в смеси.xor r64, r64
что не нужно просто тратить байт. Как вы говорите,xor r32, r32
это лучший выбор, особенно с KNL. См. Раздел 15.7 «Особые случаи независимости» в этом руководстве по микрарху, если вы хотите узнать больше.