Проверьте, равен ли регистр нулю с помощью CMP reg, 0 против OR reg, reg?
Есть ли разница в скорости выполнения, используя следующий код:
cmp al, 0 je done
и следующее:
or al, al jz done
Я знаю, что инструкции JE и JZ одинаковы, а также то, что использование OR дает улучшение размера одного байта. Тем не менее, я также обеспокоен скоростью кода. Похоже, что логические операторы будут быстрее SUB или CMP, но я просто хотел убедиться. Это может быть компромисс между размером и скоростью или беспроигрышный (конечно, код будет более непрозрачным).
- Сделать муравей тихий без флага -q?
- Дополнительная информация о макете памяти исполняемой программы (процесса)
- Код C ++ для проверки гипотезы Collatz быстрее, чем assembly вручную - почему?
- Не удалось загрузить файл или сборку HRESULT: 0x80131515 (При добавлении controllerа в проект MVC с ссылками на сборку на сетевом диске)
- Как скомпилировать и запустить программу C в Sublime Text 2?
- как загрузить все сборки из вашего каталога / bin
- Что регистрирует сохранение в соглашении вызова ARM C?
- Как загрузить сборку в AppDomain со всеми ссылками рекурсивно?
- Как я могу просмотреть MSIL / CIL, сгенерированный компилятором C #? Почему это называется сборкой?
- Самый быстрый способ вычисления 128-битного целого по модулю 64-разрядного целого числа
- Сколько циклов процессора требуется для каждой инструкции сборки?
- Как загрузить сборку во время выполнения перед событием AssemblyResolve?
- Медленная инструкция jmp
Это зависит от точной последовательности кода, конкретного процессора и других факторов.
Основная проблема с or al, al,
заключается в том or al, al,
что она «изменяет» EAX
, а это означает, что последующая инструкция, использующая EAX
каким-либо образом, может остановиться до завершения этой инструкции. Обратите внимание, что условная ветвь ( jz
) также зависит от инструкции, но производители процессоров выполняют большую работу (предсказание ветвей и спекулятивное выполнение), чтобы смягчить это. Также обратите внимание, что теоретически возможно, что разработчик ЦП для проектирования ЦП, который распознает EAX
, в этом конкретном случае не изменяется, но есть сотни этих особых случаев, и преимущества распознавания большинства из них слишком малы.
Основная проблема с cmp al,0
заключается в том, что она немного больше, что может означать медленное извлечение команды / избыточное давление в кеше, и (если это цикл) может означать, что код больше не подходит для «циклического буфера» процессора.
Как отметил Шутт в комментариях; test al,al
избегает обеих проблем – он меньше, чем cmp al,0
и не изменяет EAX
.
Конечно (в зависимости от конкретной последовательности) значение в AL
должно происходить откуда-то, и если оно исходило из инструкции, которая правильно устанавливает флаги, возможно, будет возможно изменить код, чтобы избежать использования другой команды для установки флагов позже.
Да , есть разница в производительности.
Лучшим выбором для сравнения регистра с нолем на современном x86 является test reg, reg
(если ZF
уже не установлен надлежащим образом инструкцией, устанавливающей reg
). Это как AND reg,reg
но без записи адресата.
or reg,reg
не может использовать макро-предохранитель, добавляет латентность для всего, что читает его позже, и для получения результата нужен новый физический регистр. (Таким образом, он использует ресурсы для переименования реестров, где test
не будет, ограничивая окно инструкции не по заказу процессора ). (Переписывание dst может стать победой в семействе Intel P6, хотя, см. Ниже.)
Результаты флага test reg,reg
/ and reg,reg
/ or reg,reg
идентичны cmp reg, 0
во всех случаях (кроме AF):
-
CF = OF = 0
потому чтоtest
/and
всегда это делает, и дляcmp
потому что вычитание нуля не может переполняться или переноситься. -
ZF
,SF
,PF
устанавливаются в соответствии с результатом (т.е.reg
):reg®
для теста илиreg - 0
для cmp. Таким образом, вы можете протестировать отрицательные целые числа или без знака с высоким битом, установленным при просмотре SF.Или с
jl
, поскольку OF = 0, поэтому условиеl
(SF!=OF
) эквивалентноSF
. Каждый процессор, который может использовать MEST TEST / JL, может также использовать MEST-TEST / JS, даже Core2. Но послеCMP byte [mem],0
, всегда используйте JL, а не JS, чтобы разветвить бит знака.
( AF
не определена после test
, но задана в соответствии с результатом для cmp
. Я игнорирую это, потому что это действительно неясно: единственными потребителями для AF являются ASCII-настраиваемые команды BCD, такие как AAS
, и lahf
/ pushf
.)
test
короче для кодирования, чем cmp
с немедленным 0, во всех случаях, кроме случая cmp al, imm8
который по-прежнему остается двумя байтами. Даже в этом случае test
предпочтительнее по причинам макро-фьюжн (с jle
и аналогичным по Core2), и потому что отсутствие немедленного вообще может помочь повысить плотность кеш-памяти, оставив слот, который может использовать другая команда, если ему нужно больше места (SnB -семейством).
Декодеры процессоров Intel и AMD могут выполнять внутренний test
с cmp
предохранителем и cmp
с некоторыми условными инструкциями ветвления в единую операцию сравнения и ветвления. Это дает максимальную пропускную способность 5 инструкций за цикл, когда происходит макро-слияние, против 4 без макро-слияния. (Для процессоров Intel с Core2.)
Недавние процессоры Intel могут с макросплавкой выполнять некоторые инструкции (например, and
или add
/ sub
), а также test
и cmp
, но or
не являются одним из них. Процессоры AMD могут объединять test
и cmp
с JCC. См. X86_64 – Условия сборки и порядок работы или просто обратитесь непосредственно к документам микроархива Agner Fog для деталей о том, какой CPU может скомпенсировать. test
может быть макро-предохранителем в некоторых случаях, когда cmp
не может, например, с js
.
Почти все простые операторы ALU (побитовые логические, add / sub и т. Д.) Запускаются за один цикл. Все они имеют одинаковую «стоимость» при отслеживании их по конвейеру исполнения вне очереди. Intel и AMD тратят транзисторы на то, чтобы сделать быстрые исполнительные блоки для добавления / суб / всего за один цикл. Да, побитовое OR
или OR
проще и, вероятно, потребляет меньше энергии, но все равно не может работать быстрее, чем один такт.
Кроме того, как указывает Брендан, or reg, reg
добавляет еще один цикл латентности в цепочку зависимостей для следующих инструкций, которые должны считывать регистр.
Тем не менее, на процессорах семейства P6 (PPro / PII до Nehalem) запись адресата может фактически быть преимуществом . Существует ограниченное количество портов чтения регистра для этапа выпуска / переименования для чтения из файла постоянного регистра, но недавно написанные значения доступны непосредственно из ROB. Неправильное переписывание регистра может привести к тому, что он снова будет работать в сети переадресации, чтобы избежать сбоев при чтении регистра. (См . Микроархит Agner Fog pdf .
Компилятор Delphi, как сообщается, использует or eax,eax
, который был разумным выбором в то время, предполагая, что регистрационные стойки были более важными, чем удлинение цепочки отрезков для того, что читает дальше.
К сожалению, компиляторы-писатели в то время не знали будущего, потому что and eax,eax
выполняет в точности эквивалентно or eax,eax
на семействе Intel P6, но хуже на других ургах, потому что and
может использовать макро-предохранитель на Sandybridge- семьи.
Для Core2 / Nehalem (последние 2 урфий семейства P6) test
может быть макро-предохранителем, но and
не может, поэтому (в отличие от Pentium II / III / M) это компромисс между макро-слиянием и, возможно, читать киоски. Уклонение от регистрации в режиме ожидания-чтения остается по-прежнему за счет дополнительной задержки, если значение считывается после тестирования, поэтому test
может быть лучшим выбором, чем and
в некоторых случаях даже до cmov
или setcc
, а не jcc
или на процессорах без макро-слияния.
Если вы настроите что-то быстро на несколько uarches, используйте test
если профилирование не показывает, что стойки для чтения в регистре являются большой проблемой в конкретном случае на Core2 / Nehalem и используют and
фактически исправляют его.
IDK, где произошла or reg,reg
идиома, за исключением, может быть, ее короче набирать. Или, возможно, это было специально предназначено для процессоров P6, чтобы переписать регистр преднамеренно, прежде чем использовать его еще немного. Кодеры в то время не могли предсказать, что это окажется менее эффективным, чем and
для этой цели. Но, очевидно, мы никогда не должны использовать его в test
или в новом коде. (Есть только разница, когда это происходит непосредственно перед jcc
на семействе Sandybridge, но проще просто забыть о or reg,reg
.)
Чтобы проверить значение в памяти , это прекрасно для cmp dword [mem], 0
, но процессоры Intel не могут устанавливать команды установки флага cmp dword [mem], 0
макросов, которые имеют как непосредственный, так и операнд памяти. Если вы собираетесь использовать значение после сравнения в одной из ветвей ветки, вы должны, вероятно, использовать mov eax, [mem]
/ test eax,eax
или что-то еще. Если нет (например, тестирование логического), cmp
с операндом памяти в порядке.
Хотя обратите внимание, что некоторые режимы адресации не будут микшироваться либо в семействе SnB : RIP-relative + немедленный не будет микро-предохранителем в декодерах, или режимы с индексированной адресацией не будут ламинировать. В любом случае, это приводит к 3- cmp dword [rsi + rcx*4], 0
доменами для cmp dword [rsi + rcx*4], 0
/ jne
или [rel some_static_location]
.
Вы также можете проверить значение в памяти с помощью test dword [mem], -1
, но не делать этого. Так как test r/m16/32/64, sign-extended-imm8
недоступен, это хуже размер кода, чем cmp
для чего-либо большего, чем байты. (Я думаю, что идея дизайна заключалась в том, что если вы хотите протестировать только небольшой бит регистра, просто test cl, 1
вместо test ecx, 1
, и примеры использования, такие как test ecx, 0xfffffff0
, достаточно редки, чтобы не было стоит потратить опкод. Тем более, что это решение было принято для 8086 с 16-битным кодом, где это была только разница между imm8 и imm16, а не imm32.)
Я написал -1, а не 0xFFFFFFFF, так что это будет то же самое с byte
или qword
. ~0
это еще один способ написать его.