Что является лучшим способом, чтобы установить регистр в ноль в ассемблере для x86: операции XOR, MOV или и?



все следующие инструкции делают то же самое: set %eax к нулю. Какой путь является оптимальным (требующим наименьшего количества циклов машины)?



xorl   %eax, %eax
mov , %eax
andl , %eax
990   1  

1 ответ:

TL; DR резюме:xor same, same - это лучший выбор для всех процессоров. Никакой другой метод не имеет никакого преимущества перед ним, и он имеет по крайней мере некоторое преимущество перед любым другим методом. Это официально рекомендовано Intel и AMD. В 64-битном режиме, по-прежнему использовать xor r32, r32, потому что написание 32-битных reg нулей верхний 32. xor r64, r64 это пустая трата байта, потому что ему нужен префикс REX.

обнуление векторного регистра обычно лучше всего делать с помощью pxor xmm, xmm. Это обычно то, что делает gcc (даже перед использованием с инструкциями FP).

xorps xmm, xmm может иметь смысл. Это на один байт короче, чем pxor, а xorps требуется порт выполнения 5 на Intel Nehalem, в то время как pxor может работать на любом порту (0/1/5). (Задержка задержки обхода Nehalem 2c между integer и FP обычно не имеет значения, потому что выполнение вне порядка обычно может скрыть его в начале новой цепочки зависимостей).

на SnB-семейство микроархитектур, ни один вкус XOR-обнуления даже не нуждается в порту выполнения. На AMD и pre-Nehalem P6 / Core2 Intel,xorps и pxor обрабатываются таким же образом (как векторных целочисленных инструкций).

используя версию AVX векторной инструкции 128b нули в верхней части reg, так что vpxor xmm, xmm, xmm хороший выбор для обнуления YMM(AVX1/AVX2) или ZMM (AVX512), или любого будущего векторного расширения. vpxor ymm, ymm, ymm не принимает никаких дополнительных байтов для кодирования, хотя и работает так же. В AVX512 ЗММ обнуление потребует дополнительных байтов (для префикса EVEX), поэтому обнуление XMM или YMM должно быть предпочтительным.


некоторые процессоры признать sub same,same как обнуление идиомы, как xor, а все процессоры, которые распознают любые обнуляющие идиомы, распознают xor. Просто используйте xor поэтому вам не нужно беспокоиться о том, какой процессор распознает, какая идиома обнуления.

xor (будучи признанной идиомой обнуления, в отличие от mov reg, 0) имеет некоторые очевидные и некоторые тонкие преимущества (сводный список, затем я буду расширять их):

  • меньший размер кода, чем mov reg,0. (Все Процессоры)
  • избегает штрафов частичного регистра для более позднего кода. (Intel P6-family и SnB-family).
  • не использует блок исполнения, сохраняя силу и освобождая вверх по ресурсам исполнения. (Intel SnB-family)
  • меньший uop (без непосредственных данных) оставляет место в кэш-строке uop для соседних инструкций, чтобы заимствовать, если это необходимо. (Интел СНБ-семья).
  • не использует записи в файле физического регистра. (Intel SnB-family (и P4) по крайней мере, возможно, AMD, так как они используют аналогичный дизайн PRF вместо того, чтобы сохранять состояние регистра в ROB, как микроархитектуры Intel P6-family.)

меньший размер машинного кода (2 байта вместо 5) всегда является преимуществом: более высокая плотность кода приводит к меньшему количеству пропусков команд-кэша и лучше инструкция извлекает и потенциально декодирует полосу пропускания.


пользу не используя исполнительную единицу для xor на Микроархитектурах семейства Intel SnB-это незначительно, но экономит электроэнергию. Это, скорее всего, будет иметь значение на SnB или IvB, которые имеют только 3 порта выполнения ALU. Haswell и более поздние версии имеют 4 порта выполнения, которые могут обрабатывать целочисленные инструкции ALU, включая mov r32, imm32, поэтому при идеальном принятии решений планировщиком (что на практике не происходит), HSW может все еще поддерживайте 4 uops в часы, даже когда им всем нужны порты выполнения.

посмотреть мой ответ на другой вопрос об обнулении регистров для более подробной информации.

сообщение в блоге Брюса Доусона что Майкл Петч связал (в комментарии к вопросу) указывает на то, что xor обрабатывается на этапе register-rename без необходимости в единице выполнения (ноль uops в несвязанном домене), но пропустил тот факт, что это все еще один uop в домен слит. Современные процессоры Intel могут выдавать и выводить на пенсию 4 uops с плавленым доменом за часы. Вот откуда берутся 4 нуля на тактовый лимит. Повышенная сложность аппаратного переименования регистра является лишь одной из причин ограничения ширины конструкции до 4. (Брюс написал несколько очень отличных сообщений в блоге, таких как его серия на FP math и x87 / SSE / вопросы округления, что я очень рекомендую).


