Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Суперкомпьютеры и системы. Мультипроцессоры. Учебное пособие
.pdf
пеней свободы» в реализации механизмов общей памяти. Пусть процессор P
временно с тремя операциями записи процессора P
выполняет три операции записи значений {y0, y1, y2} одно-
2
. Другие процессо-
1
ры, которые заняты считыванием слов из памяти, могут «увидеть» разные последовательности из шести значений. Например, такую: x
, y1, x2, y2 или такую: y0, x0, y1, y2, x0, x1 и т. п. При процессорной со-
y
0
, x1,
0
гласованности не гарантируется, что каждый процессор видит один и
тот же порядок состояния памяти (в отличие от последовательной
согласованности). Вполне может быть так, что одни процессоры воспринимают порядок x
, x1, y0, y1, x2, y2, другие – y0, x0, y1, y2, x0, x1, тре-
0
тьи еще какой-нибудь вариант. Единственное, что гарантируется абсолютно точно: ни один процессор не увидит последовательность, в которой сначала читается значение x
, а затем – x0. Порядок, в котором
1
выполняются обращения к памяти для записи из одного и того же процессора, остается одинаковым для всех «читающих» процессоров.
Слабая согласованность предусматривает явную синхронизацию
содержимого памяти для разных процессоров с помощью переменных
или команд синхронизации, активизируемых с фиксированным интервалом времени разными процессорами и согласованных по последовательности. По очередному сигналу или команде синхронизации завершаются все текущие операции обмена, и только после этого активизируются новые обращения к памяти. Очевидно, этапы синхронизации
вносят определенную взаимную упорядоченность операций с памятью,
но вместе с тем задерживают выполнение программы.
В модели слабой согласованности не гарантируется, что операции
записи, выполненные одним процессором, будут восприниматься другими в той же последовательности в любой момент времени. Один
процессор может увидеть сначала значение x
сначала x
, потом x0. Слабая согласованность предусматривает явную
1
, а потом x1; другой –
0
синхронизацию содержимого памяти для разных процессоров с помощью операций синхронизации, активизируемых с фиксированным интервалом времени разными процессорами. В промежутках между операциями синхронизации состояние памяти никак не согласовано, т. е.
результаты операций записи в память могут быть прочитаны разными
процессорами совершенно в произвольном порядке. Но в момент синхронизации все незаконченные операции записи завершаются, и ни
одна новая операция не может начаться, пока не будут завершены все
предыдущие записи и не завершится сама синхронизация. Синхрони-
21

зация приводит память в строго согласованное состояние, когда не
остается никаких незавершенных операций. Операции синхронизации
являются последовательно согласованными, т. е. если они одновременно инициируются несколькими процессорами, то аппаратно определяется определенный порядок их выполнения, причем все процессоры воспринимают один и тот же порядок. Таким образом, с помощью
операций синхронизации периодически вносится определенный порядок в последовательность событий модификации значений переменных, хотя это и занимает некоторое время, поскольку требует вытеснения всех модифицированных значений из всех кэшей в основное хранилище.
Слабая согласованность является не очень эффективной моделью,
поскольку требует завершения всех операций с памятью и задерживает
выполнение новых операций до тех пор, пока не завершены все предыдущие, даже если в этом нет необходимости. В модели свободной согласованности операция синхронизации вызывается программно и разделяется на две разные операции. Чтобы считать или записать совместно используемую переменную, процессор (то есть его программное
обеспечение) сначала должен выполнить операцию захвата (команду
acquire) переменной синхронизации, что позволит ему получить монопольный доступ к общим данным – войти в критическую область. Далее
процессор может делать с этими данными все, что ему требуется (считывать или записывать), а по завершении он должен выполнить операцию освобождения (команду release) переменной синхронизации, чтобы
показать остальным процессорам, что он завершил работу. Операция
release не требует завершения всех незаконченных записей в основную
память, но сама она не может завершиться, пока не закончатся все операции записи, начатые этим процессором после захвата переменной
синхронизации.
Когда начинается следующая операция acquire, производится проверка, все ли предыдущие операции release завершены. Если нет, то
операция acquire задерживается до тех пор, пока это не будет сделано
(а перед тем как завершатся все операции release, должны быть завершены все операции записи). Таким образом, если следующая операция
acquire выполняется через достаточно длительный промежуток времени после последней операции release, ей обычно не нужно ждать, и она
может войти в свою критическую область без задержки. Если операция
acquire выполняется через небольшой промежуток времени после опе-
22

