Глобально невидимые инструкции по загрузке

Могут ли некоторые инструкции по загрузке не отображаться глобально из-за пересылки данных из магазина? Иными словами, если инструкция загрузки получает свое значение из буфера хранилища, она никогда не должна читать из кэша.
Поскольку обычно указывается, что загрузка видна глобально, когда она читает из кэша L1D, те, которые не читают из L1D, должны сделать ее глобально невидимой.

4 ответа

Решение

Концепция глобальной видимости для нагрузок является хитрой, потому что нагрузка не изменяет глобальное состояние памяти, и другие потоки не могут непосредственно наблюдать его.

Но как только пыль оседает после неупорядоченного / спекулятивного выполнения, мы можем сказать, какое значение получила нагрузка, если поток где-то ее хранит, или ветвится на ее основе. Это наблюдаемое поведение потока - вот что важно. (Или мы можем наблюдать это с помощью отладчика и / или просто рассуждать о том, какие значения может видеть нагрузка, если эксперимент труден.)


По крайней мере, на строго упорядоченных процессорах, таких как x86, все процессоры могут договориться о том, чтобы общий порядок магазинов стал глобально видимым, обновляя состояние единого связного + согласованного кэша + памяти. На x86, если переупорядочение StoreStore не разрешено, этот TSO (Total Store Order) согласуется с программным порядком каждого потока. (Т.е. общий порядок - это некоторое чередование порядка программы из каждого потока). SPARC TSO также это сильно упорядочено.

(Для хранилищ, обходящих кэш, глобальная видимость - это когда они сбрасываются из некогерентных буферов объединения записи в DRAM.)

На слабо упорядоченном ISA потоки A и B могут не согласовать порядок хранилищ X и Y, выполняемых потоками C и D, даже если потоки чтения используют acqu-load, чтобы убедиться, что их собственные нагрузки не переупорядочены. т. е. не может быть глобального порядка магазинов вообще, не говоря уже о том, чтобы он не совпадал с программным порядком.

IBM POWER ISA настолько слаб, как и модель памяти C++11 ( будут ли другие атомарные записи в разные места в разных потоках всегда рассматриваться в одном и том же порядке другими потоками?). Это может вступить в противоречие с моделью хранилищ, которая становится глобально видимой, когда они фиксируются из буфера хранилища в кэш L1d. Но @BeeOnRope в комментариях говорит, что кеш действительно является связным и позволяет восстанавливать последовательную согласованность с барьерами. Эти эффекты множественного порядка возникают только из-за того, что SMT (несколько логических процессоров на одном физическом процессоре) вызывает странное локальное переупорядочение.

