Являются ли операции кэширования атомарными?
Я изучаю кэш-память процессора, и теперь у меня все еще непонимание протокола согласованности кэша (MESI). Представьте, что у нас 2 ядра имеют строку кэша в общем состоянии. И один из них выполняет чтение, другой выполняет запись:
;mem is cached in Shared state
Thread 1 (core 1) Thread 2 (core 2)
mov rax, [mem] mov [mem], dword 1
Может ли ядро 1 наблюдать какое-то промежуточное состояние. Я имею в виду что-то вроде следующего:
Core 2
отмечает строку кэша вL1D
как изменено и пишет изменения в него.- Строка кэша в
core 1
"sL1D
кеш все еще вShared
состояние, поэтому чтение происходит, чтобы прочитать устаревшее значение. - После того, как чтение устаревшего значения уже произошло, строка в
core 1
"sL1D
кеш помечен как недействительный.
Возможен ли такой сценарий в реализации Intel MESI/MESIF?
2 ответа
Строка кэша в кэш-памяти L1D ядра 1 все еще находится в состоянии общего доступа.
Это та часть сценария, которая нарушает МЭСИ. Хранилище не может зафиксировать, пока RFO, отправленный ядром 2, не завершится, поэтому ядро 1 имеет строку в неверном состоянии.
В вашем примере это не будет "промежуточным" шагом. Без синхронизации невозможно отличить ваш невозможный сценарий от простой загрузки ядра 1 до того, как линия была аннулирована. т.е. загрузка ядра 1 может появиться перед сохранением ядра 2 в глобальном порядке.
Хранилища не становятся глобально видимыми до тех пор, пока они не будут выполнены локально (они должны удалиться, а затем очередь хранилища сможет зафиксировать их в L1D), а модель памяти x86 позволяет переупорядочивать StoreLoad, поэтому хранилища могут быть отложены (скрыты в частном хранилище). очереди) до тех пор, пока последующие загрузки ядра 2 не станут видны глобально. (См. Раздел " Барьеры памяти Джеффа Пришинга", как операции управления исходным кодом, для получения дополнительной информации о переупорядочении памяти и о том, что означает переупорядочение StoreLoad).
В MESI (и во всех вариантах, таких как MESIF или MOESI), если один кэш имеет строку в состоянии E или M, никакие другие кэши не могут иметь копию этой строки. Таблица состояний в статье MESI wikipedia проясняет это: если один кэш имеет состояние E или M, у всех остальных - Invalid.
Два кэша никогда не смогут иметь действительные копии строки с разными данными. Именно это означает, что кэши должны быть связными, и предотвращение этого - вот и весь смысл протокола MESI.
Если ядро хочет изменить строку кэша, оно получает исключительное владение строкой, поэтому никакие другие ядра не могут наблюдать устаревшие значения. Это должно произойти до того, как магазин сможет перейти в L1D. Очереди хранилища существуют, чтобы скрыть задержку Read-For-Ownership (среди прочего), но данные в очереди хранилища еще не переданы в L1D. (Связано: что происходит, когда разные ядра ЦП записывают на один и тот же адрес ОЗУ без синхронизации? Больше о очереди хранилища).
И кстати, давайте предположим, что [mem]
выравнивается естественным образом, поэтому загрузка / сохранение в нем являются атомарными (что гарантируется архитектурой x86). Почему целочисленное присваивание для естественно выровненной переменной атомарно в x86?,
Многоуровневые кеши и модифицированные строки
При многоуровневом кэше грязные строки кэша могут распространяться вверх по иерархии. Таким образом, линия может быть в измененном состоянии в L1D и L2 одного и того же ядра. Это нормально, потому что обратная запись из L1D проходит через L2.
Насколько я понимаю, совместно используемый инклюзивный кэш L3 в процессорах Intel не должен выполнять обратную запись в DRAM, прежде чем он сможет совместно использовать копии строки кэша для нескольких ядер. Итак, что касается нормального / простого MESI, думайте о L3 как о резервном хранилище, а не о DRAM.
Заставить эту работу на системах с несколькими сокетами сложно; Я не уверен, что все настроено так, что L3 в сокете может кэшировать только физические адреса, которые соответствуют DRAM, подключенному к этому сокету. В любом случае запросы snoop отправляются между сокетами при пропадании кэша L3, и есть множество сложных настроек, которые вы можете настроить для настройки этого в системе Xeon. (См. Статью Anandtech о Haswell Xeon, например.)
На шаге 1, прежде чем ядро 2 сможет пометить линию как измененную, оно должно уведомить ядро 1. Таким образом, на шаге 2 линия больше не находится в ядре 1 L1D. Таким образом, на шаге 2, прежде чем получить доступ к линии, ядро 1 должно получить обновленное значение от ядра 2.