Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Суперкомпьютеры и системы. Мультипроцессоры. Учебное пособие

.pdf
Скачиваний:
1
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
пеней свободы» в реализации механизмов общей памяти. Пусть про­цессор 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, Inva­lid). Последние две наиболее многофункциональны и эффективны в мультипроцессорах. 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