(Одним из возможных механизмов было бы позволить другим логическим потокам отслеживать не спекулятивные хранилища из буфера хранилища даже до того, как они фиксируют L1d, только сохраняя хранилища, еще не списанные, как частные, для логического потока. Это может немного уменьшить задержку между потоками. X86 не может этого сделать, потому что это сломало бы модель сильной памяти: HT Intel статически разделяет буфер хранилища, когда два ядра активны на ядре. Но, как комментирует @BeeOnRope, абстрактная модель разрешенных переупорядочений является, вероятно, лучшим подходом для рассуждения о правильности. То, что вы не можете придумать механизм HW, который может привести к переупорядочению, не означает, что этого не произойдет.

Слабо упорядоченные ISA, которые не так слабы, как POWER, по-прежнему переупорядочивают в буфере локального хранилища каждого ядра, если не используются барьеры или хранилища релизов. На многих процессорах существует глобальный порядок для всех магазинов, но это не некоторое чередование порядка программ. ЦП OoO должны отслеживать порядок памяти, поэтому одному потоку не нужны барьеры, чтобы видеть свои собственные хранилища по порядку, но разрешение хранилищ зафиксировать хранилища из буфера хранилища в L1d вне порядка программы, безусловно, может улучшить пропускную способность (особенно если имеется несколько хранилищ в ожидании той же строки, но программный порядок вытеснит строку из ассоциативно-множественного кэша между каждым хранилищем (например, неприятный шаблон доступа к гистограмме).


Давайте сделаем мысленный эксперимент о том, откуда берутся данные загрузки

Выше все еще только о видимости магазина, а не о загрузках. Можем ли мы объяснить значение, видимое каждой загрузкой как считываемое из глобальной памяти / кэша в какой-то момент (без учета каких-либо правил упорядочения нагрузки)?

Если это так, то все результаты загрузки могут быть объяснены путем помещения всех хранилищ и загрузок всеми потоками в некоторый объединенный порядок, чтения и записи согласованного глобального состояния памяти.

Оказывается, нет, мы не можем, буфер хранилища ломает это: частичная пересылка из хранилища в загрузку дает нам контрпример (на x86, например). Узкое хранилище, за которым следует широкая загрузка, может объединить данные из буфера хранилища с данными из кэша L1d до того, как хранилище станет глобально видимым. Реальные x86-процессоры фактически делают это, и у нас есть реальные эксперименты, чтобы доказать это.

Если вы посмотрите только на полную пересылку хранилища, когда загрузка берет только свои данные из одного хранилища в буфере хранилища, вы можете утверждать, что загрузка задерживается буфером хранилища. то есть, что нагрузка появляется в общем порядке хранения общей загрузки сразу после хранилища, что делает это значение глобально видимым.

(Этот глобальный общий порядок хранения нагрузки не является попыткой создать альтернативную модель упорядочения памяти; он не может описать действительные правила упорядочения загрузки x86.)


Частичная пересылка хранилища обнажает тот факт, что данные загрузки не всегда поступают из глобального когерентного домена кэша.

Если хранилище из другого ядра изменяет окружающие байты, атомная загрузка может считывать значение, которое никогда не существовало и никогда не будет существовать в глобальном связном состоянии.

См. Мой ответ на вопрос Может ли x86 переупорядочить узкий магазин с более широкой загрузкой, которая полностью его содержит? и ответ Алекса для экспериментального доказательства того, что такое переупорядочение может произойти, делая предложенную схему блокировки в этом вопросе недействительной. Хранение, а затем перезагрузка с того же адреса не является барьером памяти StoreLoad.

Некоторые люди (например, Линус Торвальдс) описывают это, говоря, что буфер хранилища не согласован. (Линус отвечал кому-то, кто независимо придумал ту же самую недопустимую идею блокировки.)

Еще один вопрос и ответ, касающийся буфера и когерентности хранилища: как эффективно установить биты битового вектора параллельно?, Вы можете выполнить некоторые неатомарные операции ИЛИ для установки битов, а затем вернуться и проверить наличие пропущенных обновлений из-за конфликтов с другими потоками. Но вам нужен барьер StoreLoad (например, x86 lock or) чтобы убедиться, что вы не просто видите свои собственные магазины при перезагрузке.


Нагрузка становится видимой глобально, когда она читает свои данные. Обычно из L1d, но другие возможные источники - буфер хранения, MMIO или не кэшируемая память.

Это определение согласуется с руководствами x86, в которых говорится, что нагрузки не переупорядочиваются с другими нагрузками. то есть они загружаются (в порядке программы) с точки зрения памяти локального ядра.

Сама загрузка может стать видимой глобально независимо от того, может ли какой-либо другой поток загрузить это значение с этого адреса.

Позвольте мне немного расширить вопрос и обсудить аспект правильности реализации пересылки данных из магазина. (Вторая половина ответа Петра прямо отвечает на вопрос, который я думаю).

Пересылка из магазина изменяет задержку загрузки, а не ее видимость. Если его не покраснеть из-за какого-то неправильного предположения, магазин в конечном итоге станет глобально видимым в любом случае. Без пересылки загрузки хранилища нагрузка должна ждать, пока все конфликтующие хранилища не будут удалены. Тогда загрузка может получать данные в обычном режиме.

(Точное определение конфликтующего хранилища зависит от модели упорядочения памяти ISA. В x86 предполагается тип памяти WB, который позволяет пересылку загрузки хранилища, любое хранилище, которое находится в более раннем порядке программы и чье целевое расположение физической памяти перекрывается, что нагрузки является конфликтным магазином).

Хотя, если есть какое-либо параллельное конфликтующее хранилище от другого агента в системе, это может фактически изменить загруженное значение, потому что внешнее хранилище может вступить в силу после локального хранилища, но до локальной загрузки. Как правило, буфер хранилища не находится в области когерентности, и поэтому пересылка хранилища может снизить вероятность того, что что-то подобное произойдет. Это зависит от ограничений реализации перенаправления загрузки хранилища; обычно нет никаких гарантий, что пересылка произойдет для какой-либо конкретной операции загрузки и хранения.

Пересылка из хранилища может также привести к глобальным порядкам памяти, которые были бы невозможны без него. Например, в сильной модели x86 разрешено переупорядочение загрузки хранилища, и вместе с переадресацией загрузки хранилища каждый агент в системе может просматривать все операции с памятью в разных порядках.

В общем, рассмотрим систему с общей памятью, в которой ровно два агента. Пусть S1(A, B) будет набором возможных порядков глобальной памяти для последовательностей A и B с пересылкой с сохранением нагрузки, и пусть S2(A, B) будет набором возможных порядков глобальной памяти для последовательностей A и B без сохранения пересылка И S1(A, B), и S2(A, B) являются подмножествами набора всех допустимых порядков глобальной памяти S3(A, B). Пересылка из хранилища может сделать S1(A, B) не подмножеством S2(A, B). Это означает, что если S2(A, B) = S3(A, B), то перенаправление загрузки хранилища будет недопустимой оптимизацией.

Переадресация при загрузке из хранилища может изменить вероятность возникновения каждого глобального порядка памяти, поскольку это снижает задержку загрузки.

Я не уверен, что глобальная видимость является интересной концепцией для операций загрузки (требуется пояснение), но если вы хотите использовать ее для урегулирования какого-либо семантического аргумента, вам придется зависеть от определений. Если, например, ваше определение глобальной видимости для нагрузок является моментом, когда оно загружает значение из кэша L1 и не допускает возможности пересылки из хранилища, тогда ответ будет либо "он никогда не станет видимым", либо "ваш определение неверно ".

Однако с практической точки зрения можно думать о нагрузках, получающих свое значение из какого-то конкретного магазина в системе. Таким образом, мы можем говорить о глобальной видимости магазинов (и, возможно, о частичном или общем заказе в этих магазинах), а затем обсудить, какие грузы могут получать свою стоимость от каких магазинов. Таким образом, ряд значений, полученных различными нагрузками, помещает их в тип глобального времени (хотя, возможно, только частично упорядоченный, если магазины упорядочены только частично).

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

В случае, когда связанный магазин в конечном итоге становится видимым глобально, у вас появляется интересный эффект типа перемещения во времени: нагрузка на локальный ЦП потенциально видит хранилище намного раньше, чем другие процессоры, и, в частности, возможно, он видит его не в порядке по отношению к другим магазинам в системе. Этот эффект является одной из причин, почему с системами с пересылкой хранилища обычно связано переупорядочение - например, в модели с сильной памятью x86 допустимые переупорядочения - это те, которые вызваны буферизацией хранилища и пересылкой хранилища.

Загрузка отправляется из RS и проходит через AGU к записи буфера загрузки, которая была выделена для соответствующей записи ROB на этапе распределения, окрашенной в самый последний SBID на тот момент. Я думаю, что после того, как адрес действителен, в записи устанавливается действительный бит, означающий, что они готовы к отправке (и очищается, когда данные в конечном итоге записываются обратно в ROB). Существует также спекулятивный предсказатель устранения неоднозначности в памяти, который может быть задействован в установке действительного бита, чтобы указать, что он, по прогнозам, не будет иметь псевдонима с какими-либо хранилищами между SBID, которым он окрашен, и хранилищем указателя хвоста в SAB/SBD. Если предполагается псевдоним,или фактически является псевдонимом (он ищет адрес в буфере хранилища и использует битовую маску в SAB, чтобы определить, может ли запись удовлетворить его (битовая маска указывает уровень привилегий супервизора / не супервизора байтов), и использует подразумеваемый размер из кода операции, чтобы получить диапазон адресов, которые сохраняются для операции сохранения. Если это может быть выполнено, он читает из записи SDB), он выполняет спекулятивную пересылку от магазина к загрузке с использованием данных в SDB и завершает в LB, но не уходит из LB. Переадресация хранилища для загрузки гарантирует, что чтение никогда не может быть переупорядочено со старыми записями в одно и то же место, потому что чтение всегда будет использовать хранилище для перенаправления загрузки. Я думаю, что все адреса магазинов до SBID LFENCE необходимо вычислить, прежде чем делать прогноз.и использует подразумеваемый размер из кода операции для получения диапазона адресов, по которым сохраняется операция сохранения. Если это может быть выполнено, он читает из записи SDB), он выполняет спекулятивную пересылку от магазина к загрузке с использованием данных в SDB и завершается в LB, но не удаляется из LB. Переадресация хранилища для загрузки гарантирует, что чтение никогда не может быть переупорядочено со старыми записями в одно и то же место, потому что чтение всегда будет использовать хранилище для перенаправления загрузки. Я думаю, что все адреса магазинов перед SBID LFENCE должны быть рассчитаны, прежде чем делать прогноз.и использует подразумеваемый размер из кода операции для получения диапазона адресов, по которым сохраняется операция сохранения. Если это может быть выполнено, он считывает из записи SDB), он выполняет спекулятивную пересылку от хранилища к загрузке, используя данные в SDB и завершается в LB, но не удаляется из LB. Переадресация хранилища для загрузки гарантирует, что чтение никогда не может быть переупорядочено со старыми записями в одно и то же место, потому что чтение всегда будет использовать хранилище для перенаправления загрузки. Я думаю, что все адреса магазинов до SBID LFENCE необходимо вычислить, прежде чем делать прогноз.но не уходит из LB. Переадресация хранилища для загрузки гарантирует, что чтение никогда не может быть переупорядочено со старыми записями в одно и то же место, потому что чтение всегда будет использовать хранилище для перенаправления загрузки. Я думаю, что все адреса магазинов перед SBID LFENCE должны быть рассчитаны, прежде чем делать прогноз.но не уходит из LB. Переадресация хранилища для загрузки гарантирует, что чтение никогда не может быть переупорядочено со старыми записями в одно и то же место, потому что чтение всегда будет использовать хранилище для перенаправления загрузки. Я думаю, что все адреса магазинов до SBID LFENCE необходимо вычислить, прежде чем делать прогноз.

