Есть такая штука, instruction level parallelism, параллелизм уровня инструкций. Когда мы можем запустить в параллель несколько команд программы. Давным-давно читал, что судя по симуляции, средний ILP в фортрановских программах может превышать 10. Получается, что если сделать 10 исполнительных устройств, то программу можно выполнить в 10 раз быстрее.
Однако, та же DEC Alpha в лучшие свои годы не выдавала более 6-8 инструкций, при частоте синхронизации порядка 600 MHz, это в проекте, до кремния дело не дошло. У Интеловского Itanium все тоже не очень ровно - 6 команд, включая плавающую точку. Предел его частоты работы - 1.6GHz.
Как всегда, основным препятствием к повышению быстродействия является расстояние между элементами процессора. Которое зависит от размеров этих самых элементов.
Одним из самых больших элементов таких процессоров является регистровый файл. Та штука, в которой процессор хранит промежуточные данные вычислений.
Объясняется это очень просто: для 6 инструкций, выполняющихся одновременно, необходимо прочитать 6*2 операндов и записать 6 результатов. Итого, 18 различных путей доступа. К одной ячейке памяти этого файла подходит 18 линий ее выборки и 18 линий подвода/отвода данных. Выглядит это примерно так:
|
Выборка ------+----|--
| |
+-+-+ |
|RAM|--+
+---+ |
|
Чтение/записьЛегко увидеть, что общая площадь ячейки с инфраструктурой будет расти как O(N2) от числа N путей доступа.
Это удлинит все линии. Их придется сделать шире, для того, чтобы понизить сопротивление. В ячейки памяти надо будет поместить транзисторы помощнее, а значит, побольше. И тп.
Самый простой вариант регистрового файла - 2 пути чтения и один записи. Проще только стек, у которого число путей чтения можно сделать еще меньше, всего один, но это я уклоняюсь от основной темы.
Такой регистровый файл легко сделать из поставляемых фабриками библиотечных компонент, что сильно упрощает жизнь - не надо мучаться с транзисторами и проводниками вручную. Обслуживать он может, к сожалению, только одно исполнительное устройство.
Один из способов увеличения паралелизма на уровне команд - опережающее выполнение (out-of-order execution). Мы смотрим на некоторое "окно" в потоке команд и выбираем на выполнение те, для которых все операнды готовы. Это также способствует смягчению задержек, связанных с доступом к памяти - пока команда загрузки ждет выборки из памяти, мы можем выполнять другие команды. Для нашего простого регистрового файла (и связанного с ним единственного исполнительного устройства) эффект от этого, к сожалению, будет не очень высокий.
Зато мы можем сдублировать регистровый файл и выпонять на нем две программы попеременно - выполняем одну, пока не упрется в память, затем переключаемся на другую. Либо, как в UltraSPARC T1, переключать каждый такт. HyperThreading, изобретенный еще инженерами DEC. ;)
Посмотрел на UltraSPARC T1, как у них сделано, благо он в исходниках.
Регистровый фал тоже синтезируется (может быть создан автоматически), но у него 3 пути чтения и 2 пути записи (и еще какой-то один транспортный путь).
Надо будет потом разобраться.