рации release, то она (и все команды, которые должны выполняться следом) ожидает завершения всех критических областей. Это гарантирует,
что все переменные, измененные в критической области, будут обновлены в основной памяти. Такая модель немного сложнее, чем модель
слабой согласованности, но она имеет существенное преимущество,
поскольку не нужно задерживать выполнение команд слишком часто.
Проблема согласованности состояния памяти является общей для
всех вариантов архитектур многопроцессорных (в том числе многоядерных) вычислительных систем с общей памятью и одним или несколькими уровнями кэш-памяти. Так как большинство мультикомпьютеров
содержит многоядерные / многопроцессорные системы в качестве своих
вычислительных узлов, эта проблема может считаться всеобщей.
3.1. UMA-СИСТЕМЫ
В UMA-системах для каждого процессора аппаратно обеспечивается один и тот же механизм доступа к любому элементу всей оперативной памяти. Другими словами, каждый элемент может быть выбран из
памяти (или записан в память) с той же скоростью, что и любой другой
ее элемент. Единая память может быть построена и как одноблочная, и
в виде совокупности из нескольких модулей (банков). Обычно реализуется второй вариант, обеспечивающий более высокую производительность за счет одновременной работы разных модулей.
Архитектура UMA – наиболее распространенная архитектура памяти параллельных систем с общедоступной памятью [3, 4]. Технически UMА-системы (как SMP – симметричные мультипроцессоры, так и
PVP – параллельные векторные процессоры) содержат узел, обеспечивающий прямое взаимодействие каждого процессора (П
) с каждым
i
модулем памяти (рис. 3.2).
Существует несколько способов реализации узла сопряжения процессоров и памяти, различающихся сложностью (соответственно и
стоимостью) и масштабируемостью (влиянием количества процессоров на производительность):
общая шина;
перекрестный коммутатор;
многоступенчатый коммутатор.
Простейший путь реализации UMA-системы – соединение нескольких процессоров с памятью посредством общей шины (рис. 3.3, а).
23

П1
Узел сопряжения процессоров с модулями памяти
П2 Пk
Модуль
памяти 1
Модуль
памяти 2
Рис. 3.2. Структура UMA-системы
Модуль
памяти n
В этом случае в каждый момент времени обмен данными с памятью, очевидно, реализующей модель строгой согласованности, может
вести только один из процессоров.
Поскольку процессоры обычно работают намного быстрее модулей
памяти, общая шина является узким местом и сильно тормозит работу
всей системы. Максимальное количество процессоров, способных работать без простоев в этой структуре, не превышает 3-4. Производительность такой системы может быть существенно увеличена путем введения локальной кэш-памяти для каждого процессора (рис. 3.3, б). Однако
появление кэшей приводит к тому, что общая память системы уже не
может быть строго согласованной. Возникает проблема синхронизации
состояния локальных кэшей и основной памяти.
П1
Модуль
памяти 1
…
…
Модуль
П2
памяти n
Рис 3.3. UMA-системы с общей шиной:
а – процессоры без кэшей; б – процессоры с кэшами
Пk
П1
…
Кэш Кэш Кэш
Модуль
памяти 1
…
памяти n
Пk П2
Модуль
Для решения этой проблемы было предложено много разных алгоритмов [3, 4]. Все эти алгоритмы, называемые протоколами согласования состояния кэшей, в некоторых деталях различаются, но не допускают одновременного появления разных версий одной и той же строки
24

в двух или более кэшах. Строкой кэша называется совокупность адреса
основной памяти, обычно называемого тэгом, значений нескольких
байт данных, начиная с этого адреса, и нескольких управляющих бит,
используемых устройством управления – контроллером. Строка является единицей передачи и хранения для кэша. Обычно размер строки
кэша составляет 32 или 64 байта.
Во всех таких решениях контроллер кэш-памяти разрабатывается
так, чтобы кэш мог обеспечивать мониторинг запросов к основной памяти, идущих по шине от других процессоров и других кэшей, и в
каждом конкретном случае предпринимать те или иные действия. Такие кэши получили название следящих (snooping cache), поскольку
кэш как бы «следит» за шиной. Набор правил, которым следует кэш,
процессоры и основная память, чтобы предотвратить появление различных версий данных в нескольких кэшах, и называют протоколом
согласования кэшей.
Самый простой протокол согласования кэшей называется сквозной
записью (write through). При его использовании контроллер каждого
кэша отслеживает обращения к основной памяти по записи от других
кэшей и проверяет наличие записываемого элемента у себя путем сравнения адреса на общей шине с тэгами всех своих строк. В случае кэшпромаха (ни один собственный тэг не совпал с адресом записываемого
элемента) контроллер не делает ничего. В случае кэш-попадания строка
с совпавшим тэгом помечается как недействительная путем установки
(или сброса) специального управляющего бита. Любое последующее
обращение своего процессора к элементу недействительной строки приведет к его выборке из основной памяти.
Существуют некоторые вариации этого простого протокола. Например, в случае кэш-попадания записи следящий кэш может считать с общей шины новое значение и обновить элемент данных строки вместо
объявления его недействительным. Во всех протоколах кэширования
нужно делать выбор между стратегией обновления и стратегией объявления данных недействительными. Затраты на обновление требуют
большей работы, чем на установку бита недействительности данных, но
зато они могут предотвратить последующие кэш-промахи.
Как и большинство простых решений, сквозная запись не очень эффективна. Каждая операция записи в кэш требует передачи данных в
основную память по шине, и при большом количестве процессоров шина опять становится узким местом. Поэтому были разработаны другие
протоколы. Все они имеют одно общее свойство: не каждая операция
25

