Если регистры так невероятно быстры, почему у нас их нет больше?


в 32bit, мы имели 8 "общецелевых" регистров. С 64bit сумма удваивается, но она кажется независимой от самого изменения 64bit.
Теперь, если регистры настолько быстры (нет доступа к памяти), почему их больше нет естественно? Разве строители ЦП не должны работать как можно больше регистров в ЦП? Каково логическое ограничение, почему у нас есть только количество, которое у нас есть?

4 81

4 ответа:

есть много причин, по которым у вас не просто огромное количество регистров:

  • они сильно соединены к большинств этапам трубопровода. Для начала вам нужно отследить их срок службы и переслать результаты обратно на предыдущие этапы. Сложность становится неразрешимой очень быстро, и количество вовлеченных проводов (буквально) растет с той же скоростью. Это дорого по площади, что в конечном итоге означает, что это дорого по мощности, цене и производительности после определенного момента.
  • Это занимает пространство кодирования команд. 16 регистров занимает 4 бита для источника и назначения, и еще 4, если у вас есть 3-операнд инструкции (например, ARM). Это очень много места для кодирования набора команд, занятого только для указания регистра. Это в конечном итоге влияет на декодирование, размер кода и снова сложность.
  • есть лучшие способы достижения того же результата...

в эти дни у нас действительно есть много регистров - они просто не запрограммировано специально. Мы есть "регистрация переименования". Хотя вы получаете доступ только к небольшому набору (8-32 регистра), на самом деле они поддерживаются гораздо большим набором (например, 64-256). Затем ЦП отслеживает видимость каждого регистра и выделяет их переименованному набору. Например, вы можете загружать, изменять, а затем хранить в регистре много раз подряд, и каждая из этих операций фактически выполняется независимо в зависимости от промахов кэша и т. д. В руке:

ldr r0, [r4]
add r0, r0, #1
str r0, [r4]
ldr r0, [r5]
add r0, r0, #1
str r0, [r5]

ядра Cortex A9 регистрируют переименование, поэтому первая загрузка к "r0" фактически идет переименованный виртуальный регистр-назовем его"v0". Нагрузка, инкремент и хранение происходят на "v0". Между тем, мы также снова выполняем загрузку/изменение/хранение в r0, но это будет переименовано в "v1", потому что это полностью независимая последовательность с использованием r0. Допустим, нагрузка от указателя в "r4" остановилась из-за промаха кэша. Это нормально - нам не нужно ждать, пока "r0" будет готов. Поскольку он переименован, мы можем запустить следующую последовательность с "v1" (также сопоставленным с r0) - и, возможно, это хит кэша, и у нас просто была огромная победа в производительности.

ldr v0, [v2]
add v0, v0, #1
str v0, [v2]
ldr v1, [v3]
add v1, v1, #1
str v1, [v3]

Я думаю, что x86 до гигантского количества переименованных регистров в эти дни (ballpark 256). Это означало бы иметь 8 бит по 2 для каждой инструкции, чтобы просто сказать, что такое источник и назначение. Это значительно увеличило бы количество проводов, необходимых через сердечник, и его размер. Таким образом, есть сладкое пятно вокруг 16-32 регистров, на которые большинство дизайнеров согласились, а для неупорядоченных конструкций ЦП зарегистрируйте переименование-это способ смягчить его.

Edit: важность внепланового исполнения и переименования регистра на этом. После того, как у вас есть OOO, количество регистров не имеет большого значения, потому что они просто "временные теги" и переименовываются в гораздо больший набор виртуальных регистров. Вы не хотите, чтобы число было слишком маленьким, потому что становится трудно писать небольшие кодовые последовательности. Это проблема для x86-32, потому что ограниченные 8 регистров означает много временные устройства в конечном итоге проходят через стек, и ядро нуждается в дополнительной логике для пересылки операций чтения/записи в память. Если у вас нет OOO, вы обычно говорите о небольшом ядре, и в этом случае большой набор регистров является плохим преимуществом затрат/производительности.

таким образом, есть естественное сладкое пятно для размера банка регистров, который максимизирует около 32 архитектурных регистров для большинства классов ЦП. x86-32 имеет 8 регистров, и это определенно слишком мало. Рука пошла с 16 регистрами и это хорошо компромисс. 32 регистра немного слишком много, если что-нибудь-вам не нужны последние 10 или около того.

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

мы Do есть больше из них

поскольку почти каждая инструкция должна выбрать 1, 2 или 3 архитектурно видимых регистра, расширение их числа увеличит размер кода на несколько бит в каждой инструкции и таким образом уменьшит плотность кода. Это также увеличивает количество контекст это должно быть сохранено как состояние потока и частично сохранено в функции запись активации!--9-->. эти операции происходят часто. Блокировки конвейера должны проверять табло для каждого регистра, и это имеет квадратичную временную и пространственную сложность. И, возможно, самая большая причина-это просто совместимость с уже определенным набором инструкций.

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

пример:

load  r1, a  # x = a
store r1, x
load  r1, b  # y = b
store r1, y

в архитектуре, которая имеет только r0-r7, следующий код может быть автоматически переписан процессором как что-то вроде:

load  r1, a
store r1, x
load  r10, b
store r10, y

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

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

чтобы добавить немного интересную информацию здесь, вы заметите, что наличие 8 регистров одинакового размера позволяет кодам операций поддерживать согласованность с шестнадцатеричной нотацией. Например инструкция push ax является кодом операции 0x50 на x86 и доходит до 0x57 для последнего регистра di. Тогда инструкция pop ax начинается с 0x58 и доходит до 0x5F pop di для завершения первой базы-16. Шестнадцатеричная согласованность поддерживается с 8 регистрами на размер.