
ЭВУ 2 семестр / Презентации ЭВУ в пдф / Assotsiativnost-Printsipy_raboty_kesh-pamyati
.docАссоциативность
Ассоциативность кэш-памяти является непосредственным показателем её логической сегментации: сколько каналов, столько и сегментов. Другими словами, кэш-память с N-канальной ассоциативностью (N-way set associative) предполагает, что информация, размещённая по некоторому адресу в оперативной памяти, может находиться (кэшироваться) в N местах (строках) этой кэш-памяти. Основополагающим принципом логического сегментирования является тот факт, что в пределах любого отдельно взятого сегмента только одна строка может кэшировать информацию, находящуюся по некоторому адресу в оперативной памяти. Отсюда следует, что если адрес известен, то контроллеру во всём сегменте следует обработать только одну строку. Таким образом, при получении запроса на чтение или запись контроллер опросит нужные строки во всех имеющихся сегментах на предмет наличия (cache hit) или отсутствия (cache miss) информации, соответствующей полученному адресу. Либо все сегменты возвратят сигнал отсутствия, либо за исключением одного, ответившего сигналом наличия. В случае положительного отзыва контроллеру нет необходимости проводить какой-либо дальнейший анализ, достаточно просто довести операцию до конца. Если все сегменты ответили негативно на запрос о чтении информации, то он будет перенаправлен к контроллеру кэш-памяти более низкого уровня и так далее вплоть до контроллера оперативной памяти. Если же запрос был на запись, то контроллеру предстоит задача выбора сегмента, куда будет осуществлена операция. Для этого он анализирует строки-кандидаты, а если точнее, то их биты состояния и прочие вспомогательные, после чего принимает решение согласно одному из нижеследующих популярных алгоритмов:
LRU (Least Recently Used, то есть "наименее новый из запрашиваемых") — запись идёт в строку, находящаяся в которой информация запрашивалась в последний раз наиболее давно;
LRR (Least Recently Replaced, то есть "наименее новый из записанных"), также известный как FIFO (First In, First Out, то есть "первым пришёл, первым ушёл") — вытесняется строка, находящаяся в которой информация характеризуется наиболее старой записью;
LFU (Least Frequently Used, то есть "наименее часто запрашиваемый") — для записи выбирается строка, находящаяся в которой информация запрашивалась наименее часто;
Random — запись идёт в произвольно выбираемую строку.
Как можно предположить, более сложные по сравнению с Random алгоритмы являются более эффективными, но используют более сложную обслуживающую логику. LFU предполагает, что каждая строка кэш-памяти снабжена соответствующим счётчиком, обновляемым при каждом удачном запросе. LRU и LRR используют вместо него счётчик времени в той или иной форме, причём LRU требует его обновления при каждом удачном запросе. Random не предполагает каких-либо строчных счётчиков и вполне может быть основан на одном генераторе случайных чисел или чём-то подобном.
Существует два частных варианта политики ассоциативности, фактически являющиеся противоположными друг другу. В полностью ассоциативной (fully associative) кэш-памяти информация, находящаяся по некоторому адресу в оперативной памяти, может быть записана в произвольную строку кэш-памяти. В случае кэш-памяти с прямым отображением (direct mapped) эта же информация может быть размещена только в одной из всех имеющихся строк кэш-памяти. Если точнее, то в полноассоциативной модели каждая строка выступает в качестве самостоятельного сегмента, а в модели с прямым отображением имеется только один сегмент на все строки. Отсюда следует, что кэш-память с прямым отображением может быть охарактеризована как таковая с 1-канальной ассоциативностью, а в кэш-памяти с полной ассоциативностью количество каналов равняется количеству строк. Модель с прямым отображением определённо является наиболее быстрой в работе, так как перед контроллером не стоит проблема выбора строки при операции чтения/записи. С другой стороны, эта модель является наименее эффективной в работе, так как при записи у контроллера не будет возможности выбора строки. Таким образом, если два или более адреса постоянно конфликтуют между собой за одну и ту же строку кэш-памяти, это может привести к значительному падению производительности. Логично предположить, что полноассоциативная модель является наиболее эффективной в работе, но в то же время и наиболее медленной. Тем не менее, существуют и промежуточные варианты политики ассоциативности, компромиссные по отношению к быстродействию и эффективности. В частности, 2-, 4- и 8-канальные модели являются наиболее популярными.
Нижеследующая схема наглядно демонстрирует разные варианты политики ассоциативности в работе. Имеется некоторая абстрактная модель в составе 8 строк кэш-памяти и физического пространства оперативной памяти, эквивалентного по размеру 64 строкам кэш-памяти. Необходимо записать в кэш 10-ю так называемую строку оперативной памяти (nota bene: все строки номеруются от 0 и по возрастающей, так что у этой будет номер 9). В случае кэш-памяти с прямым отображением она может быть записана только в одно место: 9 mod 8 = 1 (остаток от деления), т. е. строка номер 1 единственного 8-строчного сегмента. Кэш-память с 2-канальной ассоциативностью предоставляет возможность выбора из двух мест: 9 mod 4 = 1, т. е. строка номер 1 любого 4-строчного сегмента. Полноассоциативная кэш-память даёт максимальную свободу выбора: 9 mod 1 = 0, т. е. строка номер 0 любого 1-строчного сегмента, фактически, любая строка. Действия при чтении подобны вышеописанным. В случае кэш-памяти с прямым отображением надлежит опросить строку номер 1, кэш-памяти с 2-канальной ассоциативностью — строки номер 1 и 5, полноассоциативной кэш-памяти — все строки.
В реальной жизни полноассоциативная кэш-память встречается, но редко и небольших размеров. Например, в процессорах семейства Cyrix 6x86 имелось 256 байт такой кэш-памяти "нолевого уровня" под названием instruction line buffer, предназначавшегося исключительно для команд. Она поддерживалась двухпортовой U-cache размером в 16Кб (6x86 и 6x86L) или 64Кб (6x86MX и 6x86MII). Основным предназначением этой необычно малой кэш-памяти было уменьшение нагрузки на U-cache и минимизация простоев процессора при чрезмерном заполнении U-cache данными. Часто полноассоциативную схему применяют при проектировке TLB (о которых речь пойдёт позже), кэшей адресов переходов, буферов ввода/вывода и т. д. Как правило, ассоциативности I-cache и D-cache низки (обычно 2 или 4 канала), так как увеличение их числа приводит к увеличению задержек доступа, что серьёзным образом отражается на производительности. В качестве некоторой компенсации показатель ассоциативности S-cache определяют более высоким (обычно 8 или 16 каналов), так как величина задержек доступа к этой кэш-памяти не так важна. Тем не менее, ассоциативности B-cache типично низки: от прямого отображения для расположенных на материнских платах кэшей до 2- или 4-канальных ассоциативностей для размещённых на процессорных модулях. Причиной этого является сложность построения высокоассоциативных кэшей при использовании стандартных дискретных компонент.
Размер строки, тэга и индекса
Как уже было упомянуто в предыдущем разделе, любая кэш-память подразделяется на так называемые строки (lines). Было бы крайне нерационально наделять каждый байт в кэш-памяти адресным полем, указывающим на его местонахождение в оперативной памяти, потому что это привело бы к очень тяжёлым последствиям с точки зрения производственных затрат. Если рассмотреть 32-битное линейное физическое адресное пространство, позволяющее непосредственно поддерживать до 4Гб оперативной памяти, каждый байт в кэш-памяти должен обслуживаться 4 адресными байтами! Кроме того, такая кэш-память была бы очень плоха с точки зрения производительности. Поэтому куда удобнее адресовать некоторые группы из соседствующих байт, которые и будут формировать строки кэш-памяти. На практике широко используются строки по по 32 или 64 байта, хотя их размер может достигать даже 1024 байт. Естественно, размер строки должен быть эквивалентен двойке в некоторой целой положительной степени. Однако, даже если требование по размеру выполнено, не каждая группа из соседствующих байт может быть кэширована по причине дополнительного ограничения, известного как адресное выравнивание (address alignment). Другими словами, группа соседствующих байт может помещена в строку кэш-памяти тогда и только тогда, если её начальный адрес выровнен по границе, равной размеру строки. Например, 32-байтная строка может быть заполнена информацией из оперативной памяти, находящейся по шестнадцатеричным (десятичным) адресам 00-1F (00-31), 20-3F (32-63), 40-5F (64-95) и т. д. Кроме иных преимуществ, это простое правило позволяет сократить число адресных бит в расчёте на одну строку. Если точнее, то на 5 в вышеприведённом примере, т. е. log2(32) = 5. Наконец, строка кэш-памяти может быть либо полностью заполненной действительной информацией, либо полностью пустой, что равносильно быть заполненной недействительной информацией. Промежуточные варианты не поддерживаются. Из этого правила есть одно исключение: если у двух строк кэш-памяти имеется одно общее адресное поле, тогда их можно рассматривать как подстроки одной строки, которые могут функционировать относительно независимо одна от другой.
Каждое адресное поле состоит из двух основных частей: динамической (tag), которая содержит старшие биты адреса, и статической (index), которая содержит младшие биты адреса (nota bene: не путать с динамическими и статическими ячейками памяти, это другое). Первая часть может быть изменена в процессе работы, значение второй зафиксировано. Для объяснения их функционального назначения необходимо ввести понятие логической сегментации оперативной памяти. Допустим, имеется некоторое физическое пространство оперативной памяти, состоящее из M сегментов памяти одинакового размера, каждый из которых равен по размеру сегменту кэш-памяти. Каждый сегмент памяти состоит из N строк одинакового размера, каждая из которых равна по размеру строке кэш-памяти. Таким образом, чтобы получить адрес какой-либо строки памяти, необходимо сначала определить номер её сегмента памяти, затем номер данной строки в этом сегменте и объединить оба номера. Для полноты картины осталось лишь подставить тэг вместо номера сегмента и индекс вместо номера строки.
Размер тэга строки кэш-памяти зависит от 3 основных факторов:
размера кэш-памяти;
ассоциативности кэш-памяти;
кэшируемого размера оперативной памяти.
Этот размер рассчитывается по следующей формуле:
Stag — размер одного тэга кэш-памяти, в битах;
Smemory — максимальный кэшируемый размер оперативной памяти, в байтах;
Scache — размер кэш-памяти, в байтах;
A — ассоциативность кэш-памяти, в каналах.
Таким образом, для абстрактной системы с максимальным кэшируемым объёмом оперативной памяти в 1Гб и кэш-памятью (неважно какого уровня) размером в 1Мб с 2-канальной ассоциативностью потребуется ровно 11 бит для каждого тэга. Другими словами, для адресации любым отдельным тэгом 1Гб / 512Кб = 2048 сегментов памяти потребуется log2(2048) = 11 бит. Следует уточнить, что ровно столько бит на тэг необходимо для кэширования именно 1Гб оперативной памяти при данной организации кэш-памяти. Если сократить количество бит, то такой кэш останется работоспособным, однако кэшируемый размер оперативной памяти уменьшится. Например, 8 бит на тэг позволят адресовать уже только 28 = 256 сегментов памяти по 512Кб, что позволит кэшировать лишь 128Мб оперативной памяти. Информация, находящаяся выше этой границы, кэшироваться не будет. Подобная ситуация нежелательна, но была широко распространена во времена процессоров 486 и Pentium. Их наборы системной логики поддерживали значительные объёмы оперативной памяти как на то время, типично от 256Мб до 1Гб для систем Pentium-класса, но кэшируемый B-cache диапазон был обычно ограничен 64Мб. Многие из тех систем использовали стандартную 8-битную микросхему SRAM для массива тэгов и восемь 8х32Кбит микросхем SRAM, формировавших 256Кб банк данных с прямым отображением. Эти микросхемы часто размещались в стандартных установочных гнёздах, поэтому могли быть заменены без каких-либо проблем. Позже, они уступили место удешёвлённым аналогам с большим числом контактов, которые распаивались прямо на материнских платах. К слову, значительно менее затратно произвести и установить пару 32х32Кбит микросхем, чем восемь 8х32Кбит, да ещё и с установочными гнёздами. Наибольший размер B-cache, устанавливавшийся на материнские платы Pentium-класса, составлял 2Мб. Подобные дискретные кэши не прижились в следующих поколениях массовых x86 систем за исключением нескольких процессорных семейств, где микросхемы кэш-памяти устанавливались на процессорные модули — Intel Pentium II (Klamath, Deschutes) и Pentium II Xeon (Drake), Intel Pentium III (Katmai) и Pentium III Xeon (Tanner), AMD Athlon (К7, K75).
Размер индекса зависит от размеров сегмента кэш-памяти и строки. Фактически, его должно быть достаточно для номерации всех строк в пределах отдельно взятого сегмента. Например, если имеется сегмент кэш-памяти в 512Кб с 32-байтными строками, то размер индекса составит log2(512Кб / 32б) = 14 бит. Следовательно, каждая строка в каком-либо сегменте имеет свой постоянный номер, который не подлежит изменению, да и какой-либо необходимости в этом нет. Если имеется N сегментов кэш-памяти, тогда всегда должно быть N строк кэш-памяти с одинаковым индексом. Увеличение размера строки при неизменном размере сегмента приведёт к уменьшению размера индекса, а также и к уменьшению их количества вследствие уменьшения числа строк. В то же время увеличение размера строки приводит к увеличению задержек при загрузке строк из других уровней кэш-памяти или оперативной памяти, равно как и при выгрузке, поскольку возможности шинных интерфейсов небезграничны. К тому же, больший размер строки ухудшает эффективность кэш-памяти вследствие большей степени её засорения ненужной информацией.
Ко всему прочему, каждая строка кэш-памяти обычно обладает некоторой избыточной информацией для надлежащего контроля за ошибками. Как правило, поля данных защищаются либо проверкой чётности (parity checking) на уровне отдельных байт, либо одним из протоколов проверки и коррекции ошибок (ECC — Error Checking and Correcting) на уровне целого поля, большинство из которых основывается на алгоритме Хэмминга (the Hamming algorithm). Тэги могут защищаться однобитовой проверкой чётности, хотя эта практика распространена в значительно меньшей степени, чем защита полей данных. Тем не менее, какой бы из алгоритмов защиты информации ни был выбран, его обслуживающая логика привнесёт сложности в существующую разработку и неминуемо отразится на задержках при работе. Если точнее, то при каждой операции чтения контрольная сумма поля данных должна быть сосчитана и сверена с сохранённой. Наиболее тяжёлым является случай частичной записи в действительную строку, так как контрольная сумма должна быть сосчитана дважды: до и после изменения строки.
Индикаторы состояния строки
Логично предположить, что любая строка кэш-памяти должна обладать некой служебной информацией, описывающей её состояние. Например, как узнать, является ли некоторая строка действительной (т. е. содержит полезную информацию) или недействительной (т. е. заполненной мусором)? Для этого необходимо определиться с соответствующей политикой. Допустим, считать строку недействительной, если все биты поля тэгов и данных являются высокими, т. е. находятся в положении единиц. Впрочем, такой подход превратит жизнь любого контроллера кэш-памяти в ужас без конца. Допустим, контроллер получил запрос на чтение или запись некоторых данных, находящихся в оперативной памяти по определённому адресу. Он активирует все строки кэш-памяти с требуемым индексом и начинает проверять их биты тэгов и данных на построчной основе (последовательное логическое умножение, AND). Если в результате получилась единица, то данную строку следует рассматривать как недействительную. К примеру, если речь идёт о кэш-памяти размером в 256Кб с 16-канальной ассоциативностью и с 64-байтными строками, а кэшируемый диапазон определён в 4Гб, то при обслуживании каждого запроса в худшем случае потребуется обработать 288 тэговых бит и 8192 бит данных! Ситуация ухудшится ещё более при использовании алгоритмов целостности информации. Конечно же, такой подход не имеет никакой практической ценности. Даже если ограничиться проверкой лишь тэговых бит, обрекая последний сегмент памяти на некэшируемость, этот метод может найти лишь ограниченное применение. К слову, B-caches времён 486 и Pentium систем иногда проектировали подобным образом, но не стоит забывать о том, что они были с прямым отображением и их тэги были относительно небольшого размера. Таким образом, этот подход является исключением, а не правилом. В то же время ситуация меняется кардинальным образом при добавлении в модель кэш-памяти по одному биту на каждую строку, описывающему её состояние. Допустим, что строка считается действительной, если этот бит является высоким. Жизнь становится проще: теперь контроллеру незачем проверять что-либо ещё, чтобы определить состояние какой-либо строки.
Хотя бит действительности (validity bit) сам по себе является отличным решением, он может стать источником серьёзных проблем при случайном инвертировании. В реальной жизни вероятность того, что недействительная строка станет действительной, очень мала, но не исключена. Существует несколько способов предотвращения этого нежелательного происшествия, но самым простым из них можно считать добавление в пару к биту действительности ещё одного, хранящего его противоположное значение. Если при чтении обоих бит окажется, что они одинаковы, то сооветствующая строка должна быть признана недействительной. Вероятность изменения обоими битами своих значений на противоположные практически исключена, так как все известные на этой планете электромагнитные помехи однополярны.
Также весьма полезен бит изменённости (modified bit), иначе именуемый грязным битом (dirty bit), который является показателем того, является ли данная строка копией соответствующей строки в оперативной памяти или отличается от неё. Если этот бит отсутствует, то строка будет считаться неизменённой/чистой, а любая запись в неё будет также направляться в оперативную память. Такая кэш-память называется согласованной с оперативной памятью (memory coherent). В целом, подобная организация работы кэш-памяти не является особенно привлекательной из-за увеличения нагрузки на оперативную память. С другой стороны, уменьшаются накладные расходы при записи, так как любая неизменённая строка в кэш-памяти может быть заменена другой строкой без каких-либо дополнительных действий. В то же время любая изменённая строка не может быть вытеснена без предварительного копирования в низлежащий уровень кэш-памяти или в оперативную память из-за очевидной потери информации. Контроллер обязан отслеживать состояние этих строк, для чего и необходим бит изменённости. Кстати, D-caches и U-caches могут включать или не включать поддержку изменённых строк, в то время как I-caches всегда согласованы с оперативной памятью. Впрочем, возможность хранения изменённых строк в кэш-памяти без использования бит изменённости всё же существует, хотя этот подход и малопопулярен. Дело в том, что все строки кэш-памяти можно попросту считать изначально грязными вне зависимости от того, являются ли они таковыми на самом деле. Побочным эффектом такой авантюры будет очень большая нагрузка по записи на низлежащую кэш-память или оперативную память, поскольку каждая загруженная строка в такую кэш-память должна быть неминуемо выгружена назад через некоторое время. Некоторые наборы системной логики прошлого, такие как VIA Apollo VP2/97 (также известный как AMD-640) для процессоров Pentium-класса, поддерживали этот режим как опцию для B-cache с целью увеличения размера кэшируемого пространства оперативной памяти, но производительность всё же оставляла желать лучшего.
Третьим важным индикатором состояния строки кэш-памяти является бит общности (shared bit). Он служит для определения факта того, имеются ли копии данной строки в других кэшах, включая принадлежащие другим процессорам. Например, если один из процессоров изменит свою локальную копию, то другие процессоры должны быть поставлены в известность и предпринять действия по отношению к своим копиям. Для этой цели существуют два метода: Write Update (также известный как Write Broadcast) и Write Invalidate. Первый обязывает модифицирующий процессор отправить изменения другим процессорам, чтобы они смогли обновить свои копии. Второй метод отличается от первого тем, что вместо собственно изменений пересылается специальное сообщение, согласно которому другие процессоры должны изменить состояние своих копий на недействительное. В целом, Write Update утилизирует системную шину в значительно большей степени, чем Write Invalidate, поэтому хорошо подходит для систем с звездообразной топологией (star-based topology; также известной как топология "точка-точка" (point to point)), а особенно хорошо — для систем с прямыми межпроцессорными связями. С другой стороны, Write Invalidate оптимален для конфигураций с топологией общей шины (bus-based topology).
Используя вышеприведённую информацию, можно приступать к рассмотрению логических протоколов состояния и согласованности, которые формируют основу почти всех физических реализаций. Наиболее популярным считается MESI (Modified, Exclusive, Shared, Invalid):
Состояние |
бит 0 |
бит 1 |
Modified |
1 |
0 |
Exclusive |
0 |
1 |
Shared |
1 |
1 |
Invalid |
0 |
0 |
Этот протокол предполагает наличие двух физических бит для реализации всех четырёх возможных состояний. Строка с состоянием Invalid не содержит полезной информации, поэтому её можно рассматривать как пустую. Состояние Exclusive подразумевает, что информация в данной строке полностью соответствует оперативной памяти (так называемая чистая строка, clean line) и не содержится в других кэшах — данная копия в буквальном смысле эксклюзивна. Строка с состоянием Modified не соответствует оперативной памяти (так называемая грязная строка, dirty line) и также не имеет копий в других кэшах. Shared является чистая строка, которая может иметь копии в других кэшах (если точнее, то копии присутствовали в момент установки состояния, но могли исчезнуть со временем).
Протокол MESI несовершенен, но довольно прост и неплохо подходит для однопроцессорных конфигураций с одним или двумя уровнями кэш-памяти. Кратко рассмотрим механизм его работы на примере. Допустим, в D-cache была загружена свежая строка из оперативной памяти, которая получает состояние Exclusive. При её вытеснении другой строкой из D-cache в S-cache она сохраняет свой прежний статус. Когда она снова будет загружена в D-cache, её состояние изменится на Shared, равно как и состояние оставшейся в S-cache копии. При изменении хранящегося в D-cache клона возможны два эффективных варианта дальнейшего развития событий: а) изменения будут направлены как в S-cache, так и в оперативную память, а состояния обеих строк сохранятся как Shared; б) изменения не будут никуда направляться, строка в S-cache будет помечена как Invalid, а изменяемая в D-cache примет статус Modified. Прочие варианты являются значительно менее рациональными и здесь рассматриваться не будут.
Существуют несколько разновидностей протокола 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, связанного с невозможностью хранения изменённых строк в более чем одной кэш-памяти. Для этого и было введено новое состояние, Owned, которое означает владение правами собственности на данную строку кэш-памяти, её клоны в других кэшах и в оперативной памяти. Эти права изначально принадлежат системной логике, на которой лежит обязанность по обеспечению всех агентов в системе (nota bene: подразумеваются не только процессоры, но и устройства типа bus master) верной информацией. Первым посягательством на эту обязанность стало состояние Modified, которое значительно усложнило жизнь системной логике. При каждом запросе агентом информации из кэшируемого пространства оперативной памяти системная логика была вынуждена его приостанавливать, затем генерировать запрос всем процессорам на предмет наличия соответствующей грязной строки в их кэшах. Если у одного из них имеется такая строка, то он был обязан записать её в оперативную память и изменить состояние на Exclusive. По окончании этой операции системная логика перезапускала изначальный запрос и доставляла информацию. Если бы поддержка состояния Modified отсутствовала, тогда кэши всех процессоров всегда были бы согласованны с оперативной памятью, а системной логике было бы незачем проводить какие-либо дополнительные мероприятия, так как она могла бы просто доставить запрашиваемую информацию непосредственно из оперативной памяти. Теперь о состоянии Owned. По предоставлении строке этого состояния процессор-хозяин берёт на себя все права и обязанности относительно неё у системной логики. Если какой-либо системный агент запросит эту строку, то процессор-хозяин обязан ответить и предоставить свою копию. Даже если системная логика активно участвует в этой операции, оперативная память не обновляется. Фактически, если состояние Modified можно представить как Exclusive Modified, то Owned — не иначе как Shared Modified. Если какая-либо строка становится Owned, то её копии в других кэшах, включая принадлежащие другим процессорам, должны иметь состояние Shared.
Однако, протокол MOESI также оказался неидеальным. Фактически, состояние Owned давало все преимущества процессору-хозяину, но ставило остальные процессоры в положение только для чтения. А если кому-нибудь из них было необходимо произвести запись в свою строку с состоянием Shared, то приходилось производить её и в оперативную память, что приводило к ликвидации состояния Owned у другого процессора. Это не представляет проблемы для большинства многопроцессорных сред, типично имеющих один активно записывающий процессор и несколько читающих процессоров на строку, но ситуация несколько иная в многопоточных средах, где разные процессоры или процессорные ядра могут одновременно выполнять разные потоки одного и того же процесса. Для улучшения производительности в этом случае и было введено состояние Written, которое привело к созданию протокола MOWESI. При записи в строку с состоянием Shared оно изменялось на Written, остальные процессоры оповещались о факте и обязывались ликвидировать или обновить свои устаревшие копии, после чего состояние строки изменялось на Modified или Owned соответственно. Обновления оперативной памяти удавалось избежать в обоих случаях. Поддержка этого протокола впервые появилась в 2-ядерном процессоре IBM POWER5.
Существуют многочисленные низкоуровневые протоколы состояния и согласованности, которые оперируют разными комбинациями индикаторов состояния, методами записи, шинного сигналирования и пр. Среди наиболее старых можно назвать Illinois, Write Once, Berkeley, DEC Firefly, Xerox Dragon. К примеру, большая часть систем на основе процессоров DEC Alpha использует MOESI-подобный протокол с поддержкой Write Invalidate, оперирующий битами tagCtlV (Valid), tagCtlS (Shared) и tagCtlD (Dirty), дополненных битом tagCtlP для реализации алгоритма чётного равенства (even parity). В нижеследующей таблице приводятся возможные комбинации смысловых бит с описанием соответствующих им состояний, а знак "x" означает, что состояние данного бита значения не имеет (nota bene: для однопроцессорных систем также поддерживается более простая реализация, не использующая tagCtlS, но о ней речь идти не будет).