Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Глава 34.Многоядерные процессоры для ПК и серверов.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
1.37 Mб
Скачать

34.3. Когерентность данных Суть проблемы когерентности данных

 Во всех ядрах имеются 1 или 2 уровня кэш-памяти (L1 и L2, причем уровень L1 разделен на кэш-память команд L1K и кэш-память данных L1D). В высокопроизводительных многоядерных процессорах имеется и 3-й уровень кэш-памяти – L3. А в уникальных высокопроизводительных процессорах имеется еще и 4-ый уровень кэш-памяти L4.

Наличие кэш-памяти необходимо для достижения хорошей производительности, поскольку оперативная память работает слишком медленно по сравнению со скоростью ядер, причем каждый год это соотношение ухудшается. Уже сейчас скорость оперативной памяти не менее чем в 100 раз меньше, чем требуется. Кэш-память первого уровня работает со скоростью близкой к скорости ядра, но эта аппаратура дорогая, и поэтому этот уровень кэш-памяти имеет относительно небольшую емкость.

В настоящее время общий объем кэш-памяти всех уровней составляет от нескольких Мбайт до нескольких десятков Мбайт, в то время как оперативная память может иметь объем в десятки и сотни Гбайт. Для ядра кэш-память исполняет роль "рабочего стола", на котором хранится используемая в текущее время информация, а оперативная память подобна большому шкафу, находящемуся в дальнем углу комнаты.

В кэш-памяти ядра могут быть как общие (разделяемые), так и частные данные. Частные данные - это данные, которые используются потоком в одном ядре. Общие данные используются многими потоками, которые могут выполняться как ядрами в одном процессоре, так и ядрами в других процессорах, если их в системе несколько.

Когда кэшируется элементы частных данных, их значение переносится в кэш-память ядра, которое выполняет поток. Поскольку никакое другое ядро не использует эти данные, этот процесс идентичен процессу для одноядерной системы с кэш-памятью.

Если в кэш-памяти используется дисциплина обратной записи (Write back), когда в некоторый момент времени запись выполняется только в кэш-память, то могут возникать ситуации, когда содержимое кэш-памяти с некоторой переменной А может не совпадать с содержимым этой переменной в оперативной памяти. В этом случае говорят, что возникает некогерентность (несогласованность) данных между кэш-памятью и оперативной памятью.

Если в кэш-памяти используется дисциплина сквозной записи, то запись выполняется и в кэш-память, и в оперативную память. В этом случае говорят, что данные в кэш-памяти и в оперативной памяти когерентны (согласованы).

В современных ядрах, как правило, используется дисциплина обратной записи (Write back). В одноядерных системах, однако, некогерентность данных влияния не оказывает. При вытеснении строки из кэш-памяти она записывается в оперативную память.

В многоядерных системах, если потоки работают только с частными данными, то некогерентность данных тоже влияния не оказывает. Если же несколько потоков работают с общими данными, то общие данные размножаются и могут содержаться в кэш-памятях нескольких ядер. Если не предпринять специальных мер, то возникает ситуации с некогерентными данными.

Неформально, проблема когерентности данных состоит в необходимости гарантировать, что любое чтение элемента данных возвращает последнее по времени записанное значение. Это определение не совсем корректно, поскольку невозможно требовать, чтобы операция чтения мгновенно видела значение, записанное в этот элемент данных некоторым другим ядром. Если, например, операция записи в одном ядре предшествует операции чтения той же ячейки в другом ядре в пределах очень короткого интервала времени, то невозможно гарантировать, что чтение вернет записанное значение данных, поскольку в этот момент времени записываемые данные могут даже не покинуть ядро. Вопрос о том, когда точно записываемое значение должно быть доступно ядру, выполняющему чтение, определяется выбранной моделью согласованного (непротиворечивого) состояния оперативной памяти и связан с реализацией синхронизации параллельных вычислений. Поэтому, с целью упрощения, предположим, что требуется только, чтобы записанное операцией записи значение было доступно операции чтения, которая выполняется немного позже записи, и чтобы операции записи данного ядра всегда видны в порядке их выполнения.

С этим простым определением согласованного состояния данных можно гарантировать когерентность путем обеспечения двух свойств:

операция чтения переменной одним ядром, которая следует за операцией записи в ту же переменную другим ядром, получит записанное значение, если операции чтения и записи достаточно отделены друг от друга по времени;

операции записи одной и той же переменной выполняются строго последовательно. Это означает, что две подряд идущие операции записи в одну и ту же ячейку оперативной памяти будут наблюдаться другими ядрами именно в том порядке, в котором они появляются в программе ядра, выполняющего эти операции записи.

Необходимость строго последовательного выполнения операций записи является более тонким, но также очень важным свойством. Представим себе, что строго последовательное выполнение операций записи не соблюдается. Тогда ядро 1 может записать данные в ячейку оперативной памяти, а затем в эту ячейку оперативной памяти выполнит запись ядро 2. Строго последовательное выполнение операций записи гарантирует два важных следствия для этой последовательности операций записи. Во-первых, оно гарантирует, что каждое ядро в системе в некоторый момент времени будет наблюдать запись, выполняемую ядром 2. Если последовательность операций записи не соблюдается, то может возникнуть ситуация, когда какое-нибудь ядро будет наблюдать сначала операцию записи ядра 2, а затем операцию записи ядра 1, и будет хранить это записанное ядром 1 значение неограниченно долго.

Более тонкая проблема возникает с поддержанием разумной модели порядка выполнения программ и когерентности данных для пользователя. Представим, что третье ядро постоянно читает ту же переменную, в которую записывают ядра 1 и 2. Он должен наблюдать сначала значение, записанное ядром 1, а затем значение, записанное ядром 2. Возможно, он никогда не сможет увидеть значения, записанного ядром 1, поскольку запись от ядра 2 возникла раньше чтения. Если он даже видит значение, записанное ядром 1, он должен видеть значение, записанное ядром 2, при последующем чтении. Подобным образом любое другое ядро, которое может наблюдать за значениями, записываемыми как ядром 1, так и ядром 2, должно наблюдать идентичное поведение. Простейший способ добиться таких свойств заключается в строгом соблюдении порядка операций записи, чтобы все записи в одну и ту же ячейку оперативной памяти могли наблюдаться в одном порядке. Это свойство называется последовательным выполнением операций записи (write serialization). Вопрос о том, когда ядро должно увидеть значение, записанное другим ядром, достаточно сложен и имеет заметное воздействие на производительность, особенно в больших системах.

Выполняемые в многоядерных системах программы оожидают видеть только одну общую оперативную память. Из этого следует, что если ядро A сохраняет значение X в ячейке Q, а позже ядро B загружает значение из ячейки Q, то ядро B должно получить X. Но если на самом деле значение X было помещено в кэш-память ядра A, то, как его сможет получить ядро B?

Если ядро B обращается к ячейке Q, и содержимое этой ячейки отсутствует в кэш-памяти ядра B, то через коммуникационную подсистему посылается широковещательный запрос "У кого Q?". Все устройства кэш-памяти, и даже подсистема ввода-вывода (если она работает с оперативной памятью напрямую) определяют, не у них ли находится актуальная копия Q. То устройство, которое находит актуальную копию, посылает ее ядру B.