Если это не предсказано для псевдонима, загрузка отправляется (и загрузки всегда отправляются в строгом порядке по отношению к другой загрузке, если только загрузка не имеет вневременного попадания или относится к памяти USWC (хотя, в отличие от хранилищ, это не так) не знаю, является ли это USWC на ​​данном этапе.) Загрузка идет в кеш dTLB/l1d параллельно.

В любое время, когда адреса хранения завершаются в SAB с любым SBID, меньшим или равным цветному SBID, это может аннулировать сделанный прогноз устранения неоднозначности в памяти, и конвейер очищается, потому что конвейер теперь либо использует устаревшие данные, сохраненные до хранилище, с которым он должен был выполнять переадресацию из хранилища в загрузку, или он использует ложные данные пересылки из хранилища в загрузку из хранилища, от которого он фактически не зависел.

Когда данные загружаются в назначенный физический регистр назначения, данные становятся действительными в ROB. Когда данные в ROB действительны и указатель вывода из эксплуатации указывает на запись, нагрузка больше не является спекулятивной и становится старшей (бит e устанавливается в LB логикой вывода из эксплуатации). Затем загрузка может быть удалена из LB (удалена из), если установлен бит, который указывает, что для всех хранилищ между указателем хвоста SAB и цветным SBID были вычислены адреса. Если это не старшая инструкция загрузки, в этом случае она может выполняться теперь, когда она является старшей и вышла из ROB.