записи в кэш ведет к немедленной записи в основную память. Вместо
этого при изменении строки в ней устанавливается особый управляющий бит, который указывает, что строка в кэше правильная, а в памяти –
неверная. Хотя в конечном итоге данную строку все равно придется записать в память. Вполне вероятно, что это произойдет после выполнения еще нескольких операций записи в этом же кэше. Протоколы такого
типа называются отложенной записью.
Одним из наиболее популярных протоколов отложенной записи [12]
является протокол MESI, названный так по первым буквам четырех
возможных состояний элементов кэша (Modified, Exclusive, Shared,
Invalid – модифицированный, эксклюзивный, разделяемый, недействительный). В его основе лежит более ранний протокол однократной записи. В соответствии с этим протоколом каждый элемент кэша может
находиться в одном из следующих четырех состояний:
недействительный: элемент кэша не содержит актуальных данных;
разделяемый: копии элемента могут храниться в нескольких
кэшах, основная память содержит то же самое актуальное значение;
эксклюзивный: элемент находится только в этом кэше (ни в ка-
ких других его нет), основная память содержит то же самое значение;
модифицированный: элемент в данном кэше содержит актуаль-
ное значение, в основной памяти значение не обновлено (не актуально), копий элемента в других кэшах не существует.
При загрузке каждого процессора все элементы его кэша помечаются как недействительные. При первом считывании из основной памяти нужная строка вызывается в кэш данного процессора (назовем
его кэш 1) и помечается как эксклюзивная, поскольку это единственная
кэшированная копия (рис. 3.4, переход из состояния I в состояние E по
событию «Чтение(pvt)»). При последующих считываниях процессор
использует эту строку, не обращаясь к шине.
Другой процессор может прочитать из памяти тот же элемент и
поместить его в свой кэш. В этом случае первый держатель строки
(контроллер кэша 1) благодаря слежению за шиной (событие «Чтение») узнает, что он уже не единственный держатель строки, и объявляет по шине, что у него есть копия. Обе копии помечаются как разделяемые. Другими словами, состояние «разделяемая» означает, что память содержит актуальное значение и его копии находятся в одном или
нескольких кэшах. При последующих чтениях любой разделяемой
строки своего кэша каждый процессор не использует шину и не меняет
состояние строки.
26

Рис. 3.4. Диаграмма состояний строки кэша, протокол MESI
Полная диаграмма состояний и переходов любой строки кэша, реализуемая контроллером согласно протоколу MESI и показанная на рис. 3.4,
обеспечивает реакцию на следующие события:
‒ чтение элемента данных;
‒ запись элемента данных;
и точки их возникновения:
‒ основная память;
‒ локальный кэш;
‒ общая шина (кэш другого процессора).
Протокол MESI несовершенен, но довольно прост, он использует
всего два управляющих бита для кодирования любого из четырех состояний строки кэша. Существуют несколько разновидностей протокола MESI, таких как MSI (Modified, Shared, Invalid), MOSI (Modified,
Owned, Shared, Invalid), MOESI (Modified, Owned, Exclusive, Shared,
Invalid) и MOWESI (Modified, Owned, Written, Exclusive, Shared, Invalid). Последние две наиболее многофункциональны и эффективны в
мультипроцессорах. MOESI требует наличия уже трех управляющих
бит для реализации состояний, а его основным предназначением была
ликвидация основного недостатка MESI, связанного с невозможностью
хранения измененных строк более чем в одном кэше.
27

Для этого и было введено новое состояние O (Owned), которое
означает владение правами собственности на данную строку кэша, ее
клоны в других кэшах и в оперативной памяти. Эти права изначально
принадлежат системной логике, на которой лежит обязанность по
обеспечению всех агентов общей шины (подразумеваются не только
процессоры, но и устройства типа bus master) верной информацией.
Введение состояния M значительно усложнило жизнь системной логи-
ке. При каждом запросе любым агентом информации из кэшируемого
пространства оперативной памяти системная логика была вынуждена
приостанавливать его выполнение, затем генерировать запрос всем
контроллерам на наличие соответствующей грязной (помеченной
как M) строки в их кэшах. Если у одного из них имеется такая строка,
то он был обязан записать ее в оперативную память и изменить состояние на E. По окончании этой операции системная логика перезапускала
изначальный запрос и доставляла нужную информацию. Если бы поддержка состояния M отсутствовала, тогда кэши всех процессоров всегда
были бы согласованы с оперативной памятью, а системной логике было
бы незачем проводить какие-либо дополнительные мероприятия, так как
она могла бы просто доставить запрашиваемую информацию непосредственно из оперативной памяти. Однако это означало бы возврат к протоколу обратной записи со всеми его недостатками.
Введение состояния O позволило передать все права и обязанности
относительно строки кэша от системной логики контроллеру. Если какой-либо агент общей шины запрашивает этот элемент, то контроллерхозяин обязан ответить на запрос и предоставить свою копию значения
элемента. Даже если системная логика активно участвует в этой операции, оперативная память не обновляется. Фактически, если состояние M можно рассматривать как Exclusive Modified, то Owned – не что
иное, как Shared Modified. Если какая-либо строка становится O, то ее
копии во всех других кэшах должны иметь состояние S.
Однако протокол MOESI также оказался неидеальным. Состояние O дает все преимущества контроллеру-хозяину, но ставит все
остальные процессоры в ситуацию, в которой они могут только читать
данные из строки. Если же кому-нибудь из них необходимо произвести
запись в строку своего кэша с состоянием S, то приходится вначале
записывать ее и в основную память, что приводит к ликвидации состояния O у предыдущего хозяина. Это не составляет проблемы для таких
многопроцессорных систем, в которых один процессор активно пишет
данные в некоторую строку, а несколько других процессоров их в основном читают. Однако в большинстве случаев при реализации парал-
28

лельных вычислений разные процессоры или процессорные ядра могут
одновременно выполнять различные потоки одной и той же параллельной программы. В этом случае интенсивность обращений по записи новых значений в строку обычно становится примерно одинаковой
и производительность падает. Для улучшения характеристик было
введено состояние W (written), которое привело к созданию протокола
MOWESI. При записи в строку с состоянием S оно изменяется на W,
остальные контроллеры оповещаются об этом факте и становятся обязаны ликвидировать или обновить свои устаревшие копии, после чего
состояние строки изменяется на M или O в зависимости от того, кто
выполнил запись. Обновления оперативной памяти удается избежать в
обоих случаях.
Альтернативным по отношению к общей шине способом построения многопроцессорной системы с общей памятью на основе UMA
является соединение процессоров с модулями памяти с помощью специализированных коммутаторов. Преимущество такого подхода состоит в том, что коммутатор может параллельно обслуживать несколько запросов разных процессоров на обращение к нескольким блокам
памяти. Коммутатор может быть перекрестным (рис. 3.5) или каскадным (рис. 3.6).
В любом случае это сложное и довольно дорогое устройство, поскольку оно имеет большое количество входных (для процессоров) и
выходных (для модулей памяти) совокупностей параллельных шин
адреса / данных. На рис. 3.5, а показано такое состояние коммутатора,
при котором процессор с двоичным номером 001 подключен к модулю
памяти с номером 000, процессор 101 подключен к модулю 101 и процессор 110 – к модулю 010. На рис 3.5, б показано состояние несоединенного (открытого) узла коммутатора, на рис. 3.5, в – соединенного
(закрытого для других подключений).
Перекрестные коммутаторы обеспечивают принципиальную возможность соединения каждого процессора с требуемым ему в данный
момент модулем памяти. Соперничество между процессорами может
возникнуть при попытке одновременного доступа к одному и тому же
модулю памяти. Как и при использовании общей шины, в этом случае
доступ получает только один процессор, а все прочие – блокируются.
Перекрестные коммутаторы считаются слишком дорогими. Действительно, при N процессорах и M модулях памяти в состав коммутатора должно входить N ∙ M узлов, что становится неприемлемым, существенно превышающим суммарную стоимость процессоров и памяти, уже при значениях N и M порядка 8–10.
29

б
в
а
Рис. 3.5. UMA-система на основе перекрестного коммутатора
Поэтому для SMP-систем были реализованы различные варианты
каскадных коммутаторов (рис. 3.6), реализующих более сложную, но и
более дешевую логику соединения процессоров с модулями памяти.
П1
П2
…
Пk
Каскад
комму-
тации 1
Каскад
комму-
тации 2
Каскад
комму-
тации 3
Модуль
памяти 1
…
Модуль
памяти n
Рис. 3.6. UMA-система на основе каскадного коммутатора
Все каскадные коммутаторы реализуют требуемые соединения с
задержкой, пропорциональной количеству простых коммутаторов, че-
30
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
