- •Глава 31. Особенности структуры высокопроизводительных ядер процессоров для персональных компьютеров
- •31.1. Основные особенности структуры ядер процессоров
- •Блок предсказания переходов и ветвлений
- •31.2. Примеры структур ядер процессоров фирмы Intel для пк
- •Структура подсистемы подготовки команд для выполнения
- •Предсказание переходов
- •Декодирование и idq
- •Особенности ядра со структурой Haswell
Глава 31. Особенности структуры высокопроизводительных ядер процессоров для персональных компьютеров
31.1. Основные особенности структуры ядер процессоров
Основные особенности структуры ядер процессоров следующие:
Большая длина конвейера – до 12-16 ступеней.
Выполнение команд по предположению - «спекулятивное» выполнение.
Иерархическая структура подсистемы кэш-памяти.
Большое число специализированных функциональных (исполнительных) устройств.
Буферирование на всех этапах конвейера.
Суперскалярная организация. Это означает, что на каждом этапе обрабатываются сразу несколько потоков команд (операций) параллельно — от выборки из кэш-памяти команд до полного завершения («отставки»). Суперскалярность наряду с частотой является важнейшим показателем пропускной способности ядра процессора. Уровень суперскалярности (количество команд подготавливаемых к выполнению в одном такте) в современных высокопроизводительных ядрах процессоров варьируется от 3 до 4-5.
Предварительное преобразование архитектурных команд в промежуточные операции (микрооперации), более удобные для обработки и выполнения. Иногда такие операции называют RISC-подобными, исходя из того, что архитектуры RISC разрабатывались как раз для того, чтобы обеспечить простоту, удобство и эффективность обработки архитектурных команд. Преобразование команд является необходимым для архитектуры IA-32/64 с её крайне нерегулярной и запутанной системой команд. Однако и для достаточно регулярной RISC-архитектуры такое преобразование также производится — оно необходимо для разбиения некоторых сложных команд на простые операции. В англоязычной литературе эти микрооперации называют «МОПами» (либо просто «операциями»).
Внеочередное выполнение команд. Это означает, что команды не обязаны выполняться в функциональных устройствах строго в том порядке, который определён в программе. Более поздние команды могут выполняться перед более ранними командами, если не зависят от порождаемых ими результатов. Ядро процессора должно лишь гарантировать, что результаты «внеочередного» выполнения команд совпадают с результатами «правильного» последовательного их выполнения. Механизм внеочередного выполнения позволяет в значительной степени сгладить эффект от ожидания считывания данных из кэш-памятей верхних уровней и из оперативной памяти. Ожидание данных может длиться десятки и сотни тактов. Также он позволяет оптимизировать выполнение смежных операций, особенно при наличии сложных зависимостей между ними в условиях длительных задержек выполнения в устройствах и недостаточного количества регистров.
Поскольку исходная программа подразумевает последовательную модель выполнения, машинные команды должны считываться также последовательно. Кроме того, есть такое понятие, как «отставка» команды, которое подразумевает, что данная команда выполнена вместе со всеми командами, которые ей предшествовали в тексте программы. Таким образом, команды уходят в «отставку» строго последовательно, именно в том порядке, который задаётся программой.
Понятия «отставки» является ключевым в отображении внеочередного выполнения команд внутри процессора в чисто последовательное выполнение в «модели ядра процессора», как она представляется пользователю. Может оказаться, что какая-то команда уже выполнилась в своём функциональном устройстве — но при этом не должна была выполняться, так как было неправильно предсказано направление условного перехода либо были считаны данные с неправильного адреса. Такое выполнение называется «спекулятивным» или «выполнением по предположению». Когда будет правильно выполнена предшествующая ей команда перехода или чтения данных, такая неверная «спекулятивная» ветвь будет отменена, и при необходимости команда будет выполнена вновь (либо выполнится другая ветвь). Процедура «отставки» команды гарантирует, что она была выполнена «правильно». Любые прерывания программы (по вводу-выводу либо аварийному событию) могут происходить только в момент «отставки» команды, к которой это прерывание относится.
Предсказание ветвлений. В любой более-менее сложной программе присутствуют команды условного перехода: «Если некое условие истинно — перейти к исполнению одного участка программы, если нет — другого». С точки зрения скорости выполнения программы современным ядром процессора с большим числом подготовительных стадий и собственно выполнением (в подсистеме обработки данных в специализированных функциональных исполнительных устройствах), любая команда условного перехода — тяжелое испытание. Ведь до тех пор, пока не станет известно, какой участок программы после условного перехода окажется «актуальным» — его невозможно начать дешифрировать и выполнять. Для того чтобы как-то примирить концепцию «длинных» конвейеров с командами условного перехода, используется блок предсказания ветвлений. Он пытается предсказать, на какой участок программы укажет команда условного перехода, ещё до того, как она будет выполнена, и сработает ли переход вообще. В соответствии с указаниями блока предсказания, ядром процессора производятся вполне реальные действия:
если предсказанный участок программы уже продешифрирован и находится в кэш-памяти продешифрированных команд (L0m – в терминологии фирмы Intel), то он читается из него и попадает в буфера подсистемы обработки данных;
если предсказанный участок программы находится в кэш-памяти команд первого уровня (L1К), то читается порция командной информации, дешифрируется сначала предварительным дешифратором (предекодером), а затем несколькими (в зависимости от степени суперскалярности) окончательными дешифраторами и попадает в буфера подсистемы обработки данных;
если предсказанный участок программы отсутствует в кэш-памятях всех уровней, то:
требуемая командная информация загружается из оперативной памяти во все уровни кэш-памяти (в том числе и в кэш-память L1K);
читается из кэш-памяти L1K порция командной информации, дешифрируется сначала предекодером, а затем несколькими (в зависимости от степени суперскалярности) окончательными дешифраторами и попадает в буфера подсистемы обработки данных.
Среди выполняемых команд могут содержаться команды условного перехода, и их результаты тоже предсказываются, что порождает целую цепочку из пока не проверенных предсказаний. Разумеется, если блок предсказания ветвлений ошибся, вся проделанная в соответствии с его предсказаниями работа просто аннулируется.
Алгоритмы, по которым работает блок предсказания ветвлений, вовсе не являются шедеврами искусственного интеллекта. Когда ядро процессора впервые встречает условный переход, оно пытается предсказать его поведение «по одёжке» — какого типа команда, куда происходит переход (вперёд по ходу выполнения программы или назад — это если он вообще произойдёт) и пр. Точность такого предсказателя (он называется статическим) невелика. Самое интересное происходит, когда встречается уже знакомый переход. Чтобы его узнать, у блока предсказания есть специальная таблица историй переходов, хранящая описание поведения нескольких сотен или тысяч последних обнаруженных в программе команд ветвления вместе с их адресами. Далее уже динамический предсказатель делает заключение о вероятном поведении команды не «по одёжке», а основываясь на детальной накопленной статистике поведения и этой команды перехода, и предыдущих её «коллег», выполненных до неё. Для поддержки этой статистики каждый раз, когда команда перехода доходит до выполнения, её результат попадает в блок предсказания, чтобы тот скорректировал свою таблицу — перешли или нет, угадали или нет.
Несмотря на достаточно высокую эффективность алгоритмов, механизмы предсказания ветвлений в ядрах современных процессоров всё равно постоянно совершенствуются и усложняются — но тут уже речь идёт о борьбе за единицы процентов: например, за то, чтобы повысить эффективность работы блока предсказания ветвлений с 95 процентов до 97, или даже с 97 до 99.
Многоуровневое кэширование
Специфика конструирования современных ядер процессоров привела к тому, что систему кэширования в подавляющем большинстве процессоров приходится делать многоуровневой.
Кэш первого уровня в каждом ядре (самый «близкий» к ядру) традиционно разделяется на две (как правило, равные) части: кэш команд (L1К) и кэш данных (L1D). Это разделение предусматривается так называемой «гарвардской структурой» ядер процессора, которая по состоянию на сегодня является самой популярной для построения ядер современных процессоров различного назначения. В кэш-памяти команд первого уровня L1К, соответственно, располагаются только команды (с ним работает подсистема подготовки команд для выполнения), а в кэш-памяти данных первого уровня L1D — только данные (они впоследствии, как правило, попадают во внутренние регистры процессора).
Иерархически «над» кэш-памятью первого уровня L1 стоит кэш-память второго уровня L2. Он, как правило, в 8 раз больше по объёму, примерно втрое медленнее, и является уже «смешанной» — там располагаются и команды, и данные. В первых многоядерных процессорах у каждого ядра были свои кэш-памяти L1К и L1D, но общая кэш-память L2. В современных ядрах процессоров у каждого ядра есть своя кэш-память второго уровня L2.
Общей для всех ядер процессора является кэш-память третьего уровня L3, которая в 4-8 раз больше, чем кэш-память L2 (в расчете на одно ядро), и ещё примерно втрое медленнее (но всё ещё быстрее оперативной памяти). Алгоритм работы с многоуровневой кэш-памятью в общих чертах не отличается от алгоритма работы с одноуровневой кэш-памятью, но добавляются дополнительные итерации. Сначала информация ищется в кэш-памяти L1, если там промах — ищется в кэш-памяти L2, если снова промах — ищется в кэш-памяти L3, и уже потом, если ни на одном уровне кэш-памяти она не найдена — идёт обращение в оперативную память.
В большинстве ядер современных процессоров кэш-памяти L1 и L2 работают на той же частоте, что и процессорное ядро.
Ширина связей между ядром и кэш-памятью первого уровня, а также самими кэш-памятями может быть до 256 разрядов.
Как правило, кэш-памяти имеют наборно ассоциативную структуру, со степенью ассоциативности от 4 до 16.
Эксклюзивная и не эксклюзивная кэш-память
В случае не эксклюзивной кэш-памяти, информация на всех уровнях кэширования дублируется. Таким образом, кэш-память L2 содержит данные, которые уже находятся в кэш-памятях L1К и L1D, а кэш-память L3 содержит полную копию всего содержимого кэш-памяти L2 (и, соответственно, кэш-памятей L1К и L1D).
Эксклюзивная кэш-память, в отличие от не эксклюзивной кэш-памяти, предусматривает чёткое разграничение: если информация содержится на каком-то уровне кэш-памяти — то на всех остальных уровнях она отсутствует.
Плюс эксклюзивной кэш-памяти очевиден: общий размер кэшируемой информации равен суммарному объёму кэш-памятей всех уровней — а у не эксклюзивной кэш-памяти размер кэшируемой информации (в худшем случае) равен объёму самого большого (по размеру и по номеру) уровня кэш-памяти.
Минус эксклюзивной кэш-памяти менее очевиден, но он есть: необходим специальный механизм, который следит за собственно «эксклюзивностью».
В ядрах процессоров фирмы Intel используется концепция не эксклюзивной кэш-кэш-памяти, а в ядрах процессоров фирмы AMD – концепция эксклюзивной кэш-памяти. В целом, наблюдается классическое противостояние между объёмом и скоростью: за счёт эксклюзивности, при одинаковых объёмах кэш-памятей L1/L2 у ядер процессоров фирмы AMD общий размер кэшируемой информации получается больше — но при этом он работает медленней (задержки, вызванные наличием механизма обеспечения эксклюзивности).
Недостатки не эксклюзивной кэш-памяти фирма Intel компенсирует просто, но весомо - наращивая ее объёмы. Для наиболее производительных процессоров данной фирмы стала нормой кэш-память L3 с объёмом (2.0 – 2.5)М байт на одно ядро.
В ядрах процессоров фирмы AMD кэш-память второго уровня L2 имеет объем 512К байт на каждое из ядер, а кэш-память третьего уровня L3 немного меньшая по сравнению с аналогичной кэш-памятью в процессорах фирмы Intel.
Начиная с 2015 г. во всех новых процессорах фирма AMD использует концепцию не эксклюзивной кэш-памяти
Блок выборки команд
Выполнение любой команды ядром процессора начинается с выборки команды из подсистемы памяти (прежде всего из кэш-памяти команд L1К). РНК (регистр текущего номера команды) содержит виртуальный адрес текущего блока командной информации. Этот виртуальный адрес выдается в преобразователь виртуальных адресов в физические адреса (буфера TLB для кэш-памятей L1K, L1D)
Буфер TLB - это специальная кэш-память чисто ассоциативного типа. На вход этой кэш-памяти подается номер виртуальной страницы (старшие разряды виртуального адреса). Выполняется сравнение этого виртуального номера с содержимым каждой ячейки кэш-памяти. При удачном поиске из кэш-памяти извлекается номер физической страницы. К нему присоединяется смещение в странице (младшие разряды виртуального адреса), и физический адрес готов.
Буфер TLB может быть одноуровневым или двухуровневым. В современных ядрах процессоров емкость буфера TLB составляет десятки или даже сотни строк. Это означает, например, что будет быстрое преобразование виртуального адреса в физический адрес (ВА→ФА) для достаточно большого адресного пространства.
В случае неудачного поиска возникает прерывание. Обработка этого прерывания происходит в привелигированном режиме при отключенной кэш-памяти L1K. Время обработки этого прерывания очень большое. Время удачного преобразования виртуального адреса в физический адрес составляет (1 – 2) такта.
Физический адрес блока командной информации подается на вход кэш-памяти команд (L1K).
Кэш-память первого уровня во всех производительных ядрах разбита на два блока – кэш-память команд (L1K) и кэш-память данных (L1D) во избежание структурных конфликтов при одновременном обращении из разных стадий конвейера к первому уровню кэш-памяти.
При неудачном обращении к кэш-памяти команд L1K выполняется обращение к следующим уровням иерархии подсистемы памяти (кэш-памяти второго уровня L2, кэш-памяти третьего уровня L3, оперативной памяти).
Блок командной информации, как правило, это (16 – 32) байта, из кэш-памяти L1K заносится в буфер предварительной дешифрации.
После предварительной дешифрации блока команд (определены границы команд в блоке, их типы и другая необходимая информация) он направляется на входы основных дешифраторов команд.
Дешифратор
Функциональные (исполнительные) блоки всех ядер современных процессоров не работают с командами архитектуры IA-32/64. У всех ядер каждого процессора есть своя, «внутренняя» система команд, имеющая мало общего с теми командами, которые описаны в архитектуре. Команды, выполняемые ядром намного проще, «примитивнее», чем команды архитектуры.
Дешифратор преобразовывает команды архитектуры IA-32/64 во «внутренние» команды (МОПы), выполняемые ядром (при этом достаточно часто одна исходная команда преобразуется в несколько более простых «внутренних»). Дешифратор является важной частью ядра современного процессора: от его быстродействия зависит то, насколько постоянным будет поток команд, поступающих на функциональные блоки. Будут ли они работать или простаивать — во многом зависит от эффективности работы дешифратора.
В высокопроизводительных ядрах процессоров имеется не менее 4 основных дешифраторов. Если дешифраторов 4, то 3 дешифратора используются для дешифрации простых команд, а 1 дешифратор – для преобразования сложных команд.
Простые команды архитектуры IA-32/64 при дешифрации представляются с помощью одного-двух МОПов, а сложные команды — тремя и более МОПами. 3 основных дешифратора для дешифрации простых команды реализованы аппаратно, а один дешифратор для дешифрации сложных команд — с использованием микропрограмм.
