Устройство обработки ветвлений BPU и предсказание ветвлений
Если взглянуть на левую часть препроцессора G4e, то можно заметить устройство, расположенное рядом со ступенями выборки и декодирования/отправки. Это устройство обработки ветвлений (branch processing unit, BPU). Оно выполняет руководящую функцию и управляет препроцессором (а, следовательно, и всем остальным процессором). Как только декодер встречает условную инструкцию ветвления, он посылает её на устройство обработки ветвлений. Устройству обработки ветвлений, в свою очередь, требуется отослать эту инструкцию на одно из устройств выполнения, чтобы оценить условие исполнения ветви. Как только BPU определяет, что данная ветвь верна, оно должно вычислить адрес следующего блока кода, который нужно выполнять. Этот адрес ("branch target") должен быть передан препроцессору, чтобы тот начал выборку кода по новому адресу.
Старые процессоры простаивали пока происходила оценка условия ветвления. Условие же могло оцениваться довольно долго - при этом могли выполнятся довольно сложные вычисления. Современные же процессоры используют так называемые "спекулятивные вычисления" ("speculative execution"). Эта техника предсказывает, какую из ветвей выполнять далее, и начинает выполнять эту ветвь до того, как было оценено условие. При таком предсказании используются различные техники "предсказания ветвлений". О них мы поговорим чуть позднее. Спекулятивные вычисления используются для избежания появления пузырьков в конвейере в результате задержек при оценке условий.
При неправильных предсказания такая техника не играет на руку. Если процессор уже начал спекулятивно выполнять все эти инструкции, если он уже загрузил их в конвейер, а потом выяснилось, что их выполнять не надо, процессор должен высвободить весь конвейер. Затем он должен выбрать правильные инструкции по правильному адресу и начать весь процесс выполнения заново. Освобождение конвейера от неправильных инструкций означает, что работа по предсказанию пошла насмарку. Более того, существует задержка (а следовательно в конвейере появляются новые пузырьки), связанная с вычислением правильной ветви и загрузкой нового потока команд в препроцессор. Всё это может значительно сократить производительность, в особенности при интенсивном ветвлении в коде.
Как вы уже вероятно догадались, чем длиннее конвейер в процессоре, тем дороже обходится ошибочное предсказание. Ранее мы уже показали как задержки в потоке команд и сопутствующие им пузырьки могут снизить производительность процессоров с длинными конвейерами, поэтому нет смысла говорить о том, как плачевно отражаются двадцать ступеней конвейера в процессоре P4 на операциях с ветвлением и как долго приходится ожидать выборки новых инструкций из кэша. Кроме того, следует учитывать и то, что чем длиннее конвейер, тем большее количество спекулятивных инструкций можно загрузить в процессор, и соответственно тем больше работы пропадёт даром при ошибочном предсказании.
Минимальные потери неправильного предсказания в P4 - 19 тактов для кода, если он находится в кэше L1. Это только минимум. При других обстоятельствах потери могут быть значительно больше, особенно если в кэше L1 нет кода правильной ветви. (В этом случае потери достигают 30 тактов). Потери же семиступенчатого конвейера G4e при неправильных предсказаниях оказываются значительно меньше. Но всё равно, его потери все же больше, чем у классического предшественника G4. Так, в G4е они составляют 6 тактов, а потери G4 - 4 такта.
Существует два вида предсказаний: статические и динамические. Статические предсказание просты, и берут за основу предположение, что большинство обратных ветвлений происходит в повторяющихся циклах, когда инструкция ветвления используется для определения продолжения цикла или выхода из него. Чаще всего цикл продолжается, так что компьютер будет снова повторно выполнять код цикла. По этой причине статическое предсказание считает что все обратные ветвления всегда выполняются. Если же ветвление указывает на блок кода, который существует дальше в программе, то статическое предсказание не будет выполнять такую ветвь.
Статические предсказания очень быстры, так как при этом не требуется дополнительных проверок или вычислений. Но эффективность таких предсказаний довольно сильно варьируется. В случае, когда в программе много циклов, статические предсказания прекрасно работают. В противном случае, производительность значительно снижается. Для разрешения проблем, связанных с предсказаниями, разработчики процессоров используют различные алгоритмы предсказания ветвлений. Алгоритмы "динамического предсказания ветвлений" обычно задействуют одну из таблиц - для хранения истории предсказаний ветвлений (Branch History Table, BHT) или для хранения адресов инструкций (Branch Target Buffer, BTB). Либо используется сразу оба типа таблиц. В таблицы записывается информация о результатах уже выполненных ветвлений. В BHT содержатся все условные переходы, что встретились устройству предсказания ветвлений за несколько последних циклов. Кроме того, здесь хранятся биты, показывающие вероятность повторного выбора той же самой ветви. Биты расставляются на основании статистики предыдущих переходов. В стандартной 2-битной схеме истории ветвления существует четыре вероятности: ветвь часто выполняется (strongly taken), ветвь выполняется (taken), ветвь не выполняется (not taken), и ветвь часто не выполняется (strongly not taken). Когда препроцессор встречает инструкцию ветвления, уже содержащуюся в BHT, устройство предсказания ветвлений использует информацию для решения о выполнении спекулятивной инструкции.
Для того, чтобы вынести решение о спекулятивном выполнении ветви, устройство должно знать точное местоположение кода в кэше L1 по направлению ветвления, назовем его целью ветвления. Цели уже выполненных ветвлений хранятся в BTB. Когда выполняется ветвление, BPU просто берёт цель ветвления из таблицы и указывает препроцессору начать выборку инструкций по этому адресу. Будем считать, что в таблице уже имеется адрес той ветви, что мы пытаемся выполнить, и будем считать, что он правильный. В противном случае мы столкнёмся с проблемой. Не будем вдаваться в подробности о влиянии BTB на производительность, просто скажем, что чем больше таблица, тем лучше.
Чтобы справиться с задержками и неправильными предсказаниями, G4e и P4 используют оба метода (статический и динамический) для предсказания ветвлений. Если в BHT нет инструкции ветвления, то оба процессора будут использовать статическое предсказание для выбора дальнейших действий. Если же инструкция в BHT есть, то оба процессора воспользуются динамическим предсказанием. Заметим, что в P4 таблица истории довольно велика - до 4096 записей, что вполне достаточно для хранения информации о большинстве ветвлений средней программы. Заметим также, что доля успешных предсказаний у его предшественника, PIII составляет около 91%. Утверждают, что у P4 эта цифра ещё больше, так как он использует более эффективные алгоритмы предсказаний. Кроме того, P4 использует BTB для хранения предсказанных адресов ветвления. В документации Intel BHT и BTB обозначены как "the front-end BTB."
Таблица истории G4e умещает до 2048 записей. В G4 это число составляло 512. Мы не обладаем данными об успешности предсказаний этого процессора, но думаем, что она довольно хороша. G4e обладает кэшем адресов инструкций (Branch Target Instruction Cache, BTIC) на 128 записей, эквивалент BTB в P4. В G4 этот кэш был размером всего в 64 записи.
Однако следует отметить, что оба процессора расходуют на предсказания ветвлений больше ресурсов, чем их предшественники. Причиной тому являются их длинные конвейеры, которые при ошибочных предсказаниях снижают производительность процессора.
У P4 есть ещё один финт (очень сомнительный) - "подсказки программного уровня" ("software branch hints") - небольшие префиксы, которые помещаются компилятором или программистом перед условными операторами ветвления. С помощью этих префиксов устройство предсказания может судить о предполагаемом направлении ветвления. Информации об эффективности этих префиксов крайне мало, и Intel рекомендует ими не злоупотреблять, так как это может привести к увеличению программного кода.
Если Вы заметили ошибку — выделите ее мышью и нажмите CTRL+ENTER.