Главная страница
Навигация по странице:

  • 5.4.Конвейерная и суперскалярная обработка 5.4.1.Параллелизм на уровне выполнения команд, планирование загрузки конвейера и методика разворачивания циклов

  • 5.4.2.Параллелизм уровня команд: зависимости и конфликты по данным

  • 5.4.2.1.Основы планирования загрузки конвейера и разворачивание циклов

  • Компьютерные системы и сети Часть 1 (Архитектура ВС) Мельникова ЕВ, БГУИР 2009 (Мет пособие). Компьютерные системы и сети Часть 1 (Архитектура ВС) Мельникова. Учебнометодический комплекс по дисциплине компьютерные системы и сети для студентов специальности Т. 10 02 00 Программное обеспечение информационных технологий


    Скачать 0.76 Mb.
    НазваниеУчебнометодический комплекс по дисциплине компьютерные системы и сети для студентов специальности Т. 10 02 00 Программное обеспечение информационных технологий
    АнкорКомпьютерные системы и сети Часть 1 (Архитектура ВС) Мельникова ЕВ, БГУИР 2009 (Мет пособие).pdf
    Дата26.03.2018
    Размер0.76 Mb.
    Формат файлаpdf
    Имя файлаКомпьютерные системы и сети Часть 1 (Архитектура ВС) Мельникова .pdf
    ТипУчебно-методический комплекс
    #17225
    КатегорияИнформатика. Вычислительная техника
    страница11 из 14
    1   ...   6   7   8   9   10   11   12   13   14
    5.3.9.Поддержка точных прерываний
    Другая проблема, связанная с реализацией команд с большим временем выполнения, может быть проиллюстрирована с помощью следующей последовательности команд:
    DIVF F0,F2,F4
    ADDF F10,F10,F8
    SUBF F12,F12,F14
    Эта последовательность команд выглядит очень просто. В ней отсутствуют какие-либо зависимости. Однако она приводит к появлению новых проблем из- за того, что выданная раньше команда может завершиться после команды,
    выданной для выполнения позже. В данном примере можно ожидать, что команды ADDF и SUBF завершаться раньше, чем завершится команда DIVF.

    101
    Этот эффект является типичным для конвейеров команд с большим временем выполнения и называется внеочередным завершением команд (out-of-order completion). Тогда, например, если команда DIVF вызовет арифметическое прерывание после завершения команды ADDF, мы не сможем реализовать точное прерывание на уровне аппаратуры. В действительности, поскольку команда ADDF меняет значение одного из своих операндов, невозможно даже с помощью программных средств восстановить состояние, которое было перед выполнением команды DIVF.
    Имеются четыре возможных подхода для работы в условиях внеочередного завершения команд. Первый из них просто игнорирует проблему и предлагает механизмы неточного прерывания. Этот подход использовался в 60-х и 70-х годах и все еще применяется в некоторых суперкомпьютерах, в которых некоторые классы прерываний запрещены или обрабатываются аппаратурой без остановки конвейера. Такой подход трудно использовать в современных машинах при наличии концепции виртуальной памяти и стандарта на операции с плавающей точкой IEEE, которые требуют реализации точного прерывания путем комбинации аппаратных и программных средств. В некоторых машинах эта проблема решается путем введения двух режимов выполнения команд:
    быстрого, но с возможно не точными прерываниями, и медленного,
    гарантирующего реализацию точных прерываний.
    Второй подход заключается в буферизации результатов операции до момента завершения выполнения всех команд, предшествовавших данной. В некоторых машинах используется этот подход, но он становится все более дорогостоящим,
    если отличия во времени выполнения разных команд велики, поскольку становится большим количество результатов, которые необходимо буферизовать. Более того, результаты из этой буферизованной очереди необходимо пересылать для обеспечения продолжения выдачи новых команд.
    Это требует большого количества схем сравнения и многовходовых мультиплексоров. Имеются две вариации этого основного подхода. Первая называется буфером истории (history file), использовавшемся в машине CYBER
    180/990. Буфер истории отслеживает первоначальные значения регистров. Если возникает прерывание и состояние машины необходимо откатить назад до точки, предшествовавшей некоторым завершившимся вне очереди командам,
    то первоначальное значение регистров может быть восстановлено из этого буфера истории. Подобная методика использовалась также при реализации автоинкрементной и автодекрементной адресации в машинах типа VAX.
    Другой подход называется буфером будущего (future file). Этот буфер хранит новые значения регистров. Когда все предшествующие команды завершены,
    основной регистровый файл обновляется значениями из этого буфера. При прерывании основной регистровый файл хранит точные значения регистров,
    что упрощает организацию прерывания. В следующей главе будут рассмотрены некоторые расширения этой идеи.

    102
    Третий используемый метод заключается в том, чтобы разрешить в ряде случаев неточные прерывания, но при этом сохранить достаточно информации,
    чтобы подпрограмма обработки прерывания могла выполнить точную последовательность прерывания. Это предполагает наличие информации о находившихся в конвейере командах и их адресов. Тогда после обработки прерывания, программное обеспечение завершает выполнение всех команд,
    предшествовавших последней завершившейся команде, а затем последовательность может быть запущена заново. Рассмотрим следующий наихудший случай:
    Команда 1 - длинная команда, которая в конце концов вызывает прерывание
    Команда 2, ... , Команда n-1 - последовательность команд, выполнение которых не завершилось
    Команда n - команда, выполнение которой завершилось
    Имея значения адресов всех команд в конвейере и адрес возврата из прерывания, программное обеспечение может определить состояние команды 1
    и команды n. Поскольку команда n завершила выполнение, хотелось бы продолжить выполнение с команды n+1. После обработки прерывания программное обеспечение должно смоделировать выполнение команд с 1 по n-
    1. Тогда можно осуществить возврат из прерывания на команду n+1.
    Наибольшая неприятность такого подхода связана с усложнением подпрограммы обработки прерывания. Но для простых конвейеров, подобных рассмотренному нами, имеются и упрощения. Если команды с 2 по n все являются целочисленными, то мы просто знаем, что в случае завершения выполнения команды n, все команды с 2 по n-1 также завершили выполнение.
    Таким образом, необходимо обрабатывать только операцию с плавающей точкой. Чтобы сделать эту схему работающей, количество операций ПТ,
    выполняющихся с совмещением, может быть ограничено. Например, если допускается совмещение только двух операций, то только прерванная команда должна завершаться программными средствами. Это ограничение может снизить потенциальную пропускную способность, если конвейеры плавающей точки являются достаточно длинными или если имеется значительное количество функциональных устройств. Такой подход использовался в архитектуре SPARC, позволяющей совмещать выполнение целочисленных операций с операциями плавающей точки.
    Четвертый метод представляет собой гибридную схему, которая позволяет продолжать выдачу команд только если известно, что все команды,
    предшествовавшие выдаваемой, будут завершены без прерывания. Это гарантирует, что в случае возникновения прерывания ни одна следующая за ней команда не будет завершена, а все предшествующие будут завершены. Иногда это означает необходимость приостановки машины для поддержки точных прерываний. Чтобы эта схема работала, необходимо, чтобы функциональные устройства плавающей точки определяли возможность появления прерывания на самой ранней стадии выполнения команд так, чтобы предотвратить

    103
    завершение выполнения следующих команд. Такая схема используется,
    например, в микропроцессорах R2000/R3000 и R4000 компании MIPS.
    5.4.Конвейерная и суперскалярная обработка
    5.4.1.Параллелизм на уровне выполнения команд, планирование загрузки
    конвейера и методика разворачивания циклов
    В предыдущем разделе мы рассмотрели средства конвейеризации, которые обеспечивают совмещенный режим выполнения команд, когда они являются независимыми друг от друга. Это потенциальное совмещение выполнения команд называется параллелизмом на уровне команд. В данном разделе мы рассмотрим ряд методов развития идей конвейеризации, основанных на увеличении степени параллелизма, используемой при выполнении команд. Мы начнем с рассмотрения методов, позволяющих снизить влияние конфликтов по данным и по управлению, а затем вернемся к теме расширения возможностей процессора по использованию параллелизма, заложенного в программах.
    Для начала запишем выражение, определяющее среднее количество тактов для выполнения команды в конвейере:
    CPI конвейера = CPI идеального конвейера +
    + Приостановки из-за структурных конфликтов +
    + Приостановки из-за конфликтов типа RAW +
    + Приостановки из-за конфликтов типа WAR +
    + Приостановки из-за конфликтов типа WAW +
    + Приостановки из-за конфликтов по управлению
    CPI идеального конвейера есть не что иное, как максимальная пропускная способность, достижимая при реализации. Уменьшая каждое из слагаемых в правой части выражения, мы минимизируем общий CPI конвейера и таким образом увеличиваем пропускную способность команд.
    Прежде, чем начать рассмотрение этих методов, необходимо определить концепции, на которых эти методы построены.
    5.4.2.Параллелизм уровня команд: зависимости и конфликты по данным
    Параллелизм, заложенный в последовательности команд, называется параллелизмом уровня команд или ILP. Степень параллелизма, доступная внутри базового блока (линейной последовательности команд, переходы из вне которой разрешены только на ее вход, а переходы внутри которой разрешены только на ее выход) достаточно мала. Например, средняя частота переходов в целочисленных программах составляет около 16%. Это означает, что в среднем между двумя переходами выполняются примерно пять команд. Поскольку эти пять команд возможно взаимозависимые, то степень перекрытия, которую мы можем использовать внутри базового блока, возможно будет меньше чем пять.
    Чтобы получить существенное улучшение производительности, мы должны использовать параллелизм уровня команд одновременно для нескольких базовых блоков.

    104
    Метод
    Снижает
    Разворачивание циклов
    Приостановки по управлению
    Базовое планирование конвейера
    Приостановки RAW
    Динамической планирование с централизованной схемой управления
    Приостановки RAW
    Динамическое планирование с переименованием регистров
    Приостановки WAR и WAW
    Динамическое прогнозирование переходов
    Приостановки по управлению
    Выдача нескольких команд в одном такте
    Идеальный CPI
    Анализ зависимостей компилятором Идеальный CPI и приостановки по данным
    Программная конвейеризация и планирование трасс
    Идеальный CPI и приостановки по данным
    Выполнение по предположению
    Все приостановки по данным и управлению
    Динамическое устранение неоднозначности памяти
    Приостановки RAW, связанные с памятью
    Рис. 5.4.2.1.
    Самый простой и общий способ увеличения степени параллелизма, доступного на уровне команд, является использование параллелизма между итерациями цикла. Этот тип параллелизма часто называется параллелизмом уровня итеративного цикла. Ниже приведен простой пример цикла, выполняющего сложение двух 1000-элементных векторов, который является полностью параллельным:
    for (i = 1; i <= 1000; i = i + 1)
    x[i] = x[i] + y[i];
    Каждая итерация цикла может перекрываться с любой другой итерацией, хотя внутри каждой итерации цикла практическая возможность перекрытия небольшая.
    Имеется несколько методов для превращения такого параллелизма уровня цикла в параллелизм уровня команд. Эти методы основаны главным образом на разворачивании цикла либо статически, используя компилятор, либо динамически с помощью аппаратуры.
    Важным альтернативным методом использования параллелизма уровня команд является использование векторных команд. По существу векторная команда оперирует с последовательностью элементов данных. Например, приведенная выше последовательность на типичной векторной машине может быть выполнена с помощью четырех команд: двух команд загрузки векторов x и y из памяти, одной команды сложения двух векторов и одной команды записи

    105
    вектора-результата. Конечно, эти команды могут быть конвейеризованными и иметь относительно большие задержки выполнения, но эти задержки могут перекрываться. Векторные команды и векторные машины заслуживают отдельного рассмотрения, которое выходит за рамки данного курса. Хотя разработка идей векторной обработки предшествовала появлению большинства методов использования параллелизма, машины, использующие параллелизм уровня команд постепенно заменяют машины, базирующиеся на векторной обработке.
    5.4.2.1.Основы планирования загрузки конвейера и разворачивание
    циклов
    Для поддержания максимальной загрузки конвейера должен использоваться параллелизм уровня команд, основанный на выявлении последовательностей несвязанных команд, которые могут выполняться в конвейере с совмещением.
    Чтобы избежать приостановки конвейера зависимая команда должна быть отделена от исходной команды на расстояние в тактах, равное задержке конвейера для этой исходной команды. Способность компилятора выполнять подобное планирование зависит как от степени параллелизма уровня команд,
    доступного в программе, так и от задержки функциональных устройств в конвейере. Будем предполагать задержки, показанные на рисунке 5.4.2.1.1, если только явно не установлены другие задержки. Мы предполагаем, что условные переходы имеют задержку в один такт, так что команда следующая за командой перехода не может быть определена в течение одного такта после команды условного перехода. Мы предполагаем, что функциональные устройства полностью конвейеризованы или дублированы (столько раз, какова глубина конвейера), так что операция любого типа может выдаваться для выполнения в каждом такте и структурные конфликты отсутствуют.
    Рис. 5.4.2.1.1
    Компилятор может увеличить степень параллелизма уровня команд путем разворачивания циклов. Для иллюстрации этих методов используем простой цикл, который добавляет скалярную величину к вектору в памяти; это параллельный цикл, поскольку зависимость между итерациями цикла отсутствует. Предполлжим, что первоначально в регистре R1 находится адрес последнего элемента вектора (например, элемент с наибольшим адресом), а в регистре F2 - скалярная величина, которая должна добавляться к каждому
    Команда, вырабатывающая результат
    Команда, использующая результат
    Задержка в тактах
    Операция АЛУ с ПТ
    Другая операция АЛУ с ПТ 3
    Операция АЛУ с ПТ
    Запись двойного слова
    2
    Загрузка двойного слова
    Другая операция АЛУ с ПТ 1
    Загрузка двойного слова
    Запись двойного слова
    0

    106
    элементу вектора. Программа для машины, не рассчитанная на использование конвейера, будет выглядеть примерно так:
    Loop: LD F0,0(R1) ;F0=элемент вектора
    ADDD F4,F0,F2 ;добавляет скаляр из F2
    SD 0(R1),F4 ;запись результата
    SUBI R1,R1,#8 ;пересчитать указатель
    ;8 байт (в двойном слове)
    BNEZ R1, Loop ;переход R1!=нулю
    Для упрощения предположим, что массив начинается с ячейки 0. Если бы он находился в любом другом месте, цикл потребовал бы наличия одной дополнительной целочисленной команды для выполнения сравнения с регистром R1.
    Рассмотрим работу этого цикла при выполнении на простом конвейере с задержками, показанными на рисунке 5.4.2.1.1.
    Если не делать никакого планирования, работа цикла будет выглядеть следующим образом:
    Такт выдачи
    Loop: LD F0,0(R1) 1
    Приостановка
    2
    ADDD F4,F0,F2 3
    Приостановка
    4
    Приостановка
    5
    SD 0(R1),F4 6
    SUBI R1,R1,#8 7
    BNEZ R1,Loop
    8
    Приостановка
    9
    Для его выполнения потребуется 9 тактов на итерацию: одна приостановка для команды LD, две для команды ADDD, и одна для задержанного перехода. Мы можем спланировать цикл так, чтобы получить
    Время выполнения уменьшилось с 9 до 6 тактов.
    Заметим, что для планирования задержанного перехода компилятор должен определить, что он может поменять местами команды SUB1 и SD путем изменения адреса в команде записи SD: Адрес был равен 0(R1), а теперь равен
    8(R1). Это не тривиальная задача, поскольку большинство компиляторов будут
    Такт выдачи
    Loop: LD F0,0(R1)
    1
    Приостановка
    2
    ADDD F4,F0,F2 3
    SUBI R1,R1,#8 4
    BNEZ R1,Loop;задержанный переход 5
    SD 8(R1),F4 6

    107
    видеть, что команда SD зависит от SUB1, и откажутся от такой перестановки мест. Более изощренный компилятор смог бы рассчитать отношения и выполнить перестановку. Цепочка зависимостей от команды LD к команде
    ADDD и далее к команде SD определяет количество тактов, необходимое для данного цикла.
    В вышеприведенном примере мы завершаем одну итерацию цикла и выполняем запись одного элемента вектора каждые 6 тактов, но действительная работа по обработке элемента вектора отнимает только 3 из этих 6 тактов (загрузка,
    сложение и запись). Оставшиеся 3 такта составляют накладные расходы на выполнение цикла (команды SUB1, BNEZ и приостановка). Чтобы устранить эти три такта нам нужно иметь больше операций в цикле относительно числа команд, связанных с накладными расходами. Одним из наиболее простых методов увеличения числа команд по отношению к команде условного перехода и команд, связанных с накладными расходами, является разворачивание цикла. Такое разворачивание выполняется путем многократной репликации (повторения) тела цикла и коррекции соответствующего кода конца цикла.
    Разворачивание циклов может также использоваться для улучшения планирования. В этом случае, мы можем устранить приостановку, связанную с задержкой команды загрузки путем создания дополнительных независимых команд в теле цикла. Затем компилятор может планировать эти команды для помещения в слот задержки команды загрузки. Если при разворачивании цикла мы просто реплицируем команды, то результирующие зависимости по именам могут помешать нам эффективно спланировать цикл. Таким образом, для разных итераций хотелось бы использовать различные регистры, что увеличивает требуемое число регистров.
    Представим теперь этот цикл развернутым так, что имеется четыре копии тела цикла, предполагая, что R1 первоначально кратен 4. Устраним при этом любые очевидные излишние вычисления и не будем пользоваться повторно никакими регистрами.
    Ниже приведен результат, полученный путем слияния команд SUB1 и выбрасывания ненужных операций BNEZ, которые дублируются при разворачивании цикла.
    Loop: LD F0,0(R1)
    ADDD F4,F0,F2
    SD 0(R1),F4 ;выбрасывается SUB1 и BNEZ
    LD F6,-8(R1)
    ADDD F8,F6,F2
    SD -8(R1),F8 ;выбрасывается SUB1 и BNEZ
    LD F10,-16(R1)
    ADDD F12,F10,F2
    SD -16(R1),F12 ;выбрасывается SUB1 и BNEZ
    LD F14,-24(R1)

    108
    ADDD F16,F14,F2
    SD -24(R1),F16
    SUB1 R1,R1,#32
    BNEZ R1, Loop
    Мы ликвидировали три условных перехода и три операции декрементирования
    R1. Адреса команд загрузки и записи были скорректированы так, чтобы позволить слить команды SUB1 в одну команду по регистру R1. При отсутствии планирования за каждой командой здесь следует зависимая команда и это будет приводить к приостановкам конвейера. Этот цикл будет выполняться за 27 тактов (на каждую команду LD потребуется 2 такта, на каждую команду ADDD - 3, на условный переход - 2 и на все другие команды 1
    такт) или по 6.8 такта на каждый из четырех элементов. Хотя эта развернутая версия в такой редакции медленнее, чем оптимизированная версия исходного цикла, после оптимизации самого развернутого цикла ситуация изменится.
    Обычно разворачивание циклов выполняется на более ранних стадиях процесса компиляции, так что избыточные вычисления могут быть выявлены и устранены оптимизатором.
    В реальных программах мы обычно не знаем верхней границы цикла.
    Предположим, что она равна n и мы хотели бы развернуть цикл так, чтобы иметь k копий тела цикла. Вместо единственного развернутого цикла мы генерируем пару циклов. Первый из них выполняется (n mod k) раз и имеет тело первоначального цикла. Развернутая версия цикла окружается внешним циклом, который выполняется (n div k) раз.
    В вышеприведенном примере разворачивание цикла увеличивает производительность этого цикла путем устранения команд, связанных с накладными расходами цикла, хотя оно заметно увеличивает размер программного кода. Насколько увеличится производительность, если цикл будет оптимизироваться?
    Ниже представлен развернутый цикл из предыдущего примера после оптимизации.
    Loop: LD F0,0(R1)
    LD F6,-8(R1)
    LD F10,-16(R1)
    LD F14,-24(R1)
    ADDD F4,F0,F2
    ADDD F8,F6,F2
    ADDD F12,F10,F2
    ADDD F16,F14,F2
    SD 0(R1),F4
    SD -8(R1),F8
    SD -16(R1),F12
    SUB1 R1,R1,#32
    BNEZ R1, Loop

    109
    SD 8(R1),F16 ; 8 - 32 = -24
    Время выполнения развернутого цикла снизилось до 14 тактов или до 3.5
    тактов на элемент, по сравнению с 6.8 тактов на элемент до оптимизации, и по сравнению с 6 тактами при оптимизации без разворачивания цикла.
    Выигрыш от оптимизации развернутого цикла даже больше, чем от оптимизации первоначального цикла. Это произошло потому, что разворачивание цикла выявило больше вычислений, которые могут быть оптимизированы для минимизации приостановок конвейера; приведенный выше программный код выполняется без приостановок. При подобной оптимизации цикла необходимо осознавать, что команды загрузки и записи являются независимыми и могут чередоваться. Анализ зависимостей по данным позволяет нам определить, являются ли команды загрузки и записи независимыми.
    Разворачивание циклов представляет собой простой, но полезный метод увеличения размера линейного кодового фрагмента, который может эффективно оптимизироваться. Это преобразование полезно на множестве машин от простых конвейеров, подобных рассмотренному ранее, до суперскалярных конвейеров, которые обеспечивают выдачу для выполнения более одной команды в такте. В следующем разделе рассмотрены методы,
    которые используются аппаратными средствами для динамического планирования загрузки конвейера и сокращения приостановок из-за конфликтов типа RAW, аналогичные рассмотренным выше методам компиляции.
    1   ...   6   7   8   9   10   11   12   13   14


    написать администратору сайта