TL; DR резюме: xor same, same
является лучшим выбором для всех процессоров. Никакой другой метод не имеет никакого преимущества перед ним, и он имеет по крайней мере некоторое преимущество перед любым другим способом. Это официально рекомендовали Intel и AMD. В 64-битном режиме все еще используйте xor r32, r32
, потому что writing a 32-bit reg zeros the upper 32. xor r64, r64
- это пустой байт, потому что ему нужен префикс REX.
Поиск нужного векторного регистра обычно лучше всего сделать с помощью pxor xmm, xmm
. Обычно это то, что делает gcc (даже перед использованием с инструкциями FP).
xorps xmm, xmm
может иметь смысл. Это один байт короче, чем pxor
, но xorps
нужен порт выполнения 5 на Intel Nehalem, а pxor
может работать на любом порту (0/1/5). (Задержка задержки байпаса Nehalem между целыми и FP обычно не актуальна, поскольку выполнение вне порядка обычно может скрыть ее в начале новой цепочки зависимостей).
В микроархитектурах семейства SnB ни один из вариантов обхода нуля не требует исполнения порта. В AMD и pre-Nehalem P6/Core2 Intel, xorps
и pxor
обрабатываются одинаково (как векторные целые инструкции).
Использование версии AVX вектора векторной команды 128b также является верхней частью регистра, поэтому vpxor xmm, xmm, xmm
является хорошим выбором для обнуления YMM (AVX1/AVX2) или ZMM (AVX512) или любого будущего векторного расширения. vpxor ymm, ymm, ymm
не требует лишних байтов для кодирования и работает одинаково. Для обнуления ZMM AVX512 потребуются дополнительные байты (для префикса EVEX), поэтому предпочтение следует отдавать XMM или YMM.
Некоторые процессоры признают sub same,same
как обнуление идиомы, как xor
, но все процессоры, которые распознают любые обнуление идиомы признают xor
. Просто используйте xor
, поэтому вам не нужно беспокоиться о том, какой процессор распознает, какая идиома обнуляется.
xor
(будучи признанным обнулением идиома, в отличии от mov reg, 0
) имеют некоторые очевидные и некоторые тонкие преимущества (сводный список, то я буду расширять на них):
- меньшего кодового размер, чем
mov reg,0
. (Все процессоры)
- позволяет избежать штрафов с частичным регистратором для последующего кода. (Семейство Intel P6 и семейство SnB).
- не использует исполнительный блок, экономя энергию и освобождая ресурсы выполнения. (Intel SnB-family)
- меньший uop (без непосредственных данных) оставляет место в кэше кэш-линии для ближайших инструкций по заимствовать при необходимости. (Семейство Intel SnB).
- doesn't use up entries in the physical register file. (Intel SnB-family (и P4), по крайней мере, возможно, AMD, так как они используют аналогичный проект PRF вместо сохранения состояния регистрации в ROB, таком как микроархитектура семейства Intel P6.)
Меньший размер машины код (2 байта вместо 5) всегда является преимуществом: Более высокая плотность кода приводит к меньшему количеству промахов кэша инструкций, и принеси лучше инструкции и потенциально декодировать пропускную способность.
Выгода не используется блоком выполнения для исключающего на Intel SnB семейство микроархитектура незначительна, но позволяет экономить энергию. Скорее всего, это имеет значение на SnB или IvB, у которых есть только три порта выполнения ALU. Haswell и позже имеют 4 порта выполнения, которые могут обрабатывать целые инструкции ALU, в том числе mov r32, imm32
, поэтому при идеальном принятии решений планировщиком (что не происходит на практике), HSW все равно может поддерживать 4 часа в час, даже когда все они нуждаются в выполнении порты.
Для получения более подробной информации см. my answer on another question about zeroing registers.
Bruce Dawson's blog post, что Майкл Petch связаны (в комментарии по этому вопросу) указывает на то, что xor
обрабатывается на этапе регистровой переименовывать без необходимости блок исполнения (ноль Uops в слитых области), но пропустил тот факт, что он по-прежнему один uop в объединенном домене. Современные процессоры Intel могут выпускать & уйти в отставку с 4-мя фьюзическими доменами за часы. Вот откуда берутся 4 нули за лимит часов. Повышенная сложность аппаратного переименования регистров является лишь одной из причин ограничения ширины дизайна до 4. (Брюс написал несколько очень хороших сообщений в блоге, например, свою серию на FP math and x87/SSE/rounding issues, которую я очень рекомендую).
На 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, когда изменяются высокие 8 бит (AH
), а затем считывается весь регистр, а Haswell даже удаляет это.
От microarch руководства Agner тумана, в пг 98 (Pentium M раздел, на который ссылается последующими разделами, включая SnB):
Процессор распознает XOR регистр с самими собой, как установкой его к нулю. Специальный тег в регистре запоминает, что верхняя часть регистра равна нулю, так что EAX = AL.Этот тег запоминается даже в цикле:
; Example 7.9. Partial register problem avoided in loop
xor eax, eax
mov ecx, 100
LL:
mov al, [esi]
mov [edi], eax ; No extra uop
inc esi
add edi, 4
dec ecx
jnz LL
(от pg82): Процессор помнит, что верхние 24 бита EAX равны нулю, пока вы не получаете прерывания, misprediction или другой событие сериализации.
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
/читать полный реестр:
...
call some_func
xor ecx,ecx ; zero *before* the test
test eax,eax
setnz cl ; cl = (some_func() != 0)
add ebx, ecx ; no partial-register penalty here
Это оптимальная производительность на все процессоры (нет киосков, сливающейся микрооперация, или ложных зависимости).
Все сложнее, если вы не хотите выполнять xor перед инструкцией по установке флага. например вы хотите разветвиться на одном условии, а затем setcc на другое условие из тех же флагов. например cmp/jle
, sete
, и у вас либо нет запасного регистра, либо вы хотите полностью удалить xor
из незанятого кода.
Нет признанных идиом обнуления, которые не влияют на флаги, поэтому лучший выбор зависит от целевой микроархитектуры. На Core2 вставка слияния uop может привести к остановке в 2 или 3 цикла. Это дешевле на SnB, но я не тратил много времени, пытаясь измерить. Использование mov reg, 0
/setcc
будет иметь существенный штраф для более старых процессоров Intel и все еще будет несколько хуже на более новой Intel.
Использование setcc
/movzx r32, r8
, вероятно, является лучшей альтернативой для семейств Intel P6 & SnB, если вы не можете xor-zero перед инструкцией по установке флага. Это должно быть лучше, чем повторять тест после обнуления нуля. (Даже не рассматривайте sahf
/lahf
или pushf
/popf
). IvB может устранить movzx r32, r8
(т. Е. Обрабатывать его с помощью переименования регистров без блока выполнения или латентности, например, xor-zeroing). Haswell, а затем устранить только регулярные mov
инструкции, так movzx
принимает исполнительный блок и имеет ненулевую задержку, что делает тест/setcc
/movzx
хуже xor
/тест/setcc
, но до сих пор, по крайней мере так же хорошо, как тест/mov r,0
/setcc
(и намного лучше на старых процессорах).
Использование setcc
/movzx
без обнуления сначала плохое для AMD/P4/Silvermont, поскольку они не отслеживают оттиски отдельно для субрегистров. Было бы ложное описание старого значения регистра. Использование mov reg, 0
/setcc
для обнуления/зависимости может быть наилучшей альтернативой, когда xor
/test/setcc
не является вариантом.
Конечно, если вы не хотите, чтобы выходной сигнал setcc
был шире 8 бит, вам не нужно ничего нуля. Однако будьте осторожны с ложными зависимостями от CPU, отличных от P6/SnB, если вы выберете регистр, который недавно был частью длинной цепи зависимостей. (И остерегайтесь вызывает частичную рег стойло или дополнительный моп, если вы вызываете функцию, которая может сохранить/восстановить регистр, который вы используете часть.)
and
с немедленным нуля не является специальным - определяется как независимая от старого значения для любых процессоров, о которых я знаю, поэтому он не нарушает сети зависимостей. Он не имеет преимуществ по сравнению с xor
и многими недостатками.
См http://agner.org/optimize/ для microarch документации, в том числе, которые обнуление идиомы признаются как нарушение зависимостей (например, sub same,same
на некоторых, но не всех процессоров, в то время как xor same,same
признается на всех.) mov
делает разорвать цепочку зависимостей на старое значение register (независимо от исходного значения, ноль или нет, так как это работает mov
). xor
только разрывает цепи зависимостей в специальном случае, где src и dest являются одним и тем же регистром, поэтому mov
исключен из списка специально признанных зависимых выключателей. (Кроме того, поскольку он не признается в качестве обнуления идиомы, с другими преимуществами, которые несут.)
Интересно, что самый старый дизайн P6 (PPro) не признать xor
-zeroing как зависимость выключатель, только идиома с нулевым значением для избежания неполных регистров, поэтому в некоторых случаях стоило использовать как. (См. Пример 6.17 Агнера Фога в своем микроархите pdf. Он утверждает, что это относится также к P2, P3 и даже (раннему?) PM, но я скептически отношусь к этому. A comment on the linked blog post говорит, что это был только PPro, который имел этот надзор. кажется, действительно маловероятно, что несколько поколений семьи P6 существовали без признания исключающего обнуления как DEP выключатель.)
Если это действительно делает ваш код более хорошим или сохраняет инструкцию, то конечно, ноль с mov
, чтобы избежать прикосновений к флаги, если вы не вводите проблему производительности, отличную от размера кода. Тем не менее, избегание флагов слияния является единственной разумной причиной для использования xor
.
Возможно, вы захотите прочитать эту статью [https://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/) –
xor vs mov: http : //stackoverflow.com/questions/1135679/does-using-xor-reg-reg-give-advantage-over-mov-reg-0 –