на бульдозере AMD-семья Процессоры,mov immediate работает на тех же самых целочисленных портах выполнения EX0/EX1, что и xor. mov reg,reg может также работать на AGU0 / 1, но это только для копирования регистра, а не для настройки из непосредственных. Так что AFAIK, на AMD единственное преимущество xor over mov короче кодирования. Это также может сэкономить физические ресурсы регистра, но я не видел никаких тестов.


распознаются обнуляющие идиомы избегайте частичной регистрации штрафов на процессорах Intel, которые переименовывают частичные регистры отдельно от полных регистров (семейства P6 и SnB).

xor будет пометьте регистр как имеющий обнуленные верхние части, так что xor eax, eax/inc al/inc eax уклоняется от обычного частичного регистра наказание, которое предварительно Ивб процессоры. Даже без xor, IvB нуждается только в слиянии uop, когда высокие 8 бит (AH) изменяются, а затем весь регистр считывается, и Хасвелл даже удаляет это.

из руководства микроархива Агнера Фога, pg 98 (раздел Pentium M, на который ссылаются более поздние разделы, включая SnB):

процессор распознает операция "исключающее ИЛИ" регистра с самим собой, как установка его к нулю. Специальный тег в регистре запоминает, что высокая часть регистр равен нулю, так что 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 равны нулю до тех пор, пока вы не получите прерывания, неправильного предсказания или другое событие сериализации.

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

это имеет оптимальную производительность на всех процессорах (без остановок, слияния uops или ложных зависимостей).

все сложнее, когда вы не хотите 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 опередить инструкцию по установке флага. Это должно быть лучше, чем повторять тест после обнуления xor. (Даже не думайте sahf/lahf или pushf/popf). IvB может устранить movzx r32, r8 (т. е. обрабатывать его с переименованием регистра без единицы выполнения или задержки, как XOR-обнуление). Haswell и позже только устранить регулярные mov инструкции movzx принимает казнь блок и имеет ненулевую задержку, делая тест/setcc/movzx хуже xor/проверки/setcc, но все равно по крайней мере так хорошо, как тест/mov r,0/setcc (и гораздо лучше на старых процессорах).

используя setcc/movzx без пристрелки-первых, это плохо на AMD/Р4/микроархитектуре Silvermont, потому что они не отслеживают депс отдельно для подгрупп регистров. Было бы ложное dep на старом значении регистра. Используя mov reg, 0/setcc для обнуления / разбиения зависимостей, вероятно, лучше всего альтернатива, когда xor/проверки/setcc не вариант.

конечно, если вам не нужен setccвыход должен быть шире, чем 8 бит, вам не нужно ничего обнулять. Однако остерегайтесь ложных зависимостей от процессоров, отличных от P6 / SnB, если вы выбираете регистр, который недавно был частью длинной цепочки зависимостей. (И остерегайтесь вызывать частичную остановку reg или дополнительный uop, если вы вызываете функцию, которая может сохранить / восстановить регистр, который вы используете часть из.)


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

см.http://agner.org/optimize/ для микроархивной документации, в том числе для которой обнуление идиом распознается как разрыв зависимостей (например,sub same,same находится на некоторых, но не на всех процессорах, в то время как xor same,same это прям по всем.)mov разрывает цепочку зависимостей от старого значения регистра (независимо от исходного значения, нулевого или нет, потому что так mov работает). xor только разрывает цепочки зависимостей в специальном случае, когда src и dest являются одним и тем же регистром, поэтому mov в список специально признал зависимость-выключатели. (Кроме того, потому что он не распознается как обнуляющая идиома, с другими преимуществами, которые нести.)

интересно, что самый старый дизайн P6 (PPro) не узнать xor-обнуление в качестве прерывателя зависимостей, только как идиома обнуления для целей избежания частичных регистров, поэтому в некоторых случаях стоило использовать и. (См. Пример палочек agner туман 6.17. в его microarch в формате PDF. Он утверждает, что это также относится к P2, P3 и даже (рано?) ПМ, но я к этому отношусь скептически. комментарий к связанному сообщению в блоге говорит он был только PPro, который имел этот надзор. Кажется действительно маловероятным, что несколько поколений семейства P6 существовали без признания XOR-обнуления в качестве выключателя dep.)


если это действительно делает ваш код лучше и сохраняет инструкции, то конечно, ноль с mov чтобы не касаться флагов, пока вы не вводите проблему производительности, отличную от размера кода. Избегая clobbering флаги является единственной разумной причиной для не использования xor, хотя.

Comments

    Ничего не найдено.