Хотя модель упорядочения памяти x86 не позволяет глобально наблюдать загрузки любого типа памяти, кроме WC, в программном порядке, реализация фактически позволяет завершать загрузки не в порядке. Было бы очень дорого останавливать выдачу запроса на загрузку, пока все предыдущие загрузки не будут завершены. Рассмотрим следующий пример:
load X
load Y
load Z
Предположим, что строка x отсутствует в иерархии кэша и должна быть извлечена из памяти. Тем не менее, оба Y и Z присутствуют в кэше L1. Один из способов соблюдения требований к упорядочению нагрузки для x86 - не выдавать нагрузки Y и X до тех пор, пока нагрузка X не получит данные. Однако это приведет к остановке всех инструкций, которые зависят от Y и Z, что приведет к потенциально значительному снижению производительности.
Множество решений было предложено и широко изучено в литературе. Тот, который Intel реализовал во всех своих процессорах, позволяет загружать нагрузки не по порядку, а затем проверять, не произошло ли нарушение порядка в памяти, и в этом случае нарушающая нагрузка переиздается и все ее зависимые инструкции воспроизводятся. Но это нарушение может иметь место только при соблюдении следующих условий:
- Загрузка завершена, пока предыдущая загрузка в программном порядке все еще ожидает своих данных, и эти две загрузки относятся к типу памяти, который требует упорядочения.
- Другое физическое или логическое ядро изменило строку, считанную более поздней загрузкой, и это изменение было обнаружено логическим ядром, которое выдало нагрузки до того, как более ранняя загрузка получила свои данные.
Когда выполняются оба эти условия, логическое ядро обнаруживает нарушение порядка в памяти. Рассмотрим следующий пример:
------ ------
core1 core2
------ ------
load rdx, [X] store [Y], 1
load rbx, [Y] store [X], 2
add rdx, rbx
call printf
Предположим, что начальное состояние:
- [X] = [Y] = 0.
- Строка кэша, содержащая Y, уже присутствует в L1D ядра core1. Но X не присутствует в частных кешах core1.
- Линия X присутствует в L1D core2 в состоянии изменяемой когерентности, а линия Y присутствует в L1D core2 в совместном состоянии.
В соответствии с моделью строгого упорядочения x86, единственно возможными правовыми исходами являются 0, 1 и 3. В частности, результат 2 не является законным.
Может произойти следующая последовательность событий:
- Core2 выдает RFO для обеих линий. RFO для строки X завершится быстро, но RFO для строки Y придется пройти до L3, чтобы сделать недействительной строку в частных кэшах core1. Обратите внимание, что core2 может фиксировать хранилища только по порядку, поэтому хранилище в строку X ожидает, пока хранилище в строку Y не завершится.
- Core1 выдает две нагрузки на L1D. Загрузка из строки Y завершается быстро, но загрузка из X требует извлечения строки из частных кешей core2. Обратите внимание, что значение Y в этой точке равно нулю.
- Строка Y является недействительной из частных кэшей core1, а ее состояние в core2 изменяется на изменяемое состояние когерентности.
- Core2 теперь фиксирует оба магазина по порядку.
- Строка X пересылается с core2 на core1.
- Core1 загружает из строки кэша X значение, хранящееся в core2, которое равно 2.
- Core1 печатает сумму X и Y, которая равна 0 + 2 = 2. Это неверный результат. По сути, в core1 загружено устаревшее значение Y.
Чтобы поддерживать порядок загрузок, загрузочный буфер core1 должен отслеживать все недействительные строки в резидентных кешах. Когда он обнаруживает, что строка Y была признана недействительной, в то время как существуют ожидающие загрузки, которые предшествуют завершенной загрузке из недействительной строки в программном порядке, происходит нарушение порядка в памяти, и нагрузка должна быть переиздана, после чего она получает самое последнее значение. Обратите внимание, что если строка Y была удалена из частных кешей core1 до того, как она была признана недействительной, и до того, как загрузка из X завершилась, она может быть не в состоянии отследить аннулирование строки Y в первую очередь. Таким образом, должен быть механизм, чтобы справиться и с этой ситуацией.
Если core1 никогда не использует одно или оба загруженных значения, нарушение порядка загрузки может произойти , но оно никогда не может наблюдаться .Точно так же, если значения, хранящиеся в core2 в строках X и Y, совпадают, нарушение порядка загрузки может произойти, но это невозможно наблюдать.Однако даже в этих случаях core1 все равно излишне переиздает нарушающую нагрузку и воспроизведет все ее зависимости.