Есть много причин, по которым у вас не просто огромное количество регистров:
- Они тесно связаны с большинством стадий конвейера. Для начала вам необходимо отслеживать их время жизни и возвращать результаты на предыдущие этапы. Сложность становится непреодолимой очень быстро, и количество задействованных проводов (буквально) растет с той же скоростью. Это дорого по площади, что в конечном итоге означает, что после определенного момента он станет дорогим по мощности, цене и производительности.
- Он занимает место для кодирования инструкций. 16 регистров занимают 4 бита для источника и назначения и еще 4, если у вас есть инструкции с 3 операндами (например, ARM). Ужасно много места для кодирования набора инструкций занято только для указания регистра. В конечном итоге это влияет на декодирование, размер кода и, опять же, на сложность.
- Есть лучшие способы добиться того же результата ...
В наши дни у нас действительно много регистров - просто они явно не запрограммированы. У нас есть «переименование реестра». Хотя вы получаете доступ только к небольшому набору (8-32 регистров), на самом деле они поддерживаются гораздо большим набором (например, 64-256). Затем ЦП отслеживает видимость каждого регистра и размещает их в переименованном наборе. Например, вы можете загружать, изменять, а затем сохранять в регистре много раз подряд, и каждая из этих операций фактически выполняется независимо в зависимости от промахов в кеше и т. Д. В ARM:
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 имеет гигантское количество переименованных регистров (приблизительное 256). Это означало бы иметь 8 бит умножить на 2 для каждой инструкции, чтобы просто сказать, что такое источник и место назначения. Это значительно увеличило бы количество проводов, необходимых для прохождения сердечника, и его размер. Таким образом, существует золотая середина между 16-32 регистрами, на которую согласились большинство разработчиков, и для нестандартных схем ЦП переименование регистров - способ смягчить ее.
Изменить : важность выполнения вне очереди и переименования реестра. Если у вас есть ООО, количество регистров не имеет большого значения, потому что они просто «временные теги» и переименовываются в гораздо больший набор виртуальных регистров. Вы не хотите, чтобы это число было слишком маленьким, потому что становится трудно писать небольшие последовательности кода. Это проблема для x86-32, потому что ограниченные 8 регистров означают, что множество временных файлов в конечном итоге проходит через стек, а ядру требуется дополнительная логика для пересылки операций чтения / записи в память. Если у вас нет ООО, вы обычно говорите о небольшом ядре, и в этом случае большой набор регистров дает низкое преимущество в соотношении цена / производительность.
Таким образом, существует естественная золотая середина для размера банка регистров, который составляет максимум около 32 регистров для большинства классов ЦП. x86-32 имеет 8 регистров и определенно слишком мал. В ARM было 16 регистров, и это хороший компромисс. 32 регистра - это немного многовато - вам не понадобятся последние 10 или около того.
Ничего из этого не касается дополнительных регистров, которые вы получаете для SSE и других векторных сопроцессоров с плавающей запятой. Это имеет смысл как дополнительный набор, потому что они работают независимо от целочисленного ядра и не увеличивают сложность процессора экспоненциально.