Архитектурные способы повышения производительности процессоров
Одним из наиболее эффективных способов повышения производительности процессоров является внедрение в вычислительный процесс параллелизма на уровне команд. Основное архитектурное решением в этом направлении – конвейерная обработка команд.
Механизм конвейерной обработки команд и организации таким образом их параллельного выполнения был впервые предложен в 1956 году в Советском Союзе С. А. Лебедевым, одним из руководителей разработки первых отечественных ВМ (см. раздел 1). Первоначально этот механизм в авторском изложении имел название «принцип трубопровода» и, перекочевав за рубежи нашей страны, получил дословный англоязычный перевод – «pipelining». Последующая «конвертация» этого понятия привела к появлению в русскоязычной технической литературе термина «конвейеризация», который и имеет на сегодняшний день наибольшее распространение. Рассмотрим основную сущность механизма конвейерной обработки команд.
В классическом процессоре реализация некоторой произвольной команды сводится к выполнению нескольких последовательных операций (этапов). Типовой набор таких простейших этапов в самом общем и простом случае может быть представлен следующим перечнем: этап A – выборка команды, т.е. чтение очередной команды из памяти и занесение ее в регистр команды; этап B – декодирование команды,т.е. определение кода операции (или операций, так как команда может содержать несколько операций) и способов адресации операндов; этап C – вычисление адресов операндов,т.е. вычисление исполнительных адресов каждого из операндов в соответствии с указанным в команде способом их адресации; этап D – выборка операндов,т.е. извлечение операндов из памяти (эта операция не нужна для операндов, находящихся в регистрах); этап E – исполнение команды,т.е. исполнение указанной операции;
этап F – запись результата,т.е. занесение результата в память.
Если упрощенно считать, что каждая команда обязательно проходит все шесть этапов, а выполнение каждого из этапов требует некоторой условной единицы времени (например, это может быть такт работы процессора), то для выполнения шести элементарных этапов некоторой команды K1 в классическом процессоре потребуется 6 условных единиц времени работы процессора T. Для выполнения же двух аналогичных команд K1 и K2 соответственно потребуется 12 условных единиц времени, для выполнения трех команд – 18 условных единиц времени и т.д.
Идея конвейеризации заключается в выделении нескольких самостоятельных исполнительных блоков процессора для выполнения каждого из элементарных этапов команды и соединение таких блоков в последовательную цепочку так называемых ступеней конвейера. Для сравнения на рис.2.1 и рис.2.2 показаны условные схемы работы такой последовательной цепочки ступеней при классическом варианте их загрузки и при варианте их конвейерной загрузки.
ИСПОЛНИТЕЛЬНЫЕ БЛОКИ
A | B | C | D | E | F |
\/ \/ \/ \/ \/ \/
T1 | К1 |
T2 | К1 |
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
T5 | К1 |
T6 | К1 |
T7 | К2 |
T8 | К2 |
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
T11 | К2 |
T12 | К2 |
Рис.2.1. Условная схема работы последовательной цепочки
исполнительных блоков (ступеней) процессора
при классическом варианте их загрузки: K – команда,
T – условная единица времени работы процессора
ИСПОЛНИТЕЛЬНЫЕ БЛОКИ
A | B | C | D | E | F |
\/ \/ \/ \/ \/ \/
T1 | К1 |
T2 | К2 | К1 |
T3 | К3 | К2 | К1 |
T4 | К4 | К3 | К2 | К1 |
T5 | К5 | К4 | К3 | К2 | К1 |
T6 | К6 | К5 | К4 | К3 | К2 | К1 |
T7 | К7 | К6 | К5 | К4 | К3 | К2 |
T8 | К8 | К7 | К6 | К5 | К4 | К3 |
T9 | К9 | К8 | К7 | К6 | К5 | К4 |
T10 | К10 | К9 | К8 | К7 | К6 | К5 |
T11 | К11 | К10 | К9 | К8 | К7 | К6 |
T12 | К12 | К11 | К10 | К9 | К8 | К7 |
Рис.2.2. Условная схема работы последовательной цепочки
исполнительных блоков (ступеней) процессора
при варианте их конвейерной загрузки: K – команда,
T – условная единица времени работы процессора
Легко подсчитать, что в последнем случае (рис.2.2), например, за 12 условных единиц времени (T1 – T12) будет выполнено 7 команд (K1 – K7) и еще 5 команд (K8 – K12) уже будут находиться на разных этапах выполнения. В классическом же варианте (рис.2.1) за эквивалентный промежуток времени будет выполнено только 2 команды, а для выполнения 7 команд потребуется 6 × 7 = 42 условных единицы времени.
Приведенный пример иллюстрирует работу конвейера команд в идеальном случае. На практике производительность конвейера может быть существенно ниже по сравнению с потенциально возможной в силу ряда имеющих место ситуаций, называемых конфликтными ситуациями в конвейере или просто «конфликтами».
Различают следующие конфликтные ситуации, которые принято именовать термином «риск»:
– риск по ресурсам, или структурный риск;
– риск по данным;
– риск по управлению (неоднозначность при выборке следующей команды в случае команд перехода).
Риск по ресурсам имеет место, когда несколько команд, находящихся на разных ступенях конвейера, пытаются одновременно использовать один и тот же ресурс, чаще всего – память. Так, в представленном выше типовом примере команды сразу три этапа (A, D и F) связаны с обращением к памяти, т.е. все три обращения могут производиться одновременно. Подобных конфликтов частично удается избежать за счет модульного построения основной памяти и использования кэш-памяти. В этих случаях имеется высокая вероятность того, что команды будут обращаться либо к разным модулям основной памяти, либо одна из них станет обращаться к основной памяти, а другая – к кэш-памяти. С этих позиций выгоднее разделять кэш-память на две части: кэш-память для команд и кэш-память для данных. Так как для сравнительно большого числа команд этапы выборки операнда и записи результата обычно не требуются, то конфликты из-за одновременного обращения к памяти могут в этом случае вообще не возникать. В целом, влияние риска по ресурсам на производительность конвейера по сравнению с другими видами рисков относительно невелико.
Риск по даннымобусловлен взаимосвязанностью команд по данным и, в противоположность структурному риску, является типичной и регулярно возникающей ситуацией. Рассмотрим этот тип конфликта подробнее.
Пусть две команды в конвейере Ki и Kj предусматривают обращение к одной и той же переменной N, причем команда Ki предшествует команде Kj. В общем случае между Ki и Kj могут возникнуть четыре типа ситуации обращения к переменной N:
1) команда Kj читает N до того, как команда Ki успела записать новое значение N, то есть Kj ошибочно получит старое значение N вместо нового;
2) команда Kj записывает новое значение N до того, как команда Ki успела прочитать N, то есть команда Ki ошибочно получит новое значение N вместо старого;
3) команда Kj записывает новое значение N прежде, чем команда Ki успела записать в качестве N свое значение, то есть N ошибочно содержит i-е значение N вместо j-го.
4) команда Kj читает N прежде команды Ki.
Первая ситуация – наиболее частый вид конфликтов по данным, поскольку операция чтения в цикле команды (этап D) предшествует операции записи (этап F). Третья ситуация не вызывает особых проблем в конвейерах, где команды следуют в порядке, определенном программой, и могут производить запись только на этапе F. В худшем случае, когда одна команда догоняет другую из-за приостановки последней, имеет место конфликт по ресурсу – попытка одновременного доступа к одной и той же ячейке. Четвертая ситуация не вызывает никаких конфликтов, поскольку как Ki, так и Kj получат верное значение N.
Для преодоления конфликтов по данным важны своевременное обнаружение потенциального конфликта и его устранение. При этом используются как программные, так и аппаратные методы. Программные методы ориентированы на устранение самой возможности конфликтов еще на стадии компиляции программы. Например, оптимизирующий компилятор пытается создать такой объектный код, чтобы между командами, склонными к конфликтам, находилось достаточное количество нейтральных в этом плане команд. Если это не удается, то между конфликтующими командами компилятор вставляет необходимое количество «пустых» команд. Аппаратные методы применяются для непосредственного устранения возникающих конфликтов по данным. Наиболее очевидным решением является остановка команды Kj на несколько тактов для того, чтобы команда Ki успела завершиться или, по крайней мере, миновать ступень конвейера, вызвавшую конфликт. Соответственно задерживаются и команды, следующие в конвейере за командой Kj. Иногда возможна приостановка только команд Kj, не задерживая следующие за ней команды. Этот прием более эффективен, но требует существенного аппаратного усложнения конвейера.
Риск по управлению обусловлен командами, изменяющими естественный порядок вычислений, что создает наибольшие проблемы практической реализации эффективного конвейера. Канонический конвейер ориентирован на линейные программы. В нем ступень выборки извлекает команды из последовательных ячеек памяти, используя для этого счетчик команд. Адрес очередной команды в линейной программе формируется автоматически, за счет прибавления к содержимому счетчика команд числа, равного длине текущей команды в байтах. Реальные программы практически никогда не бывают линейными. В них как правило обязательно присутствуют команды управления, изменяющие последовательность вычислений. Это команды безусловных и условных переходов, команды вызова процедур, команды возврата из процедур и т. п. Доля подобных команд в программе в среднем составляет не менее 15 – 20%. Неоднозначность при выборке следующей команды в случае команд перехода может приводить к приостановке конвейера, что в целом снижает производительность процессора.
Приостановки конвейера при выполнении команд перехода обусловлены двумя факторами.
Первый фактор характерен для любой команды перехода и связан с выборкой команды из точки перехода (по адресу, указанному в команде перехода). То, что текущая команда относится к командам перехода, становится ясным только после декодирования (а именно после прохождения ступени декодирования), то есть минимум спустя два такта от момента поступления команды на конвейер. За это время на первые ступени конвейера уже поступят новые команды, извлеченные в предположении, что естественный порядок вычислений не будет нарушен. В случае перехода эти ступени необходимо очистить и загрузить в конвейер команду, расположенную по адресу перехода, для чего нужен исполнительный адрес последней. Поскольку в командах перехода обычно указаны лишь способ адресации и адресный код, исполнительный адрес предварительно должен быть вычислен, что и делается на третьей ступени конвейера. Таким образом, реализация перехода в конвейере требует определенных дополнительных операций, выполнение которых равносильно остановке конвейера в лучшем случае на два такта.
Второй фактор нарушения ритмичности работы конвейера имеет отношение только к командам условного перехода. До завершения команды условного перехода невозможно определить, какая из команд должна выполняться следующей. Если по условию команды переход не требуется, то конвейер просто загружает следующую команду в последовательности и продолжает свою работу. При этом получается максимально возможная производительность. Если же по условию команды переход требуется (а об этом заранее не было известно), то конвейер должен быть очищен от ненужных команд, выполнявшихся в нем на других ступенях до данного момента, после чего в первую ступень должна быть загружена нужная по условию команда. Из-за этого в течение нескольких тактов не будет завершена ни одна другая команда, что приведет к естественным издержкам при работе конвейера. При этом важно отметить, что если переход происходит, то такие издержки существенно больше, чем для прочих команд перехода, а если переход не происходит – отсутствуют совсем.
Для сокращения издержек, обусловленных выборкой команды из точки перехода, применяются такие методы, как:
1) вычисление исполнительного адреса перехода на ступени декодирования команды;
2) использование буфера адресов перехода;
3) использование кэш-памяти для хранения команд, расположенных в точке перехода;
4) использование буфера цикла.
В результате декодирования команды выясняется не только ее принадлежность к командам перехода, но также способ адресации и адресный код точки перехода. Это позволяет сразу же приступить к вычислению исполнительного адреса перехода, не дожидаясь передачи команды на следующую ступень конвейера, и тем самым сократить время остановки конвейера с двух тактов до одного. Для реализации этого метода в состав ступени декодирования вводятся дополнительные блоки, вычисляющие исполнительный адрес точки перехода.
Буфер адресов перехода (Branch Target Buffer – ВТВ) представляет собой кэш-память небольшой емкости, в которой хранятся исполнительные адреса точек перехода нескольких последних команд. Перед выборкой очередной команды ее адрес (содержимое счетчика команд) сравнивается с адресами команд, представленных в ВТВ. Для команды, найденной в буфере адресов перехода, исполнительный адрес точки перехода не вычисляется, а берется из ВТВ, благодаря чему выборка команды из точки перехода может быть начата на один такт раньше. Команда, ссылка на которую в ВТВ отсутствует, обрабатывается стандартным образом. Если это команда перехода, то полученный при ее выполнении исполнительный адрес точки перехода заносится в ВТВ, при условии, что команда завершилась переходом. Применение ВТВ дает наибольший эффект, когда отдельные команды перехода в программе выполняются многократно, что типично для циклов.
Кэш-память команд, расположенных в точке перехода (Branch Target Instruction Cache – BTIC), – это усовершенствованный вариант ВТВ, где в кэш-память помимо исполнительного адреса команды в точке перехода записывается также и код этой команды. За счет увеличения емкости кэш-памяти BTIC позволяет при повторном выполнении команды перехода исключить не только этап вычисления исполнительного адреса точки перехода, но и этап выборки расположенной там команды. Преимущества данного подхода в наибольшей степени проявляются при многократном исполнении одних и тех же команд перехода, главным образом при реализации программных циклов.
Буфер цикла представляет собой быстродействующую память, входящую в состав первой ступени конвейера, где производится выборка команд. В этом буфере сохраняются коды нескольких последних команд в той последовательности, в которой они выбирались. Когда имеет место переход, аппаратными средствами сначала проверяется, нет ли нужной команды в буфере, и если это так, то команда извлекается из буфера. Такой метод наиболее эффективен при реализации циклов и итераций, чем и объясняется название буфера. Если буфер достаточно велик, чтобы охватить все тело цикла, выборку команд из памяти достаточно выполнить только один раз в первой итерации, поскольку необходимые для последующих итераций команды уже находятся в буфере. Буфер цикла принципиально близок к BTIC. Однако в буфере цикла сохраняется последовательность выполнения команд, что и отличает его от BTIC.
Решение проблемы условных переходов является особенно важной задачей при проектировании процессоров, поскольку именно эта проблема приводит к наибольшим издержкам в работе конвейера. Для устранения или частичного сокращения указанных издержек используются механизмы буферов предвыборки, параллельных (множественных) потоков команд, задержанных переходов, а также различные стратегии предсказания переходов.
Буфер предвыборки представляет собой буферную память, организованную по принципу очереди. Он располагается между ступенью выборки команды и остальной частью конвейера. Этот буфер может рассматриваться как несколько дополнительных ступеней конвейера. Благодаря буферу предвыборки, во-первых, обеспечивается ритмичность подачи команд на вход конвейера. Это очень важный фактор, так как равномерность поступления команд на вход конвейера часто нарушается, например, при занятости памяти или при выборке команд, состоящих из нескольких слов. Во-вторых, для решения проблемы условных переходов, в конвейер включают два таких буфера. При этом каждая извлеченная из памяти и помещенная в основной буфер команда анализируется блоком перехода. При обнаружении команды условного перехода блок перехода вычисляет исполнительный адрес точки перехода и параллельно с продолжением последовательной выборки в основной буфер организует выборку в дополнительный буфер команд, начиная с точки условного перехода. Далее блок перехода определяет исход команды условного перехода, в зависимости от которого подключает к остатку конвейера нужный буфер, а содержимое другого буфера сбрасывается.
Другим решением проблемы условных переходов служит дублирование начальных ступеней конвейера и создание тем самым параллельных потоков команд. В одной из ветвей такого «раздвоенного» конвейера последовательность выборки и выполнения команд соответствует случаю, когда условие перехода не выполнилось, во второй ветви – случаю выполнения этого условия. Оба потока сходятся в точке, где итог проверки условия перехода становится очевидным. Дальнейшее продвижение по конвейеру продолжает только «правильный» поток.
Механизм задержанного перехода предполагает продолжение выполнения команд, следующих за командой условного перехода, вне зависимости от ее исхода. Естественно, что это имеет смысл, когда последующие команды являются «полезными», то есть такими, которые все равно должны быть когда-то выполнены, независимо от того, происходит переход или нет, и если команда перехода никак не влияет на результат их выполнения. Для реализации этой идеи на этапе компиляции программы после каждой команды перехода вставляется «пустая» команда типа «нет операции». Затем на стадии оптимизации программы производятся попытки «перемешать» команды таким образом, чтобы по возможности большее количество команд «нет операции» заменить «полезными» командами программы. При этом замещать команду «нет операции» можно лишь на такую, которая не влияет на условие выполняемого перехода, иначе полученная последовательность команд не будет эквивалентна исходной.
Предсказание переходов является наиболее эффективным способом борьбы с конфликтами по управлению в современных процессорах. Эта стратегия заключается в том, что еще до момента выполнения команды условного перехода или сразу же после ее поступления на конвейер делается предположение о наиболее вероятном исходе такой команды (переход произойдет или не произойдет). Последующие команды подаются на конвейер в соответствии с принятым предсказанием. При этом ритмичность функционирования конвейера без остановок и задержек тем выше, чем выше точность предсказания. Термин «точность предсказания» обычно трактуют как процентное отношение числа правильных предсказаний к их общему количеству. Предложено достаточно много способов реализации идеи предсказания переходов, отличающихся друг от друга исходной информацией, на основании которой делается прогноз, сложностью реализации и, главное, точностью предсказания. При классификации схем предсказания переходов обычно выделяют два подхода: статический и динамический, в зависимости от того, в какой момент времени и на базе какой информации делается предсказание.
Статическое предсказание переходов осуществляется на основе некоторой априорной информации о подлежащей выполнению программе. Предсказание делается на этапе компиляции программы и в процессе вычислений уже не меняется. Главное различие между известными механизмами статического прогнозирования заключается в виде информации, используемой для предсказания, и ее трактовке. Один из таких механизмов заключается в предсказании перехода на основе кода операции команды перехода. При этом для одних команд предполагается, что переход произойдет, а для других, что его не случится. Наиболее эффективным механизмом является статическое предсказание назначения командам условного перехода наиболее вероятного исхода по результатам так называемого профилирования подлежащих выполнению программ. Под профилированием подразумевается выполнение программы при некотором эталонном наборе исходных данных, сопровождающееся сбором информации об исходах каждой команды условного перехода. Исходы фиксируются в специальном бите кода операции. При выполнении программы поведение конвейера команд определяется после выборки команды по состоянию этого бита. Недостаток этого способа связан с тем, что изменение набора исходных данных для профилирования может существенно менять поведение одних и тех же команд условного перехода.
В динамических стратегиях предсказания переходов решение о наиболее вероятном исходе команды условного перехода принимается в ходе вычислений, исходя из информации о предшествующих переходах (истории переходов), собираемой в процессе выполнения программы. История переходов фиксируется в форме таблицы, которая носит название таблицы предыстории переходов.
Следует подчеркнуть, что в поведении многих команд условного перехода явно прослеживается тенденция повторяемости исхода: одни команды программы, как правило, завершаются переходом, в то время как другие совершаются без него, то есть имеет место так называемое бимодальное распределение исходов. Идея бимодальных схем предсказания к отделению команд, имеющих склонность завершаться переходом, от команд, при выполнении которых переход обычно не происходит. Такая дифференциация позволяет для каждой команды выбрать наиболее подходящее предсказание. Для реализации идеи в составе схемы предсказания достаточно иметь лишь одну таблицу, каждый элемент которой отображает историю исходов одной команды условного перехода. Для обращения к элементу, ассоциированному с определенной командой условного перехода, используется адрес этой команды. Бимодальные схемы предсказания переходов можно определить как схемы, содержащие один уровень таблиц истории переходов (обычно единственную таблицу), адресуемых с помощью адреса команды условного перехода. Таким образом, бимодальные схемы предсказания (называемые также одноуровневыми) ориентированы на те команды условных переходов, очередной исход которых существенно зависит от их собственных предыдущих исходов. В то же время для многих программ наблюдается сильная зависимость команд условных переходов не от собственных исходов, а от результатов выполнения других предшествующих им команд условных переходов. Это обстоятельство учитывается в двухуровневых адаптивных схемах предсказания переходов, называемых также коррелированными (чем подчеркивается то, что они отражают взаимозависимость команд условного перехода). При двухуровневом предсказании выбор исхода перехода обусловливается двумя источниками – командой, для которой делается предсказание, и информацией об истории предшествующих переходов.
Для вышерассмотренных стратегий характерна сильная зависимость точности предсказания от особенностей программ, в рамках которых эти стратегии реализуются. Определенная схема, эффективно работая с одними программными продуктами, может давать совершенно неудовлетворительные результаты при работе с другими. Иными словами, ни одна из стратегий предсказания переходов не является универсальной, то есть лучшей в любых ситуациях. Для того, чтобы в каждой конкретной ситуации задействовать тот механизм предсказания, от которого в данном случае можно ожидать наибольшей точности предсказания, применяются гибридные или соревновательные схемы. Они объединяют в себе несколько различных механизмов предсказания.
Еще одним важным фактором является то, что точность предсказания повышается с увеличением глубины предыстории переходов, но происходит это лишь после накопления соответствующей информации, для чего требуется некоторое время. Период накопления предыстории принято называть временем «разогрева». В процессе «разогрева» точность предсказания постепенно увеличивается.
В целом, динамические стратегии по сравнению со статическими стратегиями обеспечивают более высокую точность предсказания, которая может достигать 99%, а в среднем составляет не менее 85% – 95%.
Прогрессивным архитектурным решением является увеличению количества ступеней конвейера как за счет добавления новых ступеней, так и путем дробления имеющихся ступеней на несколько более простых подступеней. Такое решение обычно называют суперконвейеризацией. К классу суперконвейерных принято относить процессоры с более чем шестью ступенями в их конвейерах. Например, первый серийным суперконвейерный процессор имел конвейер команд из восьми ступеней. Суперконвейеризация в нем стала следствием разбиения этапов выборки команды и выборки операнда. Разбивая этапы обработки инструкций на небольшие стадии, можно упростить соответствующие исполнительные блоки и вследствие этого повысить предельные частоты устойчивой работы их электронных схем.
При разбиении одной или нескольких ступеней конвейера на N подступеней при одновременном повышении тактовой частоты внутри этих ступеней также в N раз можно на каждой ступени конвейера в пределах одного «внешнего» тактового периода выполнить N команд (как правило, более простых, результатом выполнения которых будет исходная более сложная команда). Разбиением каждой ступени конвейера на N подступеней при одновременном повышении тактовой частоты внутри конвейера в N раз добиваются N-кратного увеличения темпа работы конвейера и соответственно его эффективности, которая находится в прямой зависимости от того, с какой частотой на вход конвейера подаются объекты обработки. Другими словами, чем длиннее конвейер (чем больше у него ступеней), тем меньший объем вычислений выполняется за один такт и тем быстрее этот объем вычислений может быть выполнен. То есть для достижения максимально высоких тактовых частот следует увеличивать длину конвейера. Однако увеличение числа ступеней конвейера приводит к возрастанию вероятности конфликтов. При ошибочных предсказаниях переходов приходится очищать большее число ступеней конвейера, на что требуется больше времени. Усложняется логика взаимодействия ступеней конвейера и возникают другие дополнительные сложности. Поэтому перед разработчиками процессоров достаточно остро стоит проблема оптимального выбора числа ступеней конвейера команд.
Важное значение имеет архитектурное исполнение процессора с точки зрения степени сложности (или «комплексности») команд, с которыми процессор способен работать. С самого начала развития языков программирования основной задачей было их совершенствование в направлении упрощения для программистов процесса написания программ. Это привело к возникновению и интенсивному практическому использованию языков программирования высокого уровня (ЯВУ). Операции, характерные для ЯВУ, становились все более сложными по сравнению с операциями, реализуемыми простыми машинными командами, что стало приводить к неэффективному выполнению программ. Для разрешения этой проблемы разработчики процессоров непрерывно ВМ расширяли систему команд, дополняя ее командами, реализующими сложные операторы ЯВУ на аппаратурном уровне. Такие процессоры принято называть процессорами с полным набором команд или процессорами с CISC-архитектурой (Complex Instruction Set Computer – CISC). CISC-архитектура во многом позволяет сократить разрыв между сложными операторами программы и системой команд процессора, их реализующих. Однако это приводит к существенному усложнению электронных схем, т.е. аппаратных средств процессора (особенно реализующих функции управления), что в целом ограничивает возможности увеличения его производительности указанными ранее способами. Особые сложности связаны с организацией эффективного конвейера команд, который, как уже отмечалось, является одним из наиболее действенных механизмов повышения производительности процессоров.
Более простая по аппаратной реализации так называемая архитектура с сокращенным набором команд или RISC-архитектура (Reduced Instruction Set Computer – RISC) базируется на использовании менее сложных команд, чем в CISC-архитектуре. Основные усилия в RISC-архитектуре направлены на построение максимально эффективного конвейера команд, то есть такого, где все команды извлекаются из памяти и поступают в процессор на обработку в виде равномерного потока. При этом ни одна команда не должна находиться в состоянии ожидания, а процессор должен оставаться загруженным на протяжении всего времени. Для RISC-архитектуры характерны команды стандартной длины, равной ширине шины данных, соединяющей процессор и память. Вследствие сокращения числа выполняемых команд, форматов команд и данных, а также видов адресации существенно упрощается устройство управления, что приводит к значительному снижению задержек в формировании сигналов управления. Естественно, что в сокращенном списке команд должны оставаться те, которые используются наиболее часто. Известно, что около 85% времени выполнения типовых программ приходится на относительно малую часть команд, составляющую примерно 15%. К наиболее часто востребуемым действиям относятся пересылка данных, арифметические и логические операции. В RISC-архитектуре максимально сокращено число команд, имеющих доступ к памяти для выборки операндов и/или записи результатов. При этом доступ к памяти во время исполнения осуществляется только командами «чтение» и «запись», а все операции, кроме «чтение» и «запись», имеют тип «регистр – регистр». Для упрощения выполнения большинства команд и приведения их к типу «регистр – регистр» процессор должен быть снабжен значительным числом регистров общего назначения. Большое число регистров в процессоре позволяет обеспечить временное хранение промежуточных результатов, используемых как операнды в последующих операциях, что ведет к уменьшению числа обращений к памяти и ускорению выполнения операций. Однако увеличение числа регистров общего назначения способно дать эффект только при определенном предельном увеличение их числа и разумном их использовании. Оптимизация использования регистров в RISC-процессорах обеспечивается как программными, так и аппаратными средствами. Программная оптимизация использования регистров выполняется на этапе компиляции программы, написанной на языке высокого уровня. Компилятор стремится так распределить регистры процессора, чтобы разместить в них те переменные, которые в течение заданного периода времени будут использоваться наиболее интенсивно. Аппаратная оптимизация использования регистров в RISC-процессорах ориентирована на сокращение затрат времени при работе с процедурами, так как наибольшее время в программах, написанных на языках высокого уровня, расходуется на вызовы процедур и возврат из них, что связано с созданием и обработкой большого числа локальных переменных и констант.
Таким образом, применение RISC-архитектуры ведет к сокращению времени выполнения программы, а соответственно к увеличению быстродействия за счет сокращения числа циклов на команду. Более простое схемное исполнение устройства управления приводит к снижению его стоимости и повышению надежности. Недостатки же RISC-архитектуры являются следствием ее преимуществ. Принципиальный недостаток – сокращенное число команд: на выполнение ряда функций приходится тратить несколько команд вместо одной в CISC. Это удлиняет код программы, увеличивает загрузку памяти и трафик обмена информацией между памятью и процессором. В среднем RISC-программа примерно на 30% длиннее CISC-программы, реализующей те же функции. Хотя большое число регистров в RISC-архитектуре дает существенные преимущества, однако оно усложняет схему декодирования номера регистра, тем самым увеличивая время доступа к регистрам.
Еще одним эффективным архитектурным решением, вводящим в вычислительный процесс определенный уровень параллелизма, является применение так называемых векторных (потоковых) и матричных схем работы процессоров, которые используются для обработки многокомпонентных операндов типа вектор и массив.
В средствах векторной обработки под вектором понимается одномерный массив однотипных данных (обычно в форме с плавающей запятой), регулярным образом размещенных в памяти ВМ. Если обработке подвергаются многомерные массивы, они также могут быть рассмотрены как одномерные массивы данных – векторы, так как при размещении матрицы в памяти все ее элементы заносятся в ячейки с последовательными адресами, причем данные могут быть записаны строка за строкой или столбец за столбцом.
Векторным процессоромназывают процессор, в котором операндами некоторых команд могут выступать упорядоченные массивы (потоки) данных – векторы. При исполнении векторного процессора с конвейерным АЛУ обработка элементов векторов производится конвейерным АЛУ для чисел с плавающей занятой. При этом очередной элемент вектора подается для обработки на вход такого конвейера, как только освобождается первая ступень. Одновременные операции над элементами векторов можно проводить и с помощью нескольких параллельно используемых АЛУ, каждое из которых отвечает за одну пару элементов. Векторный процессор можно добавлять к обычному процессору. В результате те части программы, которые могут быть преобразованы в векторную форму, выполняются векторным блоком, а остальная часть программы – обычным процессором.
При обработке больших массивов данных применяются так называемые матричные, или массивно-параллельные, схемы исполнения процессоров. Они состоят из регулярного массива процессорных элементов и имеют общее управляющее устройство, генерирующее поток команд. Процессорные элементы работают параллельно и обрабатывают каждый свой поток данных. Для обеспечения достаточной эффективности такой системы при решении широкого круга задач, необходимо организовать связи между процессорными элементами так, чтобы наиболее полно загрузить их работой.
Высокое быстродействие векторных и матричных схем работы процессоров достигается за счет одновременной обработки всех компонентов вектора или массива, однако подобные операнды характерны лишь для достаточно узкого круга решаемых задач. Основной объем вычислительной нагрузки обычно приходится на скалярные вычисления, то есть на обработку одиночных операндов, таких, например, как целые числа. Для подобных вычислений дополнительный параллелизм реализуется значительно сложнее и для этого применяются так называемые суперскалярные схемы работы процессоров. Обычно суперскалярным называют процессор, который одновременно выполняет более чем одну скалярную команду. Это достигается за счет включения в состав процессора нескольких самостоятельных функциональных исполнительных блоков, каждый из которых отвечает за свой класс операций и может присутствовать в процессоре в нескольких экземплярах. Например, в процессоре могут быть дублированы или даже «троированы» блоки целочисленной арифметики и операций с плавающей точкой.
Типичный суперскалярный процессор включает в себя шесть блоков: выборки команд, декодирования команд, диспетчеризации команд, распределения команд по функциональным блокам, исполнения и обновления состояния. Блок выборки команд извлекает команды из основной памяти через кэш-память команд. Этот блок хранит несколько значений счетчика команд и обрабатывает команды условного перехода. Блок декодирования расшифровывает код операции, содержащийся в извлеченных из кэш-памяти командах (в некоторых суперскалярных процессорах блоки выборки и декодирования могут быть совмещены). Блоки диспетчеризации и распределения взаимодействуют между собой и в совокупности играют в суперскалярном процессоре роль контроллера трафика. Оба блока хранят очереди декодированных команд. Очередь блока распределения размещается по несколько самостоятельным буферам – накопителям команд или схемам резервирования, предназначенным для хранения команд, которые уже декодированы, но еще не выполнены. Каждый накопитель команд связан со своим функциональным блоком, поэтому число накопителей обычно равно числу функциональных блоков, но если в процессоре используется несколько однотипных функциональных блоков, то им придается общий накопитель. По отношению к блоку диспетчеризации накопители команд выступают в роли виртуальных функциональных устройств. В дополнение к очереди, блок диспетчеризации хранит также список свободных функциональных блоков, который используется для отслеживания состояния очереди распределения. Один раз за цикл блок диспетчеризации извлекает команды из своей очереди, считывает из памяти или регистров операнды этих команд, после чего, в зависимости от состояния списка свободных функциональных блоков, помещает команды и значения операндов в очередь распределения. Эта операция называется выдачей команд. Блок распределения в каждом цикле проверяет каждую команду в своих очередях на наличие всех необходимых для ее выполнения операндов и при положительном ответе начинает выполнение таких команд в соответствующем функциональном блоке. Блок исполнения состоит из набора функциональных блоков, таких как целочисленные операционные блоки, блоки умножения и сложения с плавающей запятой, блок доступа к памяти. Когда исполнение команды завершается, ее результат записывается и анализируется блоком обновления состояния, который обеспечивает учет полученного результата теми командами в очередях распределения, где этот результат выступает в качестве одного из операндов.
Итак, суперскалярность предполагает параллельную работу максимального числа исполнительных блоков, что возможно лишь при одновременном выполнении нескольких скалярных команд. Такое условие хорошо сочетается с конвейерной обработкой, поэтому обычно предполагается наличие в суперскалярном процессоре нескольких конвейеров. При построении суперскалярного процессора с углубленной степенью интеграции блок выборки извлекает из памяти более одной команды и передает их через ступени декодирования команды и вычисления адресов операндов в блок выборки операндов. Когда операнды становятся доступными, команды распределяются по соответствующим исполнительным блокам. Операции «чтение», «запись» и «переход» реализуются самостоятельными исполнительными блоками.
Применение суперскалярного подхода приводит к повышению производительности процессора в несколько раз. В некоторых современных процессорах суперскалярность совмещается с суперконвейеризацией.
Сочетание в суперскалярных процессорах множественности функциональных блоков с множественностью конвейеров команд, приводит к дополнительным проблемам их эффективного функционирования, в частности к проблемам последовательности поступления команд на исполнение и проблемам последовательности завершения команд. Первая из упомянутых проблем возникает, когда очередность выдачи декодированных команд на исполнительные блоки отличается от последовательности, предписанной программой. Подобную ситуацию называют неупорядоченной выдачей команд. Термин «упорядоченная выдача команд» применяют, когда команды покидают ступени, предшествующие ступени исполнения, в определенном программой порядке. В обоих случаях завершение команд обычно является неупорядоченным (неупорядоченное завершение команд), и это является второй проблемой. Упорядоченное же завершение происходит гораздо реже. В суперскалярных процессорах, с их множественными конвейерами и неупорядоченными выдачами/завершениями, взаимозависимость команд представляет серьезную задачу. Кроме того, существует еще один фактор, характерный только для суперскалярных процессоров – это конфликт по функциональному блоку, когда на него претендуют несколько команд, поступивших из разных конвейеров. В режиме параллельного выполнения нескольких команд процессор должен определить, в какой очередности ему следует выбирать команды из памяти, выполнять эти команды, позволять командам изменять содержимое регистров и ячеек памяти. Для достижения максимальной загрузки всех ступеней своих конвейеров суперскалярный процессор должен варьировать все перечисленные виды последовательностей, но так, чтобы получаемый результат был идентичен результату при выполнении команд в порядке, определенном программой. Значит, процессор обязан учитывать все виды зависимостей и конфликтов.
В самом общем виде стратегии выдачи и завершения команд могут быть сгруппированы в следующие комбинации:
1) упорядоченная выдача и упорядоченное завершение;
2) упорядоченная выдача и неупорядоченное завершение;
3) неупорядоченная выдача и неупорядоченное завершение.
Стратегия упорядоченной выдачи и упорядоченного завершения является наиболее простым в реализации вариантом, при котором выдача декодированных команд на исполнение производится в том же порядке, в котором они должны выполняться по программе (упорядоченная выдача), с сохранением той же последовательности записи результатов (упорядоченное завершение). При этом все, что затрудняет завершение команды в одном конвейере, останавливает и другой конвейер, так как команды должны покидать конвейеры, соответствуя порядку поступления на них.
Стратегия упорядоченной выдачи и неупорядоченного завершения дает возможность одному из конвейеров продолжать работать при «заторе» в другом. При этом команды, стоящие в программе «позже», могут быть фактически выполнены раньше предыдущих, «застрявших» в другом конвейере. Естественно, процессор должен гарантировать, что результаты не будут записаны в память, а регистры не будут модифицироваться в неправильной последовательности, поскольку при этом могут получиться ошибочные результаты. По сравнению с предыдущей стратегией возможность неупорядоченного завершения команд приводит к сокращению суммарного времени выполнения команд в процессоре.
Стратегия неупорядоченной выдачи и неупорядоченного завершения развивает предыдущую концепцию, разрешая процессору нарушать предписанный программой порядок выдачи команд на исполнение. Чтобы обеспечить неупорядоченную выдачу команд, в конвейере необходимо максимально развязать ступени декодирования и исполнения. Это обеспечивается с помощью специальной буферной памяти, называемой окном команд. Каждая декодированная команда сначала помещается в эту память. Процессор может продолжать выборку и декодирование новых команд вплоть до полного заполнения буфера. Выдача команд из буфера на исполнение определяется не последовательностью их поступления, а мерой готовности. Иными словами, любая команда, для которой уже известны значения всех операндов, при условии, что функциональный блок, требуемый для ее исполнения, свободен, немедленно выдается из буфера на исполнение. Стратегии неупорядоченной выдачи и неупорядоченного завершения также свойственны ранее рассмотренные ограничения. В частности, команда не может быть выдана, если она приводит к зависимости или конфликту. Разница заключается в том, что к выдаче готово больше команд, и это позволяет уменьшить вероятность приостановки конвейера.
В целом стратегия неупорядоченной выдачи и неупорядоченного завершения команд – это дополнительный потенциал повышения производительности суперскалярного процессора, для реализации которого, вместе с тем, необходимо решить две проблемы:
1) устранить зависимость команд по данным, то есть исключить использование в качестве операнда «устаревшего» значения регистра и не допускать, чтобы очередная команда программы из-за нарушения последовательности выполнения команд заносила свой результат в регистр еще до того, как это сделала предшествующая команда;
2) сохранить такой порядок выполнения команд, чтобы общий итог вычислений остался идентичным результату, получаемому при строгом соблюдении программной последовательности.
Несмотря на то, что обе задачи в принципе могут быть решены чисто программными средствами еще на этапе компиляции программы, в реальных суперскалярных процессорах для этих целей имеются соответствующие аппаратные средства. Каждая из перечисленных проблем решается своими методами и своими аппаратными средствами. Для устранения зависимости по данным используется прием, известный как переименование регистров. Способ решения второй проблемы обобщенно называют переупорядочиванием команд (или откладыванием исполнения команд).
Основная идея переименования регистров состоит в том, что каждый новый результат записывается в один из свободных в данный момент дополнительных регистров, при этом ссылки на заменяемый регистр во всех последующих командах соответственным образом корректируются. Переименование регистров может быть реализовано и по-другому, например, с помощью специального буфера переименования.
Способ переупорядочивания команд заключается в следующем. После декодирования команд и переименования регистров команды передаются на исполнение. Как уже отмечалось, выдача команд в функциональные блоки может производиться неупорядоченно, по мере готовности. Поскольку порядок выполнения команд может отличаться от предписанного программой, необходимо обеспечить корректность их операндов (частично решается путем переименования регистров) и правильную последовательность занесения результатов в регистры. Одним из наиболее распространенных приемов решения этих проблем и является способ переупорядочивание команд. В его основе лежат использование упомянутого выше окна команд (буферной памяти, куда помещаются все команды, прошедшие декодирование) и переименование регистров (последняя операция выполняется только с теми командами, которые записывают свой результат в регистры). Окно команд обеспечивает отсрочку передачи команд на исполнение до момента готовности операндов, а также нужную очередность завершения команд и загрузки их результатов в регистры.
Рассматриваемая технология предполагает схему распределения готовых команд по требуемым для их исполнения функциональным блокам с одновременной проверкой их доступности (диспетчеризацией). В каждом такте работы процессора готовыми к выдаче могут оказаться сразу несколько команд, и все готовые команды должны быть направлены в соответствующие функциональные блоки. Если имеется несколько однотипных блоков обработки, то в процессоре должна быть предусмотрена логика выбора одного из них.
Для поддержания правильной последовательности исполнения команд в случае нескольких параллельно работающих функциональных блоков применяется механизм буфера восстановления последовательности. Название буфера подчеркивает его основную задачу – поддержание строгой последовательности завершения команд путем переупорядочивания тех из них, которые исполнялись с нарушением этой последовательности. Однако этот буфер используется на практике и для других целей, например, для переименования регистров и для распределения декодированных команд по схемам резервирования.
Достаточно часто стратегии неупорядоченной выдачи и неупорядоченного завершения команд относят к методам так называемого динамического (или с измененным порядком) исполнения команд.
В последнее время благодаря развитию технологии производства микросхем начинает применяться процессорная VLIW-архитектура с командными словами сверхбольшой длины или со сверхдлинными командами (Very Long Instruction Word – VLIW). Идея VLIW-архитектуры базируется на том, что задача эффективного планирования параллельного выполнения нескольких команд возлагается на «разумный» компилятор. Такой компилятор вначале исследует исходную программу с целью обнаружить все команды, которые могут быть выполнены одновременно, причем так, чтобы это не приводило к возникновению конфликтов. В процессе анализа компилятор может даже частично имитировать выполнение рассматриваемой программы. На следующем этапе компилятор пытается объединить такие команды в пакеты, каждый из которых рассматривается как одна сверхдлинная команда. Объединение нескольких простых команд в одну сверхдлинную производится по следующим правилам:
– количество простых команд, объединяемых в одну команду сверхбольшой длины, равно числу имеющихся в процессоре функциональных (исполнительных) блоков;
– в сверхдлинную команду входят только такие простые команды, которые исполняются разными функциональными блоками, то есть обеспечивается одновременное исполнение всех команд, составляющих сверхдлинную команду.
Длина сверхдлинной команды обычно составляет от 256 до 1024 бит. Такая метакоманда содержит несколько полей (по числу образующих ее простых команд), каждое из которых описывает операцию для конкретного функционального блока. Каждое поле сверхдлинной команды отображается на свой функциональный блок, что позволяет получить максимальную отдачу от аппаратуры блока исполнения команд.
VLIW-архитектуру можно рассматривать как статическую суперскалярную архитектуру. Имеется в виду, что распараллеливание кода производится на этапе компиляции, а не динамически во время исполнения. То, что в выполняемой сверхдлинной команде исключена возможность конфликтов, позволяет предельно упростить аппаратуру VLIW-процессора и, как следствие, добиться более высокого быстродействия.
В качестве простых команд, образующих сверхдлинную, обычно используются команды RISC-типа, поэтому архитектуру VLIW иногда называют постRISC-архитектурой. Максимальное число полей в сверхдлинной команде равно числу вычислительных устройств и обычно колеблется в диапазоне от 3 до 20. Все вычислительные устройства имеют доступ к данным, хранящимся в едином многопортовом регистровом файле. Отсутствие сложных аппаратных механизмов, характерных для суперскалярных процессоров (предсказание переходов, внеочередное исполнение и т. д.), дает значительный выигрыш в быстродействии и возможность более эффективно использовать площадь микросхемы. Серьезной проблемой VLIW является усложнение регистрового файла и связей этого файла с вычислительными устройствами.
Дальнейшим развитием технологии VLIW стал новый подход к архитектуре процессора, известный как EPIC-архитектура, предполагающаявычисления с явным параллелизмом команд (Explicitly Parallel Instruction Computing – EPIC). По сути EPIC является усовершенствованным вариантом VLIW. В EPIC предполагается наличие в процессоре 128 штук 64-разрядных регистров общего назначения и 128 штук 80-разрядных регистров с плавающей запятой. Команды упаковываются (группируются) компилятором в сверхдлинную команду – связку длиною в 128 разрядов. Связка содержит три команды и шаблон, в котором указываются зависимости между командами, а также между другими связками. Одна связка, состоящая из трех команд, соответствует набору из трех функциональных блоков процессора. Процессоры с технологией EPIC могут содержать разное количество таких наборов из блоков, оставаясь при этом совместимыми по коду. Логика выдачи команд на исполнение сложнее, чем в традиционных процессорах VLIW-архитектуры, но намного проще, чем у суперскалярных процессоров с неупорядоченной выдачей. Концепция EPIC, сохраняя все достоинства архитектурной организации VLIW, обладает лучшей масштабируемостью архитектуры до большого количества функциональных блоков.
В общих чертах основными преимуществами VLIW-архитектуры является использование компилятора, который позволяет устранить зависимости между командами до того, как они будут реально выполняться, в отличие от суперскалярных процессоров, где такие зависимости приходится обнаруживать и устранять «на лету». Отсутствие зависимостей между командами в коде, сформированном таким компилятором, ведет к упрощению аппаратных средств процессора и за счет этого к существенному увеличению его быстродействия. Наличие множества функциональных блоков дает возможность выполнять несколько команд параллельно. Однако для эффективной реализации VLIW-архитектуры требуется новое поколение компиляторов, способных проанализировать программу, найти в ней независимые команды, связать такие команды в строки длиной от 256 до 1024 бит, обеспечить их параллельное выполнение. При этом компилятор должен учитывать конкретные детали аппаратных средств и в определенных ситуациях программа может оказаться недостаточно гибкой.
Характеристики реальных процессоров и применяемые в них архитектурные решения рассмотрим на примере современного этапа разработки процессоров, начало которого положено внедрением в вычислительную технику электронных микросхем с высокой степенью интеграции активных элементов – большихисверхбольших интегральных схем(БИС и СБИС).
Дата добавления: 2015-12-17; просмотров: 4777;