feat: дописана часть конфликтов по данным и начата часть бранч-предиктора
This commit is contained in:
@ -2,9 +2,13 @@
|
||||
Статус: в работе
|
||||
Соответствует: "2"
|
||||
---
|
||||
> [!info]+ Билет может измениться
|
||||
> Поскольку с пунктом "построение статической и динамической трассы" пока еще есть вопросы, билет в будущем может измениться по мере написания других билетов, поэтому за ним закреплен статус "в работе"
|
||||
|
||||
|
||||
*Длиннющий билет*
|
||||
|
||||
> [!info]+ Отступление про конвейер
|
||||
> [!info]- Отступление про конвейер
|
||||
> Большинство процессоров сейчас конвейерные. Выполнение инструкции разбивается на отдельные этапы, которые могут выполняться +- паралелльно. В рамках методички выделяются следующие типичные стадии конвеера
|
||||
> - выборка команды - IF (по адресу, заданному счетчиком команд, из памяти извлекается команда);
|
||||
> - декодирование команды / выборка операндов из регистров - ID;
|
||||
@ -35,7 +39,7 @@
|
||||
Может возникнуть например когда нескольким ступеням конвейера необходимо обратиться к памяти, а у ОЗУ только один порт и он не поддерживает много обращений одновременно. Чтобы разрешить эту ситуацию, можно просто приостановить конвейер на один такт, когда происходит обращение к памяти за данными. Подобная приостановка часто называются "конвейерным пузырем" (pipeline bubble) или просто пузырем, поскольку пузырь проходит по конвейеру, занимая место, но не выполняя никакой полезной работы
|
||||
%%
|
||||
|
||||
Продемонстрируем структурные конфликт на следующем примере: "когда одна команда содержит обращение к памяти за данными, оно будет конфликтовать с выборкой более поздней команды из памяти"
|
||||
Продемонстрируем структурные конфликт на следующем примере: "когда одна команда содержит обращение к памяти за данными, оно будет конфликтовать с выборкой более поздней команды из памяти" (анимация далее)
|
||||
### Решение
|
||||
|
||||
**Чтобы разрешить эту ситуацию, можно просто приостановить конвейер на один такт, когда происходит обращение к памяти за данными.**
|
||||
@ -83,3 +87,122 @@
|
||||
|
||||
*схема на рисунке*
|
||||
![[Pasted image 20250609162101.png]]
|
||||
|
||||
**Закоротка - не панацея**. Проблемы появляются например при обращении к памяти, где не получится просто использовать предыдущий результат АЛУ, потому что обращение к памяти создает вполне серьезную задержку
|
||||
|
||||
![[Pasted image 20250609163959.png]]
|
||||
|
||||
> [!tip]- Анимация для удобства восприятия
|
||||
> ![[pipeline_interlook.gif]]
|
||||
|
||||
Для реализации такого "проталкивания" только части конвейера необходимы дополнительные комлпектующие, называемые "**аппаратурой внутрених блокировок конвейера (pipeline interlook)**". Эта аппаратура приостанавливает конвейер начиная с команды, которая хочет использовать данные в то время, когда предыдущая команда, результат которой является операндом для нашей, вырабатывает этот результат
|
||||
|
||||
*Заметим, что вырабатывается такой же "пузырь", что мы видели в структурных конфликтах*
|
||||
|
||||
## Зависимости по управлению и методы их преодоления
|
||||
|
||||
- Примерно 10% команд в коде - безусловные переходы
|
||||
- Примерно 10-20% - условные
|
||||
|
||||
Конфликты по управлению возникают из-за условных переходов в программе: конвейер не может начать выбирать команды заранее, потому что не знает, будет ли выполнен переход или нет. Если ничего не предпринять, то при каждой команды условного перехода придется замораживать поступление новых команд на конвейер до ее выполнения, чтобы понять, откуда их нужно будет выбирать. Это простои в 4-15 тактов в зависимости от количество этапов на конвейере.
|
||||
|
||||
![[Pasted image 20250609173721.png]]
|
||||
|
||||
> [!tip]- Анимация для удобства восприятия
|
||||
> ![[cond_jmp_ignore.gif]]
|
||||
> Здесь приостанавливается выборка последующих элементов до определения исхода условного перехода
|
||||
|
||||
То, чего мы хотели бы на самом деле - попытаться угадать, будет ли выполнен переход, и начать выбирать команды заранее. Именно это и делают производители процессора при помощи branch predictor'а. А вот как он реализуется будет рассмотрено ниже
|
||||
|
||||
**Немного терминологии**
|
||||
- Выполняемый переход - переход, который свершился (изменил значение в pc)
|
||||
- Невыполняемый переход - переход, которые не свершился (не изменил значение в pc)
|
||||
|
||||
### Простые методы сокращения приостановок конвейера
|
||||
|
||||
*Или простейшие алгоритмы для branch predictor'а*
|
||||
|
||||
- **Метод выжидания** - просто замораживает весь конвейер, пока не будет доподлинно известно, будет ли выполнен переход и куда
|
||||
- **Метод возврата** - считает все условные переходы невыполняемыми и начинает выбирать команды сразу за соответствующей инструкцией. Если переход все же выполняемый, выбрасывает из конвейера частично выполненные команды и выбирает их уже с нового места
|
||||
- **Задержанные переходы** - часть инструкций в процессе работы не выполняется, а помещается в "слот задержки". Когда встречается команда условного перехода, мы говорим "ну раз ты все равно сейчас не сможешь выполнять новые команды, выполни отложенные старые". При этом дается гарантия, что такие инструкции выполнятся до выполнения перехода. Вот 2 примера, которые приводятся в методичке:
|
||||
- Слот задержки заполняется командой перед командой условного перехода
|
||||
- Слот задержки заполняется командой, находящейся по целевому адресу команды перехода. Это выгодно, если мы считаем, что переход мы все же выполним (например если мы находимся в цикле, то с большей вероятностью мы все же перейдем)
|
||||
|
||||
*Это были простые методы уменьшить конфликты по управлению. Сложные рассмотрены будут дальше. Вот их список*
|
||||
|
||||
- Раннее распознание команды условного ветвления
|
||||
- Проверка и предсказание выполнения условия перехода
|
||||
- Ускоренное (раннее) вычисление целевого адреса перехода
|
||||
- Быстрая передача управления, в случае перехода
|
||||
- Свертывание переходов
|
||||
- Параллельное выполнение команд из окна исполнения
|
||||
- Разворачивание циклов
|
||||
|
||||
# Построение статической и динамической трассы
|
||||
|
||||
> [!warning]+ %%%%
|
||||
> В методичке нет такого раздела, да и трасы обсуждаются много позже - где-то в районе кэшей. Поэтому я предполагаю, что речь идет все же именно про предсказание переходов. возможно потом поменяется
|
||||
|
||||
Существуют два основных метода предсказании переходов - статический (static) и динамический (dynamic). Статические упрощены и предписывают всегда что-то делать или, наоборот, никогда что-то не делать. Динамические методы предсказывают условные переходы на основе их поведения в предшествующий период времени. Динамические методы, как правило, более точны
|
||||
|
||||
> [!info]+ Предсказания могут лажать
|
||||
> Все наши предсказания перехода нужны только для того, чтобы начать заранее выбирать команды и не давать конвейеру простаивать. Чтобы узнать, правильное ли было предсказание, выполнить команду перехода все равно придется и узнаем, правильно ли мы начали выбирать команды мы только в конце.
|
||||
>
|
||||
> Если мы оказались не правы, то придется сбросить весь набранный конвейер
|
||||
|
||||
## Методы статического предсказания
|
||||
|
||||
Методы живут как на этапе исполнения, так и на этапе компиляции. На этапе компиляции используются 2 основных подхода:
|
||||
|
||||
- **Метод исследования структуры программы** - компилятор анализирует представление программы и на основе этого решает, какими будут команды перехода - выполняемыми или не выполняемыми. Например (для начала рассуждений) все прыжки циклов будут считаться выполняемыми, а прыжки ветвления - невыполняемыми.
|
||||
- **Метод использования профиля программы** - программа компилируется, а затем запускается вместе с утилитами трассировки, которые строят трассу программы (в какие функции мы заходили, с какой частотой и прочее). Затем на основе этого профиля компилятор помечает какие-то прыжки более вероятными, а какие-то - менее
|
||||
|
||||
Как компилятор посигналит о предполагаемом направлении перехода зависит от команд процессора:
|
||||
|
||||
- В некоторых процессорах коды команд содержат намек о предполагаемом направлении перехода
|
||||
- В других для некоторых команд переход считается более вероятным
|
||||
|
||||
## Методы динамического предсказания
|
||||
|
||||
*Ох и много ж тут...*
|
||||
|
||||
### Прогнозирование перехода
|
||||
|
||||
Простейшей схемой такого прогнозирования является **буфер прогнозирования условных переходов (branch-prediction buffer)** или **таблица "истории" условных переходов (branch history table**. Заводится маленькая таблица, в качестве адресов в ней используются n младших бит адреса команды с ветвлением. В качестве значения там хранится 1 бит - был ли на этой команде в прошлый раз переход.
|
||||
|
||||
![[Pasted image 20250609192228.png]]
|
||||
|
||||
Это простейший вариант реализации данной схемы. Однако вариант с одним битом имеет недостаточную производительность, поскольку, например, с циклами ошибается стабильно 2 раза, а не один. При чем каждый раз, когда этот цикл будет встречаться в программе. Представьте, если это цикл в цикле
|
||||
|
||||
> [!note]- объяснение из методички
|
||||
> Простая однобитовая схема прогноза имеет недостаточную производительность. Рассмотрим, например, команду условного перехода в цикле, которая являлась выполняемым переходом последовательно девять раз подряд, а затем однажды невыполняемым. Направление перехода будет неправильно предсказываться при первой и при последней итерации цикла. Неправильный прогноз последней итерации цикла неизбежен, поскольку бит прогноза будет говорить, что переход "выполняемый" (переход был девять раз подряд выполняемым). Неправильный прогноз на первой итерации происходит из-за того, что бит прогноза инвертируется при предыдущем выполнении последней итерации цикла, поскольку в этой итерации переход был невыполняемым. Таким образом, точность прогноза для перехода, который выполнялся в 90% случаев, составила только 80% (2 некорректных прогноза и 8 корректных). В общем случае, для команд условного перехода, используемых для организации циклов, переход является выполняемым много раз подряд, а затем один раз оказывается невыполняемым. Поэтому однобитовая схема прогнозирования будет неправильно предсказывать направление перехода дважды (при первой и при последней итерации).
|
||||
|
||||
Для исправления этого положения часто используется схема двухбитового прогноза. В двухбитовой схеме прогноз должен быть сделан неверно дважды, прежде чем он изменится на противоположное значение.
|
||||
|
||||
![[Pasted image 20250609193147.png]]
|
||||
|
||||
Эта схема обобщается на n-битные счетчики:
|
||||
|
||||
- Если в самом старшем бите счетчика стоит единица - переход считается выполняемым
|
||||
- Если переход выполнился, к счетчику прибавляется единица
|
||||
- Если переход не выполнился, из счетчика вычитается единица
|
||||
- Сложения и вычитания реализуются с насыщением (при прибавлении единицы не будет переполнения, равно как и при вычитании отрицательного переполнения)
|
||||
|
||||
Тем не менее n-битная схема лишь несущественно уступает n-битной
|
||||
|
||||
Есть разные способы прикрутить это предсказание к процессору. Можно оформить это как отдельную кэш-память, к которой мы обращаемся "заодно" на этапе выборки команды из памяти (IF). Можно привязать эти 2 бита к каждому блоку кэш-памяти и тащить их к каждой команде из этого блока (только тогда если несколько переходов есть в одном блоке, начнутся неточности. Но в целом терпимые)
|
||||
|
||||
> [!info]- Точность прогнозов
|
||||
> *Он любит почему-то спрашивать цифры*
|
||||
> На замерах SPEC-89 с буфером прогнозирования в 4096 строк дает результат в 99%-82% правильных прогнозов. Правда 4096 строк - это очень большой объем буфера. Меньший буфер дает результат похуже
|
||||
|
||||
"Вероятно можно улучшить точность прогноза, если учитывать не только поведение того перехода, который мы пытаемся предсказать, но рассматривать также и недавнее поведение других команд перехода. Схемы прогнозирования, которые для предсказания направления перехода используют поведение других команд перехода, называются **коррелированными или двухуровневыми схемами прогнозирования**. Схема прогнозирования называется прогнозом (1,1), если она использует поведение одного последнего перехода для выбора из пары однобитовых схем прогнозирования на каждый переход. В общем случае схема прогнозирования (m,n) использует поведение последних m переходов для выбора из 2m схем прогнозирования, каждая из которых представляет собой n-битовую схему прогнозирования для каждого отдельного перехода"
|
||||
|
||||
Этот метод точнее, при этом не требует сильно больше аппаратуры
|
||||
|
||||
Благодаря методам предсказания, встретив команду условного перехода, мы можем заранее начать грузить конвейер, как только будет вычислен адрес, куда будет совершен прыжок... Но это вычисление адреса прыжка все еще генерирует простой конвейера. Нельзя ли как-то определить адрес перехода до непосредственного декодирования команды и выполнения вычислений адреса перехода?
|
||||
### Быстрое определение адреса перехода
|
||||
|
||||
|
||||
### Метод свертывания переходов
|
||||
### Метод прогнозирования косвенных переходов
|
||||
BIN
assets/Pasted image 20250609163959.png
(Stored with Git LFS)
Normal file
BIN
assets/Pasted image 20250609163959.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/Pasted image 20250609173721.png
(Stored with Git LFS)
Normal file
BIN
assets/Pasted image 20250609173721.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/Pasted image 20250609175941.png
(Stored with Git LFS)
Normal file
BIN
assets/Pasted image 20250609175941.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/Pasted image 20250609192228.png
(Stored with Git LFS)
Normal file
BIN
assets/Pasted image 20250609192228.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/Pasted image 20250609193147.png
(Stored with Git LFS)
Normal file
BIN
assets/Pasted image 20250609193147.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/cond_jmp_ignore.gif
(Stored with Git LFS)
Normal file
BIN
assets/cond_jmp_ignore.gif
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/pipeline_interlook.gif
(Stored with Git LFS)
Normal file
BIN
assets/pipeline_interlook.gif
(Stored with Git LFS)
Normal file
Binary file not shown.
Reference in New Issue
Block a user