Открыть сервис

Кэш-когерентность

Кэш-когерентность (англ. cache coherence) — это свойство многопроцессорной системы, обеспечивающее согласованность данных, хранящихся в кэш-памяти каждого процессора, с данными в общей оперативной памяти. В системах с несколькими процессорами (или ядрами), каждый из которых имеет собственную кэш-память, возникает проблема: один процессор может изменить значение переменной в своём кэше, в то время как другой процессор продолжает использовать устаревшее значение из своего кэша. Кэш-когерентность гарантирует, что все процессоры видят единое, актуальное состояние памяти для любой ячейки данных.

История и предпосылки

С развитием многопроцессорных архитектур в 1980-х годах проблема когерентности кэша стала критической. Ранние многопроцессорные системы, такие как IBM System/370, использовали простые протоколы, но с ростом числа процессоров и объёмов кэша потребовались более сложные решения. В 1983 году был предложен протокол MESI (Illinois protocol), ставший основой для большинства современных систем. К началу 1990-х годов, с появлением многоядерных процессоров, кэш-когерентность стала неотъемлемой частью архитектуры x86 (начиная с Intel Pentium Pro) и ARM.

Проблема когерентности

Проблема возникает из-за наличия нескольких копий одних и тех же данных: в основной памяти и в кэшах каждого процессора. Без механизма когерентности возможны три классических сценария:

  1. Сценарий чтения-записи: процессор A записывает новое значение в ячейку X, но процессор B читает устаревшую копию из своего кэша.
  2. Сценарий записи-записи: два процессора одновременно записывают разные значения в одну ячейку, и результат не определён.
  3. Сценарий миграции данных: процессор A перемещает данные в свой кэш, изменяет их, а затем процессор B пытается прочитать исходные данные из памяти.

Для решения этих проблем разработаны протоколы когерентности, которые следят за состоянием кэш-линий и координируют доступ.

Протоколы кэш-когерентности

Все протоколы можно разделить на два основных класса: снуп-протоколы (snooping) и каталоговые протоколы (directory-based). Внутри каждого класса существует множество вариаций.

Снуп-протоколы

Снуп-протоколы работают на общей шине (или кольцевой топологии), где каждый кэш-контроллер «подслушивает» (snoops) все транзакции на шине. Если один процессор выполняет запись, он отправляет широковещательное сообщение, и все остальные кэши проверяют, не содержат ли они копию изменяемой ячейки. Если да — они либо аннулируют (invalidate) свою копию, либо обновляют её (update). Основные протоколы:

  • MSI (Modified, Shared, Invalid) — базовый протокол с тремя состояниями: модифицировано (только в этом кэше и изменено), разделяемо (копии есть в других кэшах, чисто) и недействительно (копия устарела).
  • MESI (Modified, Exclusive, Shared, Invalid) — расширение MSI, добавляющее состояние Exclusive (копия только в этом кэше, но не изменена). Это позволяет избежать лишнего широковещания при записи в эксклюзивную линию.
  • MOESI (Modified, Owned, Exclusive, Shared, Invalid) — добавляет состояние Owned (модифицировано, но другие кэши могут иметь копию; владелец отвечает за запись обратно в память). Используется в процессорах AMD.
  • MESIF (Modified, Exclusive, Shared, Invalid, Forward) — вариант Intel, где состояние Forward позволяет одному кэшу отвечать на запросы чтения, не обращаясь к памяти.

Каталоговые протоколы

В системах с большим числом процессоров (например, в NUMA-архитектурах) широковещание на шине становится неэффективным. Каталоговые протоколы используют централизованный или распределённый каталог, который хранит информацию о том, какие кэши содержат копии каждой кэш-линии. При записи контроллер обращается к каталогу, чтобы отправить инвалидацию только нужным кэшам, а не всем. Примеры: протоколы для SGI Origin 2000, IBM Power4, современных процессоров Intel Xeon (с использованием кольцевой шины и распределённых каталогов).

Аппаратная реализация