LFENCEотправляется в буфер загрузки и выполняется (отправляется в кэш L1d) только тогда, когда все предыдущие мопы удалились из ROB и когда все предыдущие инструкции загрузки ушли из ROB+LB (согласно свойствам сериализации потока инструкций заявлено чтобы иметь, он, вероятно, удаляется в цикле самостоятельно, а не с 1 или 2 другими инструкциями перед ним в ROB в том же цикле). Инструкции загрузки удаляются, когда ROB сообщает им, что они могут быть отключены (больше не спекулятивно), и полученные данные действительны, и загрузка больше не зависит от памяти. LFENCE отправляет, когда он находится в хвосте буфера загрузки, и ROB (он не может отключиться, пока все буферы чтения не станут глобально видимыми.Я думаю, это означает, что он гарантирует, что любые старшие инструкции загрузки (инструкции, которые выполняются после выхода из ROB и когда они помечаются как старшие), такие как PREFETCHвыделили буферы чтения. При регулярных загрузках выделяются буферы чтения и считываются их данные, и они становятся действительными в буфере загрузки, прежде чем их можно будет удалить. Глобально видимый в этом случае означает, что все предыдущие загрузки либо выделили буферы чтения, либо попали в кеш L1d. Я не уверен (конечно, инструкции, которые удалились из MOB, уже есть, но старшие инструкции загрузки могли не выделить чтение буферы) (это в отличие от определения глобально видимого для магазинов, где он становится видимым только после завершения запроса RFO, и он устанавливает глобально видимый бит в LFB, указывающий, что у него есть разрешение на запись строки, предполагая, что это всегда будет записано, прежде чем ответить на snoop, где он теряет разрешение на линию). Я'Я не уверен, может ли он потребовать дальнейшего подтверждения из кеша L2/LLC или ему действительно нужно прочитать данные в буфере заполнения, но я не понимаю, почему. Когда LFENCE отправляет отправку, кэш L1d обрабатывает его как nop, и он завершается, удаляется в ROB, становится старшим, т.е. удаляется из LB, и uops до его отправки в буфер загрузки, которым было запрещено отправляться в кеш L1d, теперь разрешено быть отправленным.

Я думаю, что старшая загрузка - это загрузка, которая больше не является спекулятивной и ожидает, пока данные будут возвращены и станут действительными, или она уже действительна, поэтому немедленно удаляется, тогда как старшая инструкция загрузки - это инструкция, которая отправляется после того, как она была удалена из ROB.

Другие вопросы по тегам