Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Танненбаум Е. Архітектура компютера [pdf]

.pdf
Скачиваний:
103
Добавлен:
02.05.2014
Размер:
5.59 Mб
Скачать

Увеличение производительности

303

Динамическое прогнозирование ветвления

Ясно, что точные прогнозы очень ценны, поскольку это позволяет процессору работать с полной скоростью. В настоящее время проводится множество исследований, целью которых является усовершенствование алгоритмов прогнозирования ветвления (например, [32,70,108,125,138,163]). Один из подходов — хранить специальную таблицу (в особом аппаратном обеспечении), в которую центральный процессор записывает условные переходы, когда они встречаются, и там их можно искать, когда они снова появятся. Простейшая версия такой схемы показана на рис. 4.28, а. В данном случае эта таблица содержит одну ячейку для каждой команды условного перехода. В ячейке находится адрес команды перехода, а также бит, который указывает, был ли сделан переход, когда эта команда встретилась последний раз. Прогноз состоит в том, что программа пойдет тем же путем, каким она пошла в прошлый раз после этой команды перехода. Если прогноз неверен, бит в таблице меняется.

 

Бит

 

Бит

 

 

достоверности

 

достоверности

 

 

 

Бит

 

 

Биты

 

перехода

 

прогнозирования

 

Адрес/

1

 

Адрес/

перехода

-лот

тег перехода

1

Слот

тег перехода

*

II.

6

5

4

3

2

1

0

Бит

достоверности

Бит

перехода

 

Адрес/

 

Слот

тег перехода

Целевой адрес

J

Рис. 4.28. Таблицадинамики ветвлений с 1 -битным указателем перехода (а), таблица динамики ветвлений с 2-битным указателем перехода (б), соответствие между адресом команды перехода и целевым адресом (в)

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

304 Глава 4. Микроархитектурный уровень

Рассмотрим машину с 32-битными командами, которые расположены таким образом, что два младших бита каждого адреса памяти — 00. Таблица содержит 2П ячеек (строк). Из команды перехода можно извлечь п+2 младших бита и осуществить сдвиг вправо на два бита. Это n-битное число можно использовать в качестве индекса в таблице, где проверяется, совпадает ли адрес, сохраненный там, с адресом перехода. Как и в случае с кэш-памятью, здесь нет необходимости сохранять п+2 младших бита, поэтому их можно опустить (то есть сохраняются только старшие адресные биты — тег). Если адреса совпали, бит прогнозирования используется для предсказания перехода. Если тег неправильный или элемент недействителен, значит, имеет место несовпадение. В этом случае можно применять правило перехода вперед/назад.

Если таблица динамики переходов содержит, скажем, 4096 элементов, то адреса 0, 16384, 32768,... будут конфликтовать; аналогичная проблема встречается и при работе с кэш-памятью. Здесь возможно такое же решение: двухальтернативный, четырехальтернативный, n-альтернативный ассоциативный элемент. Как и у кэш-памяти, предельный случай — один п-альтернативный ассоциативный элемент.

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

Для устранения такой ситуации мы немного изменим метод, чтобы прогноз менялся только после двух последовательных неправильных предсказаний. Такой подход требует наличия двух предсказывающих битов в таблице: один указывает, предполагается ли совершить переход или нет, а второй указывает, что было сделано в прошлый раз. Таблица показана на рис. 4.28, 6.

Этот алгоритм можно представить в виде конечного автомата с четырьмя состояниями (рис. 4.29). После ряда последовательных успешных предсказаний «перехода нет» конечный автомат будет находиться в состоянии 00 и в следующий раз также прогнозировать, что «перехода нет». Если этот прогноз неправильный, автомат переходит в состояние 01, но в следующий раз все равно предсказывает отсутствие перехода. Только в том случае, если это последнее предсказание ошибочно, конечный автомат перейдет в состояние 11 и будет все время прогнозировать наличие перехода. Фактически, левый бит — это прогноз, а правый бит — это то, что было сделано в прошлый раз (то есть был ли совершен переход). В данной разработке используется только 2 специальных бита, но возможно применение и 4, и 8 битов.

Это не первый конечный автомат, который мы рассматриваем. На рис. 4.19 тоже изображен конечный автомат. На самом деле все наши микропрограммы можно считать конечными автоматами, поскольку каждая строка представляет особое состояние, в котором может находиться автомат, с четко определенными переходами к конечному набору других состояний. Конечные автоматы очень широко используются во всех аспектах разработки аппаратного обеспечения.

Увеличение производительности

305

 

Переход

Переход

Нет перехода

 

 

 

 

Повторное

Повторное

Предсказание

Предсказание

предсказание

предсказание

отсутствия

отсутствия

перехода

перехода

перехода

перехода

 

 

Нет перехода

Рис. 4.29. Двубитный конечный автомат для прогнозирования переходов

До сих пор мы предполагали, что цель каждого условного перехода известна. Обычно или в явном виде давался адрес, к которому нужно перейти (он содержался прямо в самой команде), или было известно смещение относительно текущей команды (то есть число со знаком, которое нужно было прибавить к счетчику команд). Часто это предположение имеет силу, но некоторые команды условного перехода вычисляют целевой адрес, выполняя определенные арифметические действия над значениями регистров, а затем уже переходят туда. Даже если взять конечный автомат, изображенный на рис. 4.29, который точно прогнозирует переходы, такой прогноз будет не нужен, поскольку целевой адрес неизвестен. Один из возможных выходов из подобной ситуации — сохранить в таблице адрес, к которому был осуществлен переход в прошлый раз, как показано на рис. 4.28, в. Тогда, если в таблице указано, что в прошлый раз, когда встретилась команда перехода по адресу 516, переход был совершен в адрес 4000, и если сейчас предсказывается совершение перехода, то целевым адресом снова будет 4000.

Еще один подход к прогнозированию ветвления — следить, были ли совершены последние к условных переходов, независимо от того, какие это были команды [108]. Это k-битное число, которое хранится в сдвиговом регистре динамики переходов, затем сравнивается параллельно со всеми элементами таблицы с к-бит- ным ключом, и в случае совпадения применяется то предсказание, которое найдено в этом элементе. Удивительно, но эта технология работает достаточно хорошо.

Статическое прогнозирование ветвления

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

Можно пойти другим путем и призвать на помощь компилятор. Когда компилятор получает такое выражение, как

for (1=0 1 < 1000000, 1++) {

}

306 Глава 4. Микроархитектурный уровень

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

Хотя это связано с изменением архитектуры (а не только с вопросом реализации), в некоторых машинах, например UltraSPARC II, имеется еще один набор команд условного перехода помимо обычных (которые нужны для обратной совместимости). Новые команды содержат бит, по которому компилятор определяет, совершать переход или не совершать. Когда встречается такой бит, блок выборки команд просто делает то, что ему сказано. Более того, нет необходимости тратить драгоценное пространство в таблице предыстории переходов для этих команд, что сокращает количество конфликтных ситуаций.

Наконец, наша последняя технология прогнозирования ветвления основана на профилировании [37]. Это тоже статическая технология, только в данном случае программа не заставляет компилятор вычислять, какие переходы нужно совершать, а какие нет. В данном случае программа действительно выполняется, а ветвления фиксируются. Эта информация поступает в компилятор, который затем использует специальные команды условного перехода для того, чтобы сообщить аппаратному обеспечению, что нужно делать.

Исполнение с изменением последовательности и подмена регистров

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

Ясно, что самым простым является компьютер, в котором все команды выполняются в том порядке, в котором они вызываются из памяти (предполагается, что прогнозирование переходов всегда оказывается верным). Однако такое последовательное выполнение не всегда дает оптимальную производительность из-за взаимной зависимости команд. Если команде требуется значение, которое вычисляется предыдущей командой, вторая команда не может начать выполняться, пока первая не выдаст нужную величину. В такой ситуации реальной взаимозависимости второй команде приходится ждать. Существуют и другие виды взаимозависимостей, но о них мы поговорим позже.

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

Чтобы изложить основную суть проблемы, начнем с машины, которая запускает команды в том порядке, в котором они расположены в программе, и требует, чтобы выполнение команд завершалось также в порядке, соответствующем программному. Важность второго требования прояснится позднее.

Увеличение производительности

3 0 7

Наша машина содержит 8 регистров, видимых для программиста, от R0 до R7. Все арифметические команды используют три регистра: два — для операндов и один — для результата, как и в микроархитектуре Mic-4. Мы предполагаем, что если команда декодируется в цикле п, выполнение начинается в цикле п+1. В случае с простой командой, например командой сложения или вычитания, запись обратно в выходной регистр происходит в конце цикла п+2. В случае с более сложной командой, например командой умножения, запись в регистр происходит в конце цикла n+З Чтобы сделать наш пример реалистичным, мы позволим блоку декодирования выпускать до двух команд за цикл. Некоторые суперскалярные процессоры могут выпускать 4 или даже 6 команд за цикл.

Последовательность выполнения команд показана в табл. 4.12. В первом столбце приводится номер цикла, во втором — номер команды, а в третьем — сама команда. В четвертом столбце указано, выдача каких команд произошла (максимум две команды за цикл). Цифры в пятом столбце сообщают, какие команды завершены Помните, что в нашем примере мы требуем, чтобы команды и запускались, и завершались в строгом порядке, поэтому выдача команды к+1 может произойти только после выдачи команды к, а результат команды к+1 не может быть записан в выходной регистр до того, как завершится выполнение команды к. Оставшиеся 16 столбцов мы обсудим ниже.

Таблица4.12. Суперскалярныйпроцессорспоследовательнойвыдачей и последовательным завершением команд

Цикл #

Команда

Выдача

Завер-

Считываемые регистры

Записываемые регистры

 

 

 

 

шение

0

1

2

3

4

5

6

7

0

1

2

3

4

5

6

7

1

1

R3=R0*R1

1

 

1

1

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

2

R4=R0+R2

2

 

2

1

1

 

 

 

 

 

 

 

 

1

1

 

 

 

2

3

R5=R0+R1

3

 

3

2

1

 

 

 

 

 

 

 

 

1

1

1

 

 

 

4

R6=R1+R4

-

 

3

2

1

 

 

 

 

 

 

 

 

1

1

1

 

 

3

 

 

 

 

3

2

1

 

 

 

 

 

 

 

 

1

1

1

 

 

4

 

 

 

1

2

1

1

 

 

 

 

 

 

 

 

 

1

1

 

 

 

 

 

 

2

1

1

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5

 

 

4

 

 

1

 

 

1

 

 

 

 

 

 

 

 

 

1

 

 

5

R7=R1'R2

5

 

 

2

1

 

1

 

 

 

 

 

 

 

 

 

1

1

6

6

R1=R0-R2

-

 

 

2

1

 

1

 

 

 

 

 

 

 

 

 

1

1

7

 

 

 

4

 

1

1

 

 

 

 

 

 

 

 

 

 

 

 

1

8

 

 

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

9

 

 

6

 

1

 

1

 

 

 

 

 

 

1

 

 

 

 

 

 

 

7

R3=R3*R1

-

 

1

 

1

 

 

 

 

 

 

1

 

 

 

 

 

 

10

 

 

 

 

1

 

1

 

 

 

 

 

 

1

 

 

 

 

 

 

11

 

 

 

6

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

12

 

 

7

 

 

1

 

1

 

 

 

 

 

 

 

1

 

 

 

 

 

8

R1=R4+R4

 

 

 

1

 

1

 

 

 

 

 

 

 

1

 

 

 

 

13

 

 

 

 

 

1

 

1

 

 

 

 

 

 

 

1

 

 

 

 

14

 

 

 

 

 

1

 

1

 

 

 

 

 

 

 

1

 

 

 

 

15

 

 

 

7

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

16

 

 

8

 

 

 

 

 

2

 

 

 

 

1

 

 

 

 

 

 

17

 

 

 

 

 

 

 

 

2

 

 

 

 

1

 

 

 

 

 

 

18

 

 

 

8

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3 08 Глава 4. Микроархитектурный уровень

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

Следить за состоянием регистров будет специальное устройство — счетчик обращений (scoreboard), который впервые появился в CDC 6600. Счетчик обращений содержит небольшой счетчик для каждого регистра, который показывает, сколько раз этот регистр используется командами, выполняющимися в данный момент, в качестве источника. Если одновременно может выполняться максимум 15 команд, тогда будет достаточно 4-битного счетчика. Когда запускается команда, элементы счетчика обращений, соответствующие регистрам операндов, увеличиваются на 1. Когда выполнение команды завершено, соответствующие элементы счетчика уменьшаются на 1.

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

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

втаблице не показаны.

Впервой строке табл. 4.12 показана команда 1, которая перемножает значения регистров R0 и К1,ипомещаетрезультатврегистр113. Поскольку ни один из этих регистров еще не используется, команда запускается, а счетчик обращений показывает, что регистры R0 и R1 считываются, а регистр R3 записывается. Ни одна из последующих команд не может записывать результат в эти регистры и не может считывать регистр R3 до тех пор, пока не завершится выполнение команды 1. Поскольку это команда умножения, она закончится в конце цикла 4. Значения счетчика обращений, приведенные в каждой строке, отражают состояние регистров после запуска команды, записанной в этой же строке. Пустые клетки соответствуют значению 0.

Поскольку рассматриваемый пример — это суперскалярная машина, которая может запускать две команды за цикл, вторая команда выдается также во время цикла 1. Она складывает значения регистров R0 и R2, а результат сохраняет в регистре R4. Чтобы определить, можно ли запускать эту команду, применяются следующие правила:

1. Если какой-нибудь операнд записывается, запускать команду нельзя (RAWвзаимозависимость).

2.Если считывается регистр результатов, запускать команду нельзя (WARвзаимозависимость).

3.Если записывается регистр результатов, запускать команду нельзя (WAWвзаимозависимость).

Мы уже рассматривали RAW-взаимозависимости, имеющие место, когда команде в качестве источника нужно использовать результат предыдущей команды,

Увеличение производительности

309

которая еще не завершилась. Два других типа взаимозависимостей менее серьезные. По существу, они связаны с конфликтами ресурсов. В WAR-взаимозависимо- сти (Write After Read — запись после чтения) одна команда пытается перезаписать регистр, который предыдущая команда еще не закончила считывать. WAW-взаи- мозависимость (Write After Write — запись после записи) сходна с WAR-взаи- мозависимостыо. Этого можно избежать, если вторая команда будет помещать результат где-либо в другом месте еще (возможно, временно). Если ни одна из трех упомянутых ситуаций не возникает и нужный функциональный блок доступен, то команду можно выпустить. В этом случае команда 2 использует регистр R0, который в данный момент считывается незаконченной командой, но подобное перекрытие допустимо, поэтому команда 2 может запускаться. Сходным образом команда 3 запускается во время цикла 2.

А теперь перейдем к команде 4, которая должна использовать регистр R4. К сожалению, из таблицы мы видим, что в регистр R4 в данный момент производится запись (см. строку 3 в таблице). Здесь имеет место RAW-взаимозависимость, поэтому блок декодирования простаивает до тех пор, пока регистр R4 не станет доступен. Во время простаивания блок декодирования прекращает получать команды из блока выборки команд. Когда внутренние буферы блока выборки команд заполнятся, он прекращает вызывать команды из памяти.

Следует упомянуть, что следующая команда, команда 5, не конфликтует ни с одной из заверигенных команд. Ее можно было бы декодировать и выпустить, если бы в нашей разработке не требовалось, чтобы команды выдавались по порядку.

Посмотрим, что происходит в цикле 3. Команда два, а это команда сложения (два цикла), завершается в конце цикла 3. Но ее результат не может быть сохранен в регистре R4 (который тогда освободится для команды 4). Почему? Потому что данная разработка требует записи результатов в регистры в соответствии с порядком программы. Но зачем? Что плохого произойдет, если сохранить результат в регистре R4 сейчас и сделать это значение доступным?

Ответ на этот вопрос очень важен. Предположим, что команды могут завершаться в произвольном порядке. Тогда в случае прерывания будет очень сложно сохранить состояние машины так, чтобы его можно былопотом восстановить. В частности, нельзя будет сказать, что все команды до какого-то адреса были выполнены, а все команды после этого адреса не были выполнены. Это называется точным прерыванием и является желательной характеристикой центрального процессора [99]. Сохранение результатов в произвольном порядке делает прерывания неточными, и именно поэтому в некоторых машинах требуется соблюдение жесткого порядка в завершении команд.

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

В цикле б команда 6 простаивает, потому что ей нужно записать результат в регистр R1, а регистр R1 занят. Выполнение команды начинается только в цикле 9. Чтобы завершить всю последовательность из 8 команд, требуется 15 циклов из-за многочисленных ситуаций взаимозависимости, хотя аппаратное обеспечение

310 Глава 4. Микроархитектурный уровень

способно выдавать по две команды за цикл. В колонках «Выдача» и «Завершение» табл. 4.12 видно, что все команды выдаются из блока декодирования по порядку и завершаются эти команды тоже по порядку.

Рассмотрим альтернативный подход: исполнение с изменением последовательности. В такой разработке выполнение команд может начинаться в произвольном порядке и завершаться также в произвольном порядке. В табл. 4.13 показана та же последовательность из восьми команд, только теперь разрешен другой порядок выдачи команд и сохранения результатов в регистрах.

Первое различие встречается в цикле 3. Несмотря на то, что команда 4 простаивает, мы можем декодировать и запустить команду 5, поскольку она не создает конфликтной ситуации ни с одной из выполняющихся программ. Однако пропуск команд порождает новую проблему. Предположим, что команда 5 использует операнд, который вычисляется пропущенной командой 4. При текущем состоянии счетчика обращений мы этого не заметим. В результате нам придется расширить счетчик обращений, чтобы следить за записями, которые совершают пропущенные команды. Это можно сделать, добавив еще одно битовое отображение, по одному биту на регистр, для контроля за записями, которые делают простаивающие команды (эти счетчики в таблице не показаны). Правило для запуска команд следует расширить, с тем чтобы предотвратить запуск команды, операнд которой должен быть записан той предшествующей командой, что шла до нее, но была пропущена.

Таблица 4.13. Работа суперскалярного процессора с изменением последовательности запуска и завершения команд

Цикл

#

Команда

Выдача Завер-

Считываемые регистры

Записываемые регистры

 

 

 

шение

0

1

2

3

4

5

6

7

0

1

2

3

4

5

6

7

1

1

R3=R0*R1

1

1

1

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

2

R4=R0+R2

2

2

1

1

 

 

 

 

 

 

 

 

1

1

 

 

 

2

3

R5^R0+R1

3

3

2

1

 

 

 

 

 

 

 

 

1

1

1

 

 

 

4

R6=R1+R4

-

3

2

1

 

 

 

 

 

 

 

 

1

1

1

 

 

3

5

R7^R1*R2

5

3

3

2

 

 

 

 

 

 

 

 

1

1

1

 

1

 

6

S1=R0-R2

6

4

3

3

 

 

 

 

 

 

 

 

1

1

1

 

1

 

 

 

2

3

3

2

 

 

 

 

 

 

 

 

1

 

1

 

1

4

 

 

4

3

4

2

 

1

 

 

 

 

 

 

1

 

1

1

1

 

7

R3=R3*S1

-

3

4

2

 

1

 

 

 

 

 

 

1

 

1

1

1

 

8

S2=R4+R4

8

3

4

2

 

3

 

 

 

 

 

 

1

 

1

1

1

 

 

 

1

2

3

2

 

3

 

 

 

 

 

 

 

 

1

1

1

 

 

 

3

1

2

2

 

3

 

 

 

 

 

 

 

 

 

1

1

5

 

 

6

 

2

1

 

3

 

 

 

 

1

 

 

 

 

1

1

6

 

 

7

 

2

1

1

3

 

 

 

 

1

 

1

 

 

1

1

 

 

 

4

 

1

1

1

2

 

 

 

 

1

 

1

 

 

 

1

 

 

 

5

 

 

 

1

2

 

 

 

 

1

 

1

 

 

 

 

 

 

 

8

 

 

 

1

 

 

 

 

 

 

 

1

 

 

 

 

7

 

 

 

 

 

 

1

 

 

 

 

 

 

 

1

 

 

 

 

8

 

 

 

 

 

 

1

 

 

 

 

 

 

 

1

 

 

 

 

9

 

 

7

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Теперь посмотрим на команды 6, 7 и 8 в табл. 4.12. Здесь мы видим, что команда 6 помещает вычисленное значение в регистр R1, и это значение использует-

Увеличение производительности

3 1 1

ся командой 7. Мы также видим, что это значение больше не используется, потому что команда 8 переписываетзначение регистра R1. Нет никакой надобности использовать регистр R1 для хранения результата команды 6. Еще хуже то, что далеко не лучшим является выбор R1 в качестве промежуточного регистра, хотя с точки зрения программиста, привыкшего к идее последовательного выполнения команд без перекрытий, этот выбор является самым разумным.

Втаблице 4.12 мы ввели новый метод для решения этой проблемы: подмена регистров. Блок декодирования меняет регистр R1 в команде 6 (цикл 3) и в команде 7 (цикл 4) на скрытый регистр S1, который невидим для программиста. Теперь команда 6 может запускаться одновременно с командой 5. Современные процессоры содержат десятки скрытых регистров, которые используются для процедуры подмены. Такая технология часто устраняет WAR- и WAW-взаимозависимости.

Вкоманде 8 мы снова применяем подмену регистров. На этот раз регистр R1 переименовывается в S2, поэтому операция сложения может начаться до того, как регистр R1 освободится, а освободится он только в конце цикла 6. Если окажется, что результат в этот момент должен быть в регистре R1, содержимое регистра S2 всегда можно скопировать туда. Еще лучше то, что все будущие команды, которым нужен этот результат, могут в качестве источника использовать регистры, переименованные в тот регистр, где действительно хранится нужное значение. В любом случае выполнение команды 8 начнется раньше,

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

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

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

Спекулятивное выполнение

В предыдущем разделе мы ввели понятие переупорядочения команд. Эта процедура нужна для улучшения производительности. В действительности имелось

ввиду переупорядочение команд в пределах одного базового элемента программы. Рассмотрим этот аспект подробнее.

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

вначале и точкой выхода в конце. Базовый элемент не содержит никаких управляющих структур (например, условных операторов i f или операторов цикла whi I e), поэтому при трансляции на машинный язык нет никаких ветвлений. Базовые элементы связываются операторами управления.

312 Глава 4. Микроархитектурный уровень

Программа в такой форме может быть представлена в виде ориентированного графа, как показано на рис. 4.30. Здесь мы вычисляем сумму кубов четных и нечетных целых чисел до какого-либо предела и помещаем результаты в evensum и oddsum соответственно (листинг 4.6). В пределах каждого базового элемента технологии, упомянутые в предыдущем разделе, работают отлично.

Листинг4.6. Фрагментпрограммы

evesum=0, oddsum-O;

i=0;

while (I<limit) {

k-i*i*i.

evensum=evensunH-k; else

oddsum=oddsum+k; i-i+l:

}

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

evensum=0;

oddsum = 0;

= 0;

while (i < limit) {

k= i * i * i;

if ((1/2)* 2) ==0)

evensum = evensum + k;

else

oddsum = oddsum + k;

1=1+ 1;

evensum = 0; oddsum = 0; = 0;

\

while (i < limit)

t

k = i * i * i; if((i/2)*2)= = 0)

evensum = evensum + k;

oddsum = oddsum + k;

. * i + 1 ;

i

Рис.4.30.Графбазовогоэлементадляфрагментапрограммы,приведенноговлистинге4.6

Посмотрите на рис. 4.30. Представим, что все переменные были помещены в регистры, кроме evensum и oddsum (из-за недостатка регистров). Тогда имело бы смысл переместить команды LOAD в начало цикла до вычисления переменной к,

Соседние файлы в предмете Аппаратное обеспечение ЭВМ, средств телекоммуникаций и сетей