Все современные ISA используют (вариант) MESI для когерентности кэша.Это обеспечивает согласованность во все времена общего представления памяти (через кэш), которое есть у всех процессоров.
См., Например, Можно ли принудительно установить когерентность кэша на многоядерном процессоре x86? Это распространенное явлениезаблуждение, что хранилища идут в кэш, в то время как другие ядра все еще имеют старые копии строки кэша, и тогда должна произойти «когерентность кэша».
Но это не так: чтобы изменить строку кэша, ЦПУ необходимоиметь исключительное владение линией (измененное или исключительное состояние MESI).Это возможно только после получения ответов на чтение для владельца, которое делает недействительными все остальные копии строки кэша, если ранее она находилась в состоянии общего или недействительного.См. Будут ли две атомные записи в разные места в разных потоках всегда рассматриваться в одном и том же порядке другими потоками? * Например, 1012 *.
Однако модели памяти допускают локальное переупорядочениемагазинов и грузов .Последовательная согласованность будет слишком медленной, поэтому процессоры всегда допускают как минимум переупорядочение StoreLoad.См. Также Безопасен ли mov + mfence для NUMA? для получения подробной информации о модели памяти TSO (общий порядок хранения), используемой в x86.Многие другие ISA используют еще более слабую модель.
Для несинхронизированного считывателя в этом случае есть три возможности, если оба работают на отдельных ядрах
load(a)
происходит на ядре #2 до того, как строка кэша будет признана недействительной, поэтому она считывает старое значение и, таким образом, эффективно происходит до сохранения a=1
в глобальном порядке. Загрузка может ударить в кэш-памяти L1d . load(a)
происходит после того, как ядро # 1 зафиксировало хранилище в своем кэш-памяти L1d и еще не отозвало.Запрос на чтение в Core # 2 запускает Core # 2 для обратной записи в совместно используемый общий уровень кэша (например, L3) и переводит строку в состояние Shared. Загрузка определенно будет отсутствовать в L1d . load(a)
происходит после обратной записи в память или, по крайней мере, L3 уже произошла, поэтому ей не нужно ждать ядра # 1отписаться Загрузка будет отсутствовать в L1d , если аппаратная предварительная выборка не вернула его по какой-либо причине.Но обычно это происходит только как часть последовательного доступа (например, к массиву).
Так что да, загрузка остановится, если другое ядро уже передало ее в кеш, прежде чем это ядро попытается
См. также Размер буферов хранилища на оборудовании Intel?Что именно представляет собой буфер хранилища? для получения дополнительной информации о влиянии буфера хранилища на все, включая переупорядочение памяти.
Здесь не имеет значения, потому что у вас есть производитель только для записи и только для чтенияпотребитель.Ядро производителя не ждет, пока его магазин станет глобально видимым, прежде чем продолжить, и он может сразу увидеть свой собственный магазин, прежде чем он станет глобально видимым. имеет значение , когда каждый поток просматривает хранилища, созданные другим потоком;тогда вам нужны барьеры или последовательно согласованные атомарные операции (которые компиляторы реализуют с барьерами). См. https://preshing.com/20120515/memory-reordering-caught-in-the-act
См. Также Может ли num ++ быть атомарным для 'int num'? о том, как атомарный RMW работает с MESI, поучительно для понимания концепции.(например, что атомарный RMW может работать, заставляя ядро зависать в строке кэша в состоянии Modified и задерживать ответ на RFO или запросы на его совместное использование до тех пор, пока часть записи RMW не будет зафиксирована.)