Многопроцессорные и многоядерные системы: когерентность кэш‑памяти (протоколы MESI, MOESI)
Введение
В многопроцессорных и многоядерных системах каждое ядро имеет собственную кэш‑память для ускорения доступа к данным. Однако это создаёт проблему когерентности: если несколько ядер одновременно читают и пишут одни и те же данные, их кэши могут содержать несогласованные (рассогласованные) копии.
Когерентность кэша (cache coherence) — гарантия того, что:
- Все ядра видят одинаковые значения для одного адреса памяти.
- Операции записи одного ядра становятся видимыми другим ядрам в корректном порядке.
- Нет «старых» или «потерянных» значений из‑за задержек в обновлении кэшей.
В статье рассмотрены:
- причины возникновения проблем когерентности;
- требования к когерентной системе;
- протоколы MESI и MOESI;
- механизмы реализации (snooping, directory);
- примеры работы;
- влияние на производительность.
1. Причины проблем когерентности
1.1. Сценарии несогласованности
- Ядро 1 читает значение
A = 42из памяти и помещает в свой кэш. - Ядро 2 тоже читает
A = 42и сохраняет в свой кэш. - Ядро 1 записывает
A = 100. Теперь в кэше Ядра 1 —100, в кэше Ядра 2 —42, в памяти — ещё42(если запись отложена). - Ядро 2 читает
Aи получает устаревшее значение42.
1.2. Источники конфликтов
- Параллельные записи в один адрес.
- Отложенные записи (write‑back кэш).
- Реплицированные данные в нескольких кэшах.
- Задержки распространения сигналов между ядрами и памятью.
2. Требования к когерентной системе
- Однородность записи (Write Propagation)
- Результат записи должен стать видимым всем ядрам.
- Транзакционность записи (Write Serialization)
- Все ядра наблюдают записи в одном и том же порядке.
- Согласованность с памятью (Memory Consistency)
- Порядок видимости операций соответствует модели согласованности (например, sequential consistency).
3. Протоколы когерентности: общие принципы
3.1. Основные механизмы
- Отслеживание (snooping) — каждое ядро «слушает» шины/интерконнекта и реагирует на обращения к своим кэшированным данным.
- Директории (directory‑based) — централизованная таблица, хранящая, какие ядра кэшируют данные.
- Сообщения (messages) — передача команд между кэшами (Invalid, Shared, Exclusive и т. п.).
3.2. Состояния кэш‑строки
Каждая кэш‑строка имеет состояние (state), определяющее её доступность и актуальность. Протоколы используют разные наборы состояний.
4. Протокол MESI
MESI — один из самых распространённых протоколов (используется в x86, ARM). Название отражает 4 состояния:
- M (Modified) — строка изменена (dirty), только в этом кэше; данные в памяти устарели.
- E (Exclusive) — строка свежая, только в этом кэше; можно записывать без оповещений.
- S (Shared) — строка может быть в других кэшах; чтение разрешено, запись требует согласования.
- I (Invalid) — строка не действительна (не содержит актуальных данных).
4.1. Переходы между состояниями
Чтение (Read):
- Если строка в состоянии
I— запрос на шину; если кто‑то имеетM/E/S— получаем данные и переходим вS. - Если строка
EилиS— читаем локально. - Если строка
M— передаём данные и переходим вS(если другие тоже читают).
Запись (Write):
- Если строка
I— отправляем запрос на исключительное владение; получаем данные, переходим вE, затем вMпосле записи. - Если строка
S— отправляем Invalid‑сообщение другим, переходим вM. - Если строка
E— пишем локально, переходим вM. - Если строка
M— пишем локально (уже модифицирована).
Ответ на запрос другого ядра:
- При запросе на чтение: если
M— передаём данные и переходим вS; еслиE/S— передаём данные. - При запросе на запись: если
M/E/S— переходим вI(Invalid).
4.2. Пример работы MESI
Пусть два ядра (C1, C2) кэшируют строку A:
- C1 читает
A→ C1:E. - C2 читает
A→ C1 отправляет данные, C1:S, C2:S. - C1 пишет
A = 100→ C1 посылает Invalid‑сообщение C2; C1:M, C2:I. - C2 снова читает
A→ C2 запрашивает данные; C1 отправляет100, C1:S, C2:S.
5. Протокол MOESI
MOESI расширяет MESI состоянием O (Owned), чтобы уменьшить трафик при чтении модифицированных данных.
Состояния:
- M — как в MESI.
- O (Owned) — строка модифицирована и может быть в других кэшах (но только это ядро «владеет» правом записи).
- E — как в MESI.
- S — как в MESI.
- I — как в MESI.
5.1. Преимущества MOESI
- При чтении строки в состоянии
MилиOядро‑владелец может передавать данные напрямую, не сбрасывая их в память. - Снижается число обращений к основной памяти (меньше задержек).
- Особенно полезно для многоядерных систем с общей шиной/интерконнектом.
5.2. Переходы в MOESI
Чтение при M:
- Вместо перехода в
Sи сброса в память, ядро переходит вOи передаёт данные другому кэшу.
Запись при O:
- Ядро может писать локально (оно «владеет» строкой).
Запрос на запись от другого ядра:
- Если текущее состояние
O— ядро сбрасывает данные в память (если нужно) и переходит вI.
5.3. Пример работы MOESI
- C1 пишет
A = 100→ C1:M. - C2 читает
A→ C1 передаёт данные и переходит вO, C2:S. - C1 снова пишет
A = 200→ C1 пишет локально, остаётся вO. - C3 читает
A→ C1 передаёт200, остаётся вO, C3:S. - C1 записывает
A = 300→ локальная запись,O.
Без MOESI на шагах 2 и 4 C1 пришлось бы сбрасывать данные в память и переходить в S, увеличивая трафик.
6. Механизмы реализации
6.1. Snooping (наблюдение)
- Все кэши подключены к общей шине/интерконнекту.
- Каждое ядро «слушает» транзакции и проверяет, есть ли у него копия запрашиваемого адреса.
- При конфликте — выполняет переход состояния согласно протоколу.
- Плюсы: простота, низкая задержка для малого числа ядер.
- Минусы: масштабируемость (трафик растёт с числом ядер).
6.2. Directory‑based (директивный)
- Централизованная или распределённая директория хранит, какие ядра кэшируют каждую строку.
- Запросы направляются только заинтересованным ядрам.
- Плюсы: масштабируемость до десятков/сотен ядер.
- Минусы: сложность, дополнительные задержки на запросы к директории.
7. Оптимизации протоколов
- Write‑invalidate vs Write‑update
- Write‑invalidate — отправка Invalid‑сообщений (MESI/MOESI).
- Write‑update — рассылка нового значения всем кэшам (реже используется).
- Отложенная обработка (deferred actions) — группировка сообщений.