Кэш-когерентность реализуется на уровне кэш-контроллера и межсоединений. В современных многоядерных процессорах (например, Intel Core i7, AMD Ryzen) используется комбинация подходов: внутри кристалла — снуп-протоколы на кольцевой или сетчатой топологии, между кристаллами (в многокристальных системах) — каталоговые протоколы. Ключевые компоненты:

  • Кэш-контроллер — управляет состояниями кэш-линий и обрабатывает протокольные сообщения.
  • Межсоединение — шина, кольцо или сетка (mesh), по которой передаются сообщения (запросы чтения, инвалидации, ответы).
  • Буферы — для хранения ожидающих транзакций (например, write buffer, invalidation queue).

Влияние на производительность

Кэш-когерентность вносит накладные расходы:

  • Задержка — при записи процессор может ждать подтверждения инвалидации от других кэшей.
  • Трафик — широковещательные сообщения загружают шину (особенно в снуп-протоколах).
  • Сложность — увеличение числа транзисторов и энергопотребления.

Для снижения накладных расходов применяются оптимизации: использование состояний Exclusive/Owned, предварительная выборка, буферизация инвалидаций, а также когерентность на уровне кэш-линии (64 байта в x86), а не отдельных байтов.

Программная когерентность

В некоторых системах (например, в графических процессорах или встраиваемых системах) кэш-когерентность может обеспечиваться программно. Программист или компилятор вставляет барьеры памяти (memory barriers) и инструкции сброса кэша (например, clflush в x86). Этот подход дешевле аппаратно, но требует аккуратности и снижает производительность при частых синхронизациях. Примеры: OpenCL, CUDA (частично), некоторые DSP.

Примеры в архитектурах

  • x86 (Intel/AMD) — полная аппаратная когерентность на уровне кэшей L1, L2, L3. Используются протоколы MESI/MOESI/MESIF. В многокристальных системах (например, Intel Xeon с несколькими кристаллами) — каталоговый протокол с распределённым каталогом.
  • ARM — в многоядерных процессорах (Cortex-A серии) — снуп-протоколы (MESI с поддержкой ACE — Advanced Coherency Extensions). В big.LITTLE — когерентность между кластерами через шину CCI (Cache Coherent Interconnect).
  • RISC-V — стандарт не фиксирует протокол, но в реализациях (например, SiFive) используется MESI или MOESI.
  • GPU — в NVIDIA (начиная с архитектуры Pascal) и AMD (Vega) — частичная когерентность между GPU и CPU через унифицированную память (HMM — Heterogeneous Memory Management).

Критика и альтернативы

Основная критика аппаратной когерентности — сложность и энергопотребление, особенно в системах с десятками и сотнями ядер. Альтернативные подходы:

  • Когерентность на уровне транзакционной памяти — аппаратная поддержка атомарных блоков, которые автоматически управляют когерентностью.
  • Модели памяти без когерентности — в некоторых встраиваемых системах (например, Texas Instruments C66x) программист сам управляет кэшем.
  • Распределённые разделяемые памяти — программные системы (DSM — Distributed Shared Memory), которые эмулируют когерентность на уровне ОС.

Интересные факты

  • Первый коммерческий процессор с аппаратной когерентностью кэша — Intel 80386 (1985), но только в многопроцессорных конфигурациях с внешним контроллером.
  • В суперкомпьютере Cray X-MP (1982) использовалась программная когерентность через барьеры.
  • Протокол MESI был разработан в Университете Иллинойса в 1983 году и до сих пор является основой для большинства CPU.

Источники

  • Hennessy, J. L., Patterson, D. A. «Computer Architecture: A Quantitative Approach» (6th edition), 2019.
  • Intel Corporation. «Intel 64 and IA-32 Architectures Software Developer’s Manual», Volume 3A, Chapter 11.
  • AMD. «AMD64 Architecture Programmer’s Manual», Volume 2, Chapter 7.
  • Sorin, D. J., Hill, M. D., Wood, D. A. «A Primer on Memory Consistency and Cache Coherence», Morgan & Claypool, 2011.
  • ARM Limited. «ARM Architecture Reference Manual ARMv8-A», Chapter B2.

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →