Использует ли xor reg, reg преимущество над mov reg, 0?

Существует два известных способа установки целочисленного регистра на нулевое значение на x86.

Или

mov reg, 0 

или

 xor reg, reg 

Существует мнение, что второй вариант лучше, поскольку значение 0 не сохраняется в коде и что сохраняет несколько байтов полученного машинного кода. Это, безусловно, хорошо – меньше кэша команд используется, и иногда это может привести к более быстрому выполнению кода. Такой компилятор создает такой код.

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

 add reg, 17 ;do something else with reg here xor reg, reg 

Очевидно, что результат xor будет точно таким же, независимо от исходного значения регистра. Но это процессор способен распознать это?

Я пробовал следующий тест в VC ++ 7:

 const int Count = 10 * 1000 * 1000 * 1000; int _tmain(int argc, _TCHAR* argv[]) { int i; DWORD start = GetTickCount(); for( i = 0; i < Count ; i++ ) { __asm { mov eax, 10 xor eax, eax }; } DWORD diff = GetTickCount() - start; start = GetTickCount(); for( i = 0; i < Count ; i++ ) { __asm { mov eax, 10 mov eax, 0 }; } diff = GetTickCount() - start; return 0; } 

При оптимизации обе эти петли берут ровно одно и то же время. Означает ли это обоснованное доказательство того, что процессор распознает, что нет никакой зависимости от команды xor reg, reg в предыдущей команде mov eax, 0 ? Что может быть лучшим испытанием для проверки этого?

    фактический ответ для вас:

    Справочное руководство по оптимизации архитектуры Intel 64 и IA-32

    Раздел 3.5.1.8 – это то, где вы хотите посмотреть.

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

    Я перестал быть в состоянии исправить свои собственные машины после того, как я продал свой 1966-й универсал. У меня такое же решение с современными процессорами 🙂

    Это будет зависеть от базового микрокода или схемы. Вполне возможно, что ЦП мог бы распознать "XOR Rn,Rn" и просто нуля всех битов, не беспокоясь о содержимом. Но, конечно, он может сделать то же самое с "MOV Rn, 0" . Хороший компилятор в любом случае выберет лучший вариант для целевой платформы, так что это, как правило, только проблема, если вы кодируете в ассемблере.

    Если процессор достаточно умен, ваша зависимость XOR исчезает, так как он знает, что значение не имеет значения, и все равно будет устанавливать его на ноль (опять же это зависит от используемого процессора).

    Тем не менее, я давно не заботился о нескольких байтах или нескольких тактах в моем коде – похоже, микро-оптимизация сошла с ума.

    x86 имеет инструкции переменной длины. MOV EAX, 0 требует одного или двух байтов в кодовом пространстве, чем XOR EAX, EAX.

    На современных процессорах предпочтение отдается шаблону XOR. Он меньше и быстрее.

    Меньше на самом деле имеет значение, потому что во многих реальных нагрузках одним из основных факторов, ограничивающих производительность, являются промахи i-cache. Это не будет зафиксировано в микро-бенчмарке, сравнивая два варианта, но в реальном мире код будет работать немного быстрее.

    И, игнорируя уменьшенные пропуски i-cache, XOR на любом процессоре за последние много лет является той же скоростью или быстрее, чем MOV. Что может быть быстрее, чем выполнение инструкции MOV? Не выполняйте никаких инструкций вообще! На последних процессорах Intel логика отправки / переименования распознает шаблон XOR, «реализует», что результат будет равен нулю, и просто указывает регистр в физическом нулевом регистре. Затем он отбрасывает инструкцию, потому что нет необходимости ее выполнять.

    Конечным результатом является то, что шаблон XOR использует нулевые ресурсы выполнения и может на последних процессорах Intel выполнять «четыре команды за цикл». MOV заканчивается тремя инструкциями за цикл.

    Подробнее см. В этом сообщении в блоге, которое я написал:

    https://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/

    Большинство программистов не должны беспокоиться об этом, но писателям-компиляторам приходится беспокоиться, и хорошо понимать генерируемый код, и это просто круто!

    Я думаю, что на более ранних архитектурах команда mov eax, 0 используемая для того, чтобы взять немного дольше, чем xor eax, eax также … не помню, почему именно. Однако, если у вас много других mov я бы предположил, что вы вряд ли допустите промахи в кеше из-за того, что один литерал хранится в коде.

    Также обратите внимание, что из памяти состояние флагов не одинаково между этими методами, но я могу забыть об этом.

    Как отмечали другие, ответ «кто заботится?». Вы пишете компилятор?

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

    Другая причина, по которой вы не можете сравнить одну инструкцию в цикле, – это то, что весь ваш код будет кэшироваться (в отличие от реального кода). Таким образом, вы использовали большую разницу в размерах между mov eax, 0 и xor eax, eax из-за того, что все это время в L1-кешировании.

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

    Давайте будем гением компьютера.