Микроархитектурное обнуление регистра через регистр renamer: производительность по сравнению с mov?

Я читаю в сообщении в блоге, что недавние микроархитектуры X86 также могут обрабатывать идиомы обнуления регистровых регистров (такие как xor-ing регистр с сам) в регистре renamer; в словах автора:

"переименовать регистр также знает, как выполнять эти инструкции - он может обнулить сами регистры."

Кто-нибудь знает, как это работает на практике? Я знаю, что некоторые ISA, такие как MIPS, содержат архитектурный регистр, который всегда установлен на ноль в аппаратном обеспечении; означает ли это, что внутри микроархитектура X86 имеет аналогичные "нулевые" регистры внутри, которые регистрируются при удобстве? Или моя ментальная модель не совсем корректна в отношении того, как этот материал работает микроархитектурно?

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

В основном то, что происходит, это то, что я хотел бы обнулить регистр в цикле в зависимости от условия; это может быть сделано путем распределения архитектурного регистра досрочно для хранения нуля (%xmm3, в данном случае), который не изменяется для всей продолжительности цикла и выполняет в нем следующее:

movapd  %xmm3, %xmm0

или вместо этого с xor трюком:

xorpd   %xmm0, %xmm0

(И синтаксис AT & T).

Другими словами, выбор заключается в подъеме постоянного нуля за пределы цикла или рематериализация его внутри него для каждой итерации. Последний сокращает количество живых архитектурных регистров на единицу и, с предполагаемым особым осознанием случая и обработкой икомы процессором процессором, кажется, что он должен быть таким же быстрым, как и первый (тем более, что эти машины имеют больше физических в любом случае регистры, чем архитектурные регистры, поэтому он должен иметь возможность внутренне выполнять эквивалент того, что я сделал в сборке, вытаскивая постоянный нуль или даже лучше, внутренне, с полной осведомленностью и контролем над своими собственными ресурсами). Но, похоже, это не так, поэтому мне любопытно, сможет ли кто-либо с знаниями архитектуры ЦП объяснить, есть ли для этого хорошая теоретическая причина.

Регистры в этом случае происходят с регистрами SSE, и машина оказывается Ivy Bridge; Я не уверен, насколько важен любой из этих факторов.

Ответы

Ответ 1

Резюме. Вы можете выполнить до четырех инструкций xor ax, ax за цикл по сравнению с более медленными инструкциями mov immediate, reg.

Детали и ссылки:

В Википедии есть хороший обзор переименования регистров в целом: http://en.wikipedia.org/wiki/Register_renaming

Torbj¨orn Granlund тайминги для латентность команд и пропускная способность для Процессоры AMD и Intel x86: http://gmplib.org/~tege/x86-timing.pdf

Agner Fog прекрасно описывает особенности своего исследования Micro-architecture:

8.8 Регистрация и переименование регистра

Переименование регистров контролируется таблицей псевдонимов регистров (RAT) и буфер переупорядочения (ROB)... μops из декодеров и стека двигатель перейдет в RAT через очередь, а затем в ROB-чтение и резервационная станция. RAT может обрабатывать 4 микрофона за такт. RAT может переименовать четыре регистра за такт и может даже переименовать один и тот же регистр четыре раза за один такт.

Особые случаи независимости

Общим способом установки регистра на ноль является XOR'ing его с самим собой или вычитания из него, например. XOR EAX, EAX. Процессор Sandy Bridge распознает, что определенные инструкции не зависят от предшествующего значения регистра, если два регистра операндов совпадают. Этот регистр установлен на ноль на этапе переименования без использования какой-либо исполнительной единицы. Это относится к все следующие инструкции: XOR, SUB, PXOR, XORPS, XORPD, VXORPS, VXORPD и все варианты PSUBxxx и PCMPGTxx, но не PANDN и т.д.

Инструкции, для которых не требуется блок выполнения

Вышеупомянутые специальные случаи, когда регистры устанавливаются в ноль с помощью инструкций, таких как XOR EAX, EAX обрабатываются на этапе переименования/распределения регистров без используя любой исполнительный блок. Это позволяет использовать эти обнуления чрезвычайно эффективны, с пропускной способностью четыре обнуления инструкторов за такт.

Ответ 2

В этом предложении скрыты самые большие затраты на производительность при обнулении:

В основном, что происходит, я хотел бы регистрироваться в цикле в зависимости от условия

Это предложение подразумевает ветвь. Даже если ветвь правильно предсказана, она все равно будет стоить больше, чем обнулить регистр.

Как для переименования регистра...

В процессоре OutOfOrder (OOO) каждый раз, когда вы записываете в регистр, CPU дает вам новый регистр. Если вы выполнили эти три инструкции:

xor eax,eax
add eax,eax
add eax,1

то для первой команды ЦП (если это последний процессор Intel в любом случае) просто обновляет свои сопоставления, чтобы сказать, что eax теперь ссылается на внутренний нулевой регистр. При первом добавлении оно считывается из eax (дважды, поскольку оно используется дважды как вход), а затем обновляет его отображение, чтобы указать на новый регистр и записывает результат в этот регистр. То же самое происходит со вторым добавлением. Таким образом, в течение этих трех инструкций регистр eax изменяется, чтобы указать на три разных физических регистра.

Почему? Из-за этого:

mov eax,[esi]    ; Load from esi
add eax, 1
mov [esi], eax   ; Store to esi
mov eax,[esi+4]  ; Load from esi+4
add eax, 1
mov [esi+4], eax ; Store to esi+4

В процессоре ООО одним из основных ограничений на производительность являются зависимости. Инструкции от одного до трех должны выполняться в порядке. Инструкции от четырех до шести должны выполняться в порядке. Но между этими двумя блоками нет зависимостей. Таким образом, один-три и четыре-шесть могут выполняться параллельно. Но все они относятся к eax.

Нет проблем. Регистрация переименования решает это. Первая и четвертая команды выполняются одновременно. ЦП создает отдельное отображение для eax для каждой точки потока команд, а последующие инструкции работают с этими переименованными регистрами. Это позволяет выполнять два блока инструкций полностью параллельно.

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

Во всяком случае, длинный рассказ короткий, "xor eax, eax" никогда не выполняется, и это круто. Эта оптимизация может быть применена к любой инструкции, которая всегда производит нуль или всегда производит их или что-то еще, но Intel собирается потратить транзисторы на это, когда это имеет значение. Наверное, xorpd еще не делает разреза.

Я писал об этом (http://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/), потому что я думал, что это круто. Мне также понравилась идея о том, что "add" и "sub", которые в основном идентичны инструкциям, могут иметь слегка или значительно отличающуюся производительность из-за этого поведения, хотя и только в том случае, когда регистр вычитается